Skip to content

Commit

Permalink
feat: np based layer
Browse files Browse the repository at this point in the history
  • Loading branch information
supersergiy committed May 18, 2024
1 parent 00f373b commit 26746b0
Show file tree
Hide file tree
Showing 34 changed files with 236 additions and 199 deletions.
4 changes: 2 additions & 2 deletions docs/source/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Layers for CloudVolume IO:
... )
>>> data = cvl[Vec3D(64, 64, 40), 7500:7564, 2250:2314, 2000:2001]
>>> data.shape # channel, x, y, z
torch.Size([1, 64, 64, 1])
(1, 64, 64, 1)


>>> from zetta_utils.layer.volumetric.cloudvol import build_cv_layer
Expand All @@ -114,7 +114,7 @@ Layers for CloudVolume IO:
... )
>>> data = cvl[120000:121024, 36000:37024, 2000:2001] # (4, 4, 40) indexing
>>> data.shape # channel, x, y, z
torch.Size([1, 64, 64, 1])
(1, 64, 64, 1)

Layer sets for grouping layers together:

Expand Down
10 changes: 5 additions & 5 deletions docs/source/subchunkable_apply_flow_quick_start_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ For this guide, we will use FAFB v15, which is a ``precomputed`` dataset, and us
... )
>>> data = cvl[Vec3D(64, 64, 40), 13000:13100, 4000:4100, 2000:2001]
>>> data.shape # channel, x, y, z
torch.Size([1, 100, 100, 1])
(1, 100, 100, 1)

.. collapse:: Vec3D

Expand Down Expand Up @@ -89,7 +89,7 @@ For instance, suppose that you were looking at the FAFB v15 in neuroglancer, and
... )
>>> data = cvl[211200:216000, 64800:69600, 2000:2002] # (4, 4, 40) indexing
>>> data.shape # channel, x, y, z at (192, 192, 80) resolution
torch.Size([1, 100, 100, 1])
(1, 100, 100, 1)

This feature can be used to:

Expand Down Expand Up @@ -171,7 +171,7 @@ Using ``VolumetricIndex``, the first example above becomes:
... )
>>> data = cvl[idx]
>>> data.shape # channel, x, y, z
torch.Size([1, 100, 100, 1])
(1, 100, 100, 1)

.. note::
Since ``VolumetricIndex`` already contains the resolution information, the ``index_resolution`` provided at the initialisation of ``VolumetricLayer`` is overridden when indexing into it using a ``VolumetricIndex``.
Expand Down Expand Up @@ -273,9 +273,9 @@ We initialise the ``VolumetricLayer`` with this ``DataProcessor``, and compare t
... resolution = Vec3D(64, 64, 40)
... )
>>> cvl_without_proc[idx].min()
tensor(-2.9492)
-2.9491823
>>> cvl_with_proc[idx].min()
tensor(0.)
0.0

This ``VolumetricLayer`` will now apply the ``__call__`` from the ``ThresholdProcessor`` before returning the output for each read.

Expand Down
42 changes: 10 additions & 32 deletions tests/unit/layer/volumetric/cloudvol/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import numpy as np
import pytest
import torch

from zetta_utils.geometry import BBox3D, IntVec3D, Vec3D
from zetta_utils.layer.volumetric import VolumetricIndex
Expand Down Expand Up @@ -40,14 +39,7 @@ def test_cv_backend_dtype(clear_caches_reset_mocks):
info_spec = PrecomputedInfoSpec(reference_path=LAYER_X0_PATH, data_type="uint8")
cvb = CVBackend(path=LAYER_X0_PATH, info_spec=info_spec, on_info_exists="overwrite")

assert cvb.dtype == torch.uint8


def test_cv_backend_dtype_exc(clear_caches_reset_mocks):
info_spec = PrecomputedInfoSpec(reference_path=LAYER_X0_PATH, data_type="nonsense")
cvb = CVBackend(path=LAYER_SCRATCH0_PATH, info_spec=info_spec, on_info_exists="overwrite")
with pytest.raises(ValueError):
cvb.dtype
assert cvb.dtype == np.dtype("uint8")


def test_cv_backend_info_expect_same_exc(clear_caches_reset_mocks, mocker):
Expand Down Expand Up @@ -101,8 +93,8 @@ def test_cv_backend_info_overwrite(clear_caches_reset_mocks, path, reference, mo


def test_cv_backend_read(clear_caches_reset_mocks, mocker):
data_read = torch.ones([3, 4, 5, 2])
expected = torch.ones([2, 3, 4, 5])
data_read = np.ones([3, 4, 5, 2])
expected = np.ones([2, 3, 4, 5])
cv_m = mocker.MagicMock()
cv_m.__getitem__ = mocker.MagicMock(return_value=data_read)
mocker.patch("cloudvolume.CloudVolume.__new__", return_value=cv_m)
Expand All @@ -125,8 +117,8 @@ def test_cv_backend_write(clear_caches_reset_mocks, mocker):
cv_m.__setitem__ = mocker.MagicMock()
mocker.patch("cloudvolume.CloudVolume.__new__", return_value=cv_m)
cvb = CVBackend(path=LAYER_SCRATCH0_PATH, info_spec=info_spec, on_info_exists="overwrite")
value = torch.ones([2, 3, 4, 5])
expected_written = torch.ones([3, 4, 5, 2]) # channel as ch 0
value = np.ones([2, 3, 4, 5])
expected_written = np.ones([3, 4, 5, 2]) # channel as ch 0

index = VolumetricIndex(
bbox=BBox3D.from_slices((slice(0, 1), slice(1, 2), slice(2, 3))),
Expand All @@ -148,7 +140,7 @@ def test_cv_backend_write_scalar(clear_caches_reset_mocks, mocker):
cv_m.__setitem__ = mocker.MagicMock()
mocker.patch("cloudvolume.CloudVolume.__new__", return_value=cv_m)
cvb = CVBackend(path=LAYER_SCRATCH0_PATH, info_spec=info_spec, on_info_exists="overwrite")
value = torch.tensor([1])
value = np.array([1])
expected_written = 1

index = VolumetricIndex(
Expand All @@ -163,7 +155,7 @@ def test_cv_backend_write_scalar(clear_caches_reset_mocks, mocker):

def test_cv_backend_read_uint63(clear_caches_reset_mocks, mocker):
data_read = np.array([[[[2 ** 63 - 1]]]], dtype=np.uint64)
expected = torch.tensor([[[[2 ** 63 - 1]]]], dtype=torch.int64)
expected = np.array([[[[2 ** 63 - 1]]]], dtype=np.int64)
cv_m = mocker.MagicMock()
cv_m.__getitem__ = mocker.MagicMock(return_value=data_read)
mocker.patch("cloudvolume.CloudVolume.__new__", return_value=cv_m)
Expand All @@ -177,20 +169,6 @@ def test_cv_backend_read_uint63(clear_caches_reset_mocks, mocker):
cv_m.__getitem__.assert_called_with(index.bbox.to_slices(index.resolution))


def test_cv_backend_read_uint64_exc(clear_caches_reset_mocks, mocker):
data_read = np.array([[[[2 ** 63 + 1]]]], dtype=np.uint64)
cv_m = mocker.MagicMock()
cv_m.__getitem__ = mocker.MagicMock(return_value=data_read)
mocker.patch("cloudvolume.CloudVolume.__new__", return_value=cv_m)
cvb = CVBackend(path=LAYER_UINT63_0_PATH)
index = VolumetricIndex(
bbox=BBox3D.from_slices((slice(0, 1), slice(0, 1), slice(0, 1))),
resolution=Vec3D(1, 1, 1),
)
with pytest.raises(ValueError):
cvb.read(index)


def test_cv_backend_write_scalar_uint63(clear_caches_reset_mocks, mocker):
info_spec = PrecomputedInfoSpec(
reference_path=LAYER_UINT63_0_PATH,
Expand All @@ -200,7 +178,7 @@ def test_cv_backend_write_scalar_uint63(clear_caches_reset_mocks, mocker):
cv_m.dtype = "uint64"
mocker.patch("cloudvolume.CloudVolume.__new__", return_value=cv_m)
cvb = CVBackend(path=LAYER_SCRATCH0_PATH, info_spec=info_spec, on_info_exists="overwrite")
value = torch.tensor([2 ** 63 - 1], dtype=torch.int64)
value = np.array([2 ** 63 - 1], dtype=np.int64)
expected_written = np.uint64(2 ** 63 - 1)

index = VolumetricIndex(
Expand All @@ -222,7 +200,7 @@ def test_cv_backend_write_scalar_uint63_exc(clear_caches_reset_mocks, mocker):
cv_m.dtype = "uint64"
mocker.patch("cloudvolume.CloudVolume.__new__", return_value=cv_m)
cvb = CVBackend(path=LAYER_SCRATCH0_PATH, info_spec=info_spec, on_info_exists="overwrite")
value = torch.tensor([-1], dtype=torch.int64)
value = np.array([-1], dtype=np.int64)

index = VolumetricIndex(
bbox=BBox3D.from_slices((slice(0, 1), slice(0, 1), slice(0, 1))),
Expand All @@ -236,7 +214,7 @@ def test_cv_backend_write_scalar_uint63_exc(clear_caches_reset_mocks, mocker):
"data_in,expected_exc",
[
# Too many dims
[torch.ones((1, 2, 3, 4, 5, 6)), ValueError],
[np.ones((1, 2, 3, 4, 5, 6)), ValueError],
],
)
def test_cv_backend_write_exc(clear_caches_reset_mocks, data_in, expected_exc, mocker):
Expand Down
17 changes: 8 additions & 9 deletions tests/unit/layer/volumetric/tensorstore/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import numpy as np
import pytest
import tensorstore
import torch

from zetta_utils.geometry import BBox3D, IntVec3D, Vec3D
from zetta_utils.layer.volumetric import (
Expand Down Expand Up @@ -48,7 +47,7 @@ def test_ts_backend_dtype(clear_caches_reset_mocks):
info_spec = PrecomputedInfoSpec(reference_path=LAYER_X0_PATH, data_type="uint8")
tsb = TSBackend(path=LAYER_X0_PATH, info_spec=info_spec, on_info_exists="overwrite")

assert tsb.dtype == torch.uint8
assert tsb.dtype == np.dtype("uint8")


def test_ts_backend_dtype_exc(clear_caches_reset_mocks):
Expand Down Expand Up @@ -148,8 +147,8 @@ def test_ts_backend_read_partial(clear_caches_reset_mocks, mocker):
resolution=Vec3D(1, 1, 1),
)
result = tsb.read(index)
assert result[:, 0:1, :, :] == torch.zeros((1, 1, 1, 1), dtype=torch.uint8)
assert result[:, 1:2, :, :] == torch.ones((1, 1, 1, 1), dtype=torch.uint8)
assert result[:, 0:1, :, :] == np.zeros((1, 1, 1, 1), dtype=np.uint8)
assert result[:, 1:2, :, :] == np.ones((1, 1, 1, 1), dtype=np.uint8)


def test_ts_backend_write_idx(clear_caches_reset_mocks, mocker):
Expand All @@ -160,7 +159,7 @@ def test_ts_backend_write_idx(clear_caches_reset_mocks, mocker):
default_voxel_offset=IntVec3D(1, 2, 3),
)
tsb = TSBackend(path=LAYER_SCRATCH0_PATH, info_spec=info_spec, on_info_exists="overwrite")
value = torch.ones([1, 3, 5, 7], dtype=torch.uint8)
value = np.ones([1, 3, 5, 7], dtype=np.uint8)

index = VolumetricIndex(
bbox=BBox3D.from_slices((slice(1, 4), slice(2, 7), slice(3, 10))),
Expand All @@ -185,7 +184,7 @@ def test_ts_backend_write_idx_partial(clear_caches_reset_mocks, mocker):
on_info_exists="overwrite",
enforce_chunk_aligned_writes=False,
)
value = torch.ones([1, 3, 4, 5], dtype=torch.uint8)
value = np.ones([1, 3, 4, 5], dtype=np.uint8)

index = VolumetricIndex(
bbox=BBox3D.from_slices((slice(-2, 1), slice(-3, 1), slice(-4, 1))),
Expand All @@ -205,7 +204,7 @@ def test_ts_backend_write_scalar_idx(clear_caches_reset_mocks, mocker):
default_voxel_offset=IntVec3D(1, 2, 3),
)
tsb = TSBackend(path=LAYER_SCRATCH0_PATH, info_spec=info_spec, on_info_exists="overwrite")
value = torch.tensor([1], dtype=torch.uint8)
value = np.array([1], dtype=np.uint8)

index = VolumetricIndex(
bbox=BBox3D.from_slices((slice(1, 4), slice(2, 7), slice(3, 10))),
Expand All @@ -221,7 +220,7 @@ def test_ts_backend_write_scalar_idx(clear_caches_reset_mocks, mocker):
"data_in,expected_exc",
[
# Too many dims
[torch.ones((1, 2, 3, 4, 5, 6)), ValueError],
[np.ones((1, 2, 3, 4, 5, 6)), ValueError],
],
)
def test_ts_backend_write_exc_dims(data_in, expected_exc, clear_caches_reset_mocks, mocker):
Expand All @@ -243,7 +242,7 @@ def test_ts_backend_write_exc_dims(data_in, expected_exc, clear_caches_reset_moc
"data_in,expected_exc",
[
# idx not chunk aligned
[torch.ones((3, 3, 3, 3), dtype=torch.uint8), ValueError],
[np.ones((3, 3, 3, 3), dtype=np.uint8), ValueError],
],
)
def test_ts_backend_write_exc_chunks(data_in, expected_exc, clear_caches_reset_mocks, mocker):
Expand Down
32 changes: 17 additions & 15 deletions tests/unit/layer/volumetric/test_layer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# pylint: disable=missing-docstring,redefined-outer-name,unused-argument,pointless-statement,line-too-long,protected-access,unsubscriptable-object
from __future__ import annotations

import numpy as np
import pytest
import torch

from zetta_utils.geometry import BBox3D, Vec3D
from zetta_utils.layer.volumetric import (
Expand All @@ -11,10 +11,12 @@
build_volumetric_layer,
)

from ...helpers import assert_array_equal


def test_data_resolution_read_interp(mocker):
backend = mocker.MagicMock()
backend.read = mocker.MagicMock(return_value=torch.ones((2, 2, 2, 2)) * 2)
backend.read = mocker.MagicMock(return_value=np.ones((2, 2, 2, 2)) * 2)

layer = build_volumetric_layer(
backend,
Expand All @@ -32,9 +34,9 @@ def test_data_resolution_read_interp(mocker):
bbox=BBox3D.from_slices((slice(0, 3), slice(0, 3), slice(0, 3))),
)
)
assert torch.equal(
assert_array_equal(
read_data,
torch.ones((2, 1, 1, 1)),
np.ones((2, 1, 1, 1)),
)


Expand All @@ -60,11 +62,11 @@ def test_data_resolution_write_interp(mocker):
bbox=BBox3D.from_slices((slice(0, 4), slice(0, 4), slice(0, 4))),
)

layer[0:4, 0:4, 0:4] = torch.ones((2, 1, 1, 1))
layer[0:4, 0:4, 0:4] = np.ones((2, 1, 1, 1))
assert backend.write.call_args.kwargs["idx"] == idx
assert torch.equal(
assert_array_equal(
backend.write.call_args.kwargs["data"],
torch.ones((2, 2, 2, 2)) * 2,
np.ones((2, 2, 2, 2)) * 2,
)


Expand All @@ -79,9 +81,9 @@ def test_write_scalar(mocker):
)

layer[0:1, 0:1, 0:1] = 1.0
assert torch.equal(
assert_array_equal(
backend.write.call_args.kwargs["data"],
torch.Tensor([1]),
np.array([1]),
)


Expand All @@ -97,16 +99,16 @@ def test_write_scalar_with_processor(mocker):
)

layer[0:1, 0:1, 0:1] = 1.0
assert torch.equal(
assert_array_equal(
backend.write.call_args.kwargs["data"],
torch.Tensor([2]),
np.array([2]),
)


def test_read_write_with_idx_processor(mocker):
backend = mocker.MagicMock()
backend.write = mocker.MagicMock()
backend.read = mocker.MagicMock(return_value=torch.ones((2, 1, 1, 1)))
backend.read = mocker.MagicMock(return_value=np.ones((2, 1, 1, 1)))

layer = build_volumetric_layer(
backend,
Expand All @@ -122,14 +124,14 @@ def test_read_write_with_idx_processor(mocker):
bbox=BBox3D.from_slices((slice(2, 4), slice(4, 6), slice(8, 10))),
)
layer[0:1, 0:1, 0:1] = 1.0
assert torch.equal(
assert_array_equal(
backend.write.call_args.kwargs["data"],
torch.Tensor([2]),
np.array([2]),
)
assert backend.write.call_args.kwargs["idx"] == expected_idx

data_read = layer[0:1, 0:1, 0:1]
assert torch.equal(data_read, torch.zeros((2, 1, 1, 1)))
assert_array_equal(data_read, np.zeros((2, 1, 1, 1)))
assert backend.write.call_args.kwargs["idx"] == expected_idx


Expand Down
8 changes: 4 additions & 4 deletions tests/unit/layer/volumetric/test_layer_set.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import torch
import numpy as np

from zetta_utils.geometry import BBox3D, Vec3D
from zetta_utils.layer.volumetric import VolumetricIndex, build_volumetric_layer_set


def test_read(mocker):
layer_a = mocker.MagicMock()
layer_a.read_with_procs = mocker.MagicMock(return_value=torch.tensor([1]))
layer_a.read_with_procs = mocker.MagicMock(return_value=np.array([1]))
layer_b = mocker.MagicMock()
layer_b.read_with_procs = mocker.MagicMock(return_value=torch.tensor([2]))
layer_b.read_with_procs = mocker.MagicMock(return_value=np.array([2]))
layer_set = build_volumetric_layer_set(layers={"a": layer_a, "b": layer_b})
idx = VolumetricIndex(bbox=BBox3D(bounds=((0, 1), (0, 1), (0, 1))), resolution=Vec3D(1, 1, 1))
result = layer_set[idx]
assert result == {"a": torch.Tensor([1]), "b": torch.Tensor([2])}
assert result == {"a": np.array([1]), "b": np.array([2])}
layer_a.read_with_procs.called_with(idx=idx)
layer_b.read_with_procs.called_with(idx=idx)

Expand Down
Loading

0 comments on commit 26746b0

Please sign in to comment.