ngio 0.3.4__py3-none-any.whl → 0.4.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ngio/__init__.py +6 -0
- ngio/common/__init__.py +50 -48
- ngio/common/_array_io_pipes.py +549 -0
- ngio/common/_array_io_utils.py +508 -0
- ngio/common/_dimensions.py +63 -27
- ngio/common/_masking_roi.py +38 -10
- ngio/common/_pyramid.py +9 -7
- ngio/common/_roi.py +571 -72
- ngio/common/_synt_images_utils.py +101 -0
- ngio/common/_zoom.py +17 -12
- ngio/common/transforms/__init__.py +5 -0
- ngio/common/transforms/_label.py +12 -0
- ngio/common/transforms/_zoom.py +109 -0
- ngio/experimental/__init__.py +5 -0
- ngio/experimental/iterators/__init__.py +17 -0
- ngio/experimental/iterators/_abstract_iterator.py +170 -0
- ngio/experimental/iterators/_feature.py +151 -0
- ngio/experimental/iterators/_image_processing.py +169 -0
- ngio/experimental/iterators/_rois_utils.py +127 -0
- ngio/experimental/iterators/_segmentation.py +278 -0
- ngio/hcs/_plate.py +41 -36
- ngio/images/__init__.py +22 -1
- ngio/images/_abstract_image.py +247 -117
- ngio/images/_create.py +15 -15
- ngio/images/_create_synt_container.py +128 -0
- ngio/images/_image.py +425 -62
- ngio/images/_label.py +33 -30
- ngio/images/_masked_image.py +396 -122
- ngio/images/_ome_zarr_container.py +203 -66
- ngio/{common → images}/_table_ops.py +41 -41
- ngio/ome_zarr_meta/ngio_specs/__init__.py +2 -8
- ngio/ome_zarr_meta/ngio_specs/_axes.py +151 -128
- ngio/ome_zarr_meta/ngio_specs/_channels.py +55 -18
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +7 -7
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +6 -15
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +11 -68
- ngio/ome_zarr_meta/v04/_v04_spec_utils.py +1 -1
- ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/mask.png +0 -0
- ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
- ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg +0 -0
- ngio/resources/__init__.py +54 -0
- ngio/resources/resource_model.py +35 -0
- ngio/tables/backends/_abstract_backend.py +5 -6
- ngio/tables/backends/_anndata.py +1 -2
- ngio/tables/backends/_anndata_utils.py +3 -3
- ngio/tables/backends/_non_zarr_backends.py +1 -1
- ngio/tables/backends/_table_backends.py +0 -1
- ngio/tables/backends/_utils.py +3 -3
- ngio/tables/v1/_roi_table.py +156 -69
- ngio/utils/__init__.py +2 -3
- ngio/utils/_logger.py +19 -0
- ngio/utils/_zarr_utils.py +1 -5
- {ngio-0.3.4.dist-info → ngio-0.4.0a1.dist-info}/METADATA +12 -10
- ngio-0.4.0a1.dist-info/RECORD +76 -0
- ngio/common/_array_pipe.py +0 -288
- ngio/common/_axes_transforms.py +0 -64
- ngio/common/_common_types.py +0 -5
- ngio/common/_slicer.py +0 -96
- ngio-0.3.4.dist-info/RECORD +0 -61
- {ngio-0.3.4.dist-info → ngio-0.4.0a1.dist-info}/WHEEL +0 -0
- {ngio-0.3.4.dist-info → ngio-0.4.0a1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from collections.abc import Callable, Generator, Sequence
|
|
2
|
+
|
|
3
|
+
import dask.array as da
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from ngio.common import (
|
|
7
|
+
Roi,
|
|
8
|
+
TransformProtocol,
|
|
9
|
+
build_roi_dask_getter,
|
|
10
|
+
build_roi_dask_setter,
|
|
11
|
+
build_roi_numpy_getter,
|
|
12
|
+
build_roi_numpy_setter,
|
|
13
|
+
)
|
|
14
|
+
from ngio.experimental.iterators._abstract_iterator import AbstractIteratorBuilder
|
|
15
|
+
from ngio.images import Image
|
|
16
|
+
from ngio.images._image import (
|
|
17
|
+
ChannelSlicingInputType,
|
|
18
|
+
add_channel_selection_to_slicing_dict,
|
|
19
|
+
)
|
|
20
|
+
from ngio.utils._errors import NgioValidationError
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ImageProcessingIterator(AbstractIteratorBuilder):
|
|
24
|
+
"""Base class for iterators over ROIs."""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
input_image: Image,
|
|
29
|
+
output_image: Image,
|
|
30
|
+
input_channel_selection: ChannelSlicingInputType = None,
|
|
31
|
+
output_channel_selection: ChannelSlicingInputType = None,
|
|
32
|
+
axes_order: Sequence[str] | None = None,
|
|
33
|
+
input_transforms: Sequence[TransformProtocol] | None = None,
|
|
34
|
+
output_transforms: Sequence[TransformProtocol] | None = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Initialize the iterator with a ROI table and input/output images.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
input_image (Image): The input image to be used as input for the
|
|
40
|
+
segmentation.
|
|
41
|
+
output_image (Image): The image where the ROIs will be written.
|
|
42
|
+
input_channel_selection (ChannelSlicingInputType): Optional
|
|
43
|
+
selection of channels to use for the input image.
|
|
44
|
+
output_channel_selection (ChannelSlicingInputType): Optional
|
|
45
|
+
selection of channels to use for the output image.
|
|
46
|
+
axes_order (Sequence[str] | None): Optional axes order for the
|
|
47
|
+
segmentation.
|
|
48
|
+
input_transforms (Sequence[TransformProtocol] | None): Optional
|
|
49
|
+
transforms to apply to the input image.
|
|
50
|
+
output_transforms (Sequence[TransformProtocol] | None): Optional
|
|
51
|
+
transforms to apply to the output label.
|
|
52
|
+
"""
|
|
53
|
+
self._input = input_image
|
|
54
|
+
self._output = output_image
|
|
55
|
+
self._ref_image = input_image
|
|
56
|
+
self._rois = input_image.build_image_roi_table().rois()
|
|
57
|
+
|
|
58
|
+
# Set iteration parameters
|
|
59
|
+
self._input_slicing_kwargs = add_channel_selection_to_slicing_dict(
|
|
60
|
+
image=self._input,
|
|
61
|
+
channel_selection=input_channel_selection,
|
|
62
|
+
slicing_dict={},
|
|
63
|
+
)
|
|
64
|
+
self._output_slicing_kwargs = add_channel_selection_to_slicing_dict(
|
|
65
|
+
image=self._output,
|
|
66
|
+
channel_selection=output_channel_selection,
|
|
67
|
+
slicing_dict={},
|
|
68
|
+
)
|
|
69
|
+
self._input_channel_selection = input_channel_selection
|
|
70
|
+
self._output_channel_selection = output_channel_selection
|
|
71
|
+
self._axes_order = axes_order
|
|
72
|
+
self._input_transforms = input_transforms
|
|
73
|
+
self._output_transforms = output_transforms
|
|
74
|
+
|
|
75
|
+
# Check compatibility between input and output images
|
|
76
|
+
if not self._input.dimensions.is_compatible_with(self._output.dimensions):
|
|
77
|
+
raise NgioValidationError(
|
|
78
|
+
"Input image and output label have incompatible dimensions. "
|
|
79
|
+
f"Input: {self._input.dimensions}, Output: {self._output.dimensions}."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def get_init_kwargs(self) -> dict:
|
|
83
|
+
"""Return the initialization arguments for the iterator."""
|
|
84
|
+
return {
|
|
85
|
+
"input_image": self._input,
|
|
86
|
+
"output_image": self._output,
|
|
87
|
+
"input_channel_selection": self._input_channel_selection,
|
|
88
|
+
"output_channel_selection": self._output_channel_selection,
|
|
89
|
+
"axes_order": self._axes_order,
|
|
90
|
+
"input_transforms": self._input_transforms,
|
|
91
|
+
"output_transforms": self._output_transforms,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
def build_numpy_getter(self, roi: Roi):
|
|
95
|
+
return build_roi_numpy_getter(
|
|
96
|
+
zarr_array=self._input.zarr_array,
|
|
97
|
+
dimensions=self._input.dimensions,
|
|
98
|
+
axes_order=self._axes_order,
|
|
99
|
+
transforms=self._input_transforms,
|
|
100
|
+
pixel_size=self._input.pixel_size,
|
|
101
|
+
roi=roi,
|
|
102
|
+
slicing_dict=self._input_slicing_kwargs,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def build_numpy_setter(self, roi: Roi):
|
|
106
|
+
return build_roi_numpy_setter(
|
|
107
|
+
zarr_array=self._output.zarr_array,
|
|
108
|
+
dimensions=self._output.dimensions,
|
|
109
|
+
axes_order=self._axes_order,
|
|
110
|
+
transforms=self._output_transforms,
|
|
111
|
+
pixel_size=self._output.pixel_size,
|
|
112
|
+
roi=roi,
|
|
113
|
+
slicing_dict=self._output_slicing_kwargs,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def build_dask_getter(self, roi: Roi):
|
|
117
|
+
return build_roi_dask_getter(
|
|
118
|
+
zarr_array=self._input.zarr_array,
|
|
119
|
+
dimensions=self._input.dimensions,
|
|
120
|
+
axes_order=self._axes_order,
|
|
121
|
+
transforms=self._input_transforms,
|
|
122
|
+
pixel_size=self._input.pixel_size,
|
|
123
|
+
roi=roi,
|
|
124
|
+
slicing_dict=self._input_slicing_kwargs,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def build_dask_setter(self, roi: Roi):
|
|
128
|
+
return build_roi_dask_setter(
|
|
129
|
+
zarr_array=self._output.zarr_array,
|
|
130
|
+
dimensions=self._output.dimensions,
|
|
131
|
+
axes_order=self._axes_order,
|
|
132
|
+
transforms=self._output_transforms,
|
|
133
|
+
pixel_size=self._output.pixel_size,
|
|
134
|
+
roi=roi,
|
|
135
|
+
slicing_dict=self._output_slicing_kwargs,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def post_consolidate(self):
|
|
139
|
+
self._output.consolidate()
|
|
140
|
+
|
|
141
|
+
def iter_as_numpy(
|
|
142
|
+
self,
|
|
143
|
+
) -> Generator[tuple[np.ndarray, Callable[[np.ndarray], None]]]:
|
|
144
|
+
"""Create an iterator over the pixels of the ROIs as Dask arrays.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Generator[tuple[da.Array, DaskWriter]]: An iterator the input
|
|
148
|
+
image as Dask arrays and a writer to write the output
|
|
149
|
+
to the label image.
|
|
150
|
+
"""
|
|
151
|
+
return super().iter_as_numpy()
|
|
152
|
+
|
|
153
|
+
def map_as_numpy(self, func: Callable[[np.ndarray], np.ndarray]) -> None:
|
|
154
|
+
"""Apply a transformation function to the ROI pixels."""
|
|
155
|
+
return super().map_as_numpy(func)
|
|
156
|
+
|
|
157
|
+
def iter_as_dask(self) -> Generator[tuple[da.Array, Callable[[da.Array], None]]]:
|
|
158
|
+
"""Create an iterator over the pixels of the ROIs as Dask arrays.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Generator[tuple[da.Array, DaskWriter]]: An iterator the input
|
|
162
|
+
image as Dask arrays and a writer to write the output
|
|
163
|
+
to the label image.
|
|
164
|
+
"""
|
|
165
|
+
return super().iter_as_dask()
|
|
166
|
+
|
|
167
|
+
def map_as_dask(self, func: Callable[[da.Array], da.Array]) -> None:
|
|
168
|
+
"""Apply a transformation function to the ROI pixels."""
|
|
169
|
+
return super().map_as_dask(func)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from ngio import Roi, RoiPixels
|
|
2
|
+
from ngio.images._abstract_image import AbstractImage
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def rois_product(rois_a: list[Roi], rois_b: list[Roi]) -> list[Roi]:
|
|
6
|
+
"""Compute the product of two sets of ROIs."""
|
|
7
|
+
rois_product = []
|
|
8
|
+
for roi_a in rois_a:
|
|
9
|
+
for roi_b in rois_b:
|
|
10
|
+
intersection = roi_a.intersection(roi_b)
|
|
11
|
+
if intersection:
|
|
12
|
+
rois_product.append(intersection)
|
|
13
|
+
return rois_product
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def grid(
|
|
17
|
+
rois: list[Roi],
|
|
18
|
+
ref_image: AbstractImage,
|
|
19
|
+
size_x: int | None = None,
|
|
20
|
+
size_y: int | None = None,
|
|
21
|
+
size_z: int | None = None,
|
|
22
|
+
size_t: int | None = None,
|
|
23
|
+
stride_x: int | None = None,
|
|
24
|
+
stride_y: int | None = None,
|
|
25
|
+
stride_z: int | None = None,
|
|
26
|
+
stride_t: int | None = None,
|
|
27
|
+
base_name: str = "",
|
|
28
|
+
) -> list[Roi]:
|
|
29
|
+
"""This method is a placeholder for creating a regular grid of ROIs."""
|
|
30
|
+
t_dim = ref_image.dimensions.get("t", default=1)
|
|
31
|
+
z_dim = ref_image.dimensions.get("z", default=1)
|
|
32
|
+
y_dim = ref_image.dimensions.get("y", default=1)
|
|
33
|
+
x_dim = ref_image.dimensions.get("x", default=1)
|
|
34
|
+
|
|
35
|
+
size_t = size_t if size_t is not None else t_dim
|
|
36
|
+
size_z = size_z if size_z is not None else z_dim
|
|
37
|
+
size_y = size_y if size_y is not None else y_dim
|
|
38
|
+
size_x = size_x if size_x is not None else x_dim
|
|
39
|
+
|
|
40
|
+
stride_t = stride_t if stride_t is not None else size_t
|
|
41
|
+
stride_z = stride_z if stride_z is not None else size_z
|
|
42
|
+
stride_y = stride_y if stride_y is not None else size_y
|
|
43
|
+
stride_x = stride_x if stride_x is not None else size_x
|
|
44
|
+
|
|
45
|
+
# Here we would create a grid of ROIs based on the specified parameters.
|
|
46
|
+
new_rois = []
|
|
47
|
+
for t in range(0, t_dim, stride_t):
|
|
48
|
+
for z in range(0, z_dim, stride_z):
|
|
49
|
+
for y in range(0, y_dim, stride_y):
|
|
50
|
+
for x in range(0, x_dim, stride_x):
|
|
51
|
+
roi = RoiPixels(
|
|
52
|
+
name=f"{base_name}({t}, {z}, {y}, {x})",
|
|
53
|
+
x=x,
|
|
54
|
+
y=y,
|
|
55
|
+
z=z,
|
|
56
|
+
t=t,
|
|
57
|
+
x_length=size_x,
|
|
58
|
+
y_length=size_y,
|
|
59
|
+
z_length=size_z,
|
|
60
|
+
t_length=size_t,
|
|
61
|
+
)
|
|
62
|
+
new_rois.append(roi.to_roi(pixel_size=ref_image.pixel_size))
|
|
63
|
+
|
|
64
|
+
return rois_product(rois, new_rois)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def by_yx(rois: list[Roi], ref_image: AbstractImage) -> list[Roi]:
|
|
68
|
+
"""Return a new iterator that iterates over ROIs by YX coordinates."""
|
|
69
|
+
return grid(
|
|
70
|
+
rois=rois,
|
|
71
|
+
ref_image=ref_image,
|
|
72
|
+
size_z=1,
|
|
73
|
+
stride_z=1,
|
|
74
|
+
size_t=1,
|
|
75
|
+
stride_t=1,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def by_zyx(rois: list[Roi], ref_image: AbstractImage, strict: bool = True) -> list[Roi]:
|
|
80
|
+
"""Return a new iterator that iterates over ROIs by ZYX coordinates."""
|
|
81
|
+
if strict and not ref_image.is_3d:
|
|
82
|
+
raise ValueError(
|
|
83
|
+
"Reference Input image must be 3D to iterate by ZXY coordinates. "
|
|
84
|
+
f"Current dimensions: {ref_image.dimensions}"
|
|
85
|
+
)
|
|
86
|
+
return grid(
|
|
87
|
+
rois=rois,
|
|
88
|
+
ref_image=ref_image,
|
|
89
|
+
size_t=1,
|
|
90
|
+
stride_t=1,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def by_chunks(
|
|
95
|
+
rois: list[Roi],
|
|
96
|
+
ref_image: AbstractImage,
|
|
97
|
+
overlap_xy: int = 0,
|
|
98
|
+
overlap_z: int = 0,
|
|
99
|
+
overlap_t: int = 0,
|
|
100
|
+
) -> list[Roi]:
|
|
101
|
+
"""This method is a placeholder for chunked processing."""
|
|
102
|
+
chunk_size = ref_image.chunks
|
|
103
|
+
t_axis = ref_image.axes_mapper.get_index("t")
|
|
104
|
+
z_axis = ref_image.axes_mapper.get_index("z")
|
|
105
|
+
y_axis = ref_image.axes_mapper.get_index("y")
|
|
106
|
+
x_axis = ref_image.axes_mapper.get_index("x")
|
|
107
|
+
|
|
108
|
+
size_x = chunk_size[x_axis] if x_axis is not None else None
|
|
109
|
+
size_y = chunk_size[y_axis] if y_axis is not None else None
|
|
110
|
+
size_z = chunk_size[z_axis] if z_axis is not None else None
|
|
111
|
+
size_t = chunk_size[t_axis] if t_axis is not None else None
|
|
112
|
+
stride_x = size_x - overlap_xy if size_x is not None else None
|
|
113
|
+
stride_y = size_y - overlap_xy if size_y is not None else None
|
|
114
|
+
stride_z = size_z - overlap_z if size_z is not None else None
|
|
115
|
+
stride_t = size_t - overlap_t if size_t is not None else None
|
|
116
|
+
return grid(
|
|
117
|
+
rois=rois,
|
|
118
|
+
ref_image=ref_image,
|
|
119
|
+
size_x=size_x,
|
|
120
|
+
size_y=size_y,
|
|
121
|
+
size_z=size_z,
|
|
122
|
+
size_t=size_t,
|
|
123
|
+
stride_x=stride_x,
|
|
124
|
+
stride_y=stride_y,
|
|
125
|
+
stride_z=stride_z,
|
|
126
|
+
stride_t=stride_t,
|
|
127
|
+
)
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
from collections.abc import Callable, Generator, Sequence
|
|
2
|
+
|
|
3
|
+
import dask.array as da
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from ngio.common import (
|
|
7
|
+
Roi,
|
|
8
|
+
TransformProtocol,
|
|
9
|
+
build_roi_dask_getter,
|
|
10
|
+
build_roi_dask_setter,
|
|
11
|
+
build_roi_masked_dask_getter,
|
|
12
|
+
build_roi_masked_dask_setter,
|
|
13
|
+
build_roi_masked_numpy_getter,
|
|
14
|
+
build_roi_masked_numpy_setter,
|
|
15
|
+
build_roi_numpy_getter,
|
|
16
|
+
build_roi_numpy_setter,
|
|
17
|
+
)
|
|
18
|
+
from ngio.experimental.iterators._abstract_iterator import AbstractIteratorBuilder
|
|
19
|
+
from ngio.images import Image, Label
|
|
20
|
+
from ngio.images._image import (
|
|
21
|
+
ChannelSlicingInputType,
|
|
22
|
+
add_channel_selection_to_slicing_dict,
|
|
23
|
+
)
|
|
24
|
+
from ngio.images._masked_image import MaskedImage
|
|
25
|
+
from ngio.utils._errors import NgioValidationError
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SegmentationIterator(AbstractIteratorBuilder):
|
|
29
|
+
"""Base class for iterators over ROIs."""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
input_image: Image,
|
|
34
|
+
output_label: Label,
|
|
35
|
+
channel_selection: ChannelSlicingInputType = None,
|
|
36
|
+
axes_order: Sequence[str] | None = None,
|
|
37
|
+
input_transforms: Sequence[TransformProtocol] | None = None,
|
|
38
|
+
output_transforms: Sequence[TransformProtocol] | None = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Initialize the iterator with a ROI table and input/output images.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
input_image (Image): The input image to be used as input for the
|
|
44
|
+
segmentation.
|
|
45
|
+
output_label (Label): The label image where the ROIs will be written.
|
|
46
|
+
channel_selection (ChannelSlicingInputType): Optional
|
|
47
|
+
selection of channels to use for the segmentation.
|
|
48
|
+
axes_order (Sequence[str] | None): Optional axes order for the
|
|
49
|
+
segmentation.
|
|
50
|
+
input_transforms (Sequence[TransformProtocol] | None): Optional
|
|
51
|
+
transforms to apply to the input image.
|
|
52
|
+
output_transforms (Sequence[TransformProtocol] | None): Optional
|
|
53
|
+
transforms to apply to the output label.
|
|
54
|
+
"""
|
|
55
|
+
self._input = input_image
|
|
56
|
+
self._output = output_label
|
|
57
|
+
self._ref_image = input_image
|
|
58
|
+
self._rois = input_image.build_image_roi_table().rois()
|
|
59
|
+
|
|
60
|
+
# Set iteration parameters
|
|
61
|
+
self._input_slicing_kwargs = add_channel_selection_to_slicing_dict(
|
|
62
|
+
image=self._input, channel_selection=channel_selection, slicing_dict={}
|
|
63
|
+
)
|
|
64
|
+
self._channel_selection = channel_selection
|
|
65
|
+
self._axes_order = axes_order
|
|
66
|
+
self._input_transforms = input_transforms
|
|
67
|
+
self._output_transforms = output_transforms
|
|
68
|
+
|
|
69
|
+
# Check compatibility between input and output images
|
|
70
|
+
if not self._input.dimensions.is_compatible_with(self._output.dimensions):
|
|
71
|
+
raise NgioValidationError(
|
|
72
|
+
"Input image and output label have incompatible dimensions. "
|
|
73
|
+
f"Input: {self._input.dimensions}, Output: {self._output.dimensions}."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def get_init_kwargs(self) -> dict:
|
|
77
|
+
"""Return the initialization arguments for the iterator."""
|
|
78
|
+
return {
|
|
79
|
+
"input_image": self._input,
|
|
80
|
+
"output_label": self._output,
|
|
81
|
+
"channel_selection": self._channel_selection,
|
|
82
|
+
"axes_order": self._axes_order,
|
|
83
|
+
"input_transforms": self._input_transforms,
|
|
84
|
+
"output_transforms": self._output_transforms,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
def build_numpy_getter(self, roi: Roi):
|
|
88
|
+
return build_roi_numpy_getter(
|
|
89
|
+
zarr_array=self._input.zarr_array,
|
|
90
|
+
dimensions=self._input.dimensions,
|
|
91
|
+
axes_order=self._axes_order,
|
|
92
|
+
transforms=self._input_transforms,
|
|
93
|
+
pixel_size=self._input.pixel_size,
|
|
94
|
+
roi=roi,
|
|
95
|
+
slicing_dict=self._input_slicing_kwargs,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def build_numpy_setter(self, roi: Roi):
|
|
99
|
+
return build_roi_numpy_setter(
|
|
100
|
+
zarr_array=self._output.zarr_array,
|
|
101
|
+
dimensions=self._output.dimensions,
|
|
102
|
+
axes_order=self._axes_order,
|
|
103
|
+
transforms=self._output_transforms,
|
|
104
|
+
pixel_size=self._output.pixel_size,
|
|
105
|
+
roi=roi,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def build_dask_getter(self, roi: Roi):
|
|
109
|
+
return build_roi_dask_getter(
|
|
110
|
+
zarr_array=self._input.zarr_array,
|
|
111
|
+
dimensions=self._input.dimensions,
|
|
112
|
+
axes_order=self._axes_order,
|
|
113
|
+
transforms=self._input_transforms,
|
|
114
|
+
pixel_size=self._input.pixel_size,
|
|
115
|
+
roi=roi,
|
|
116
|
+
slicing_dict=self._input_slicing_kwargs,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def build_dask_setter(self, roi: Roi):
|
|
120
|
+
return build_roi_dask_setter(
|
|
121
|
+
zarr_array=self._output.zarr_array,
|
|
122
|
+
dimensions=self._output.dimensions,
|
|
123
|
+
axes_order=self._axes_order,
|
|
124
|
+
transforms=self._output_transforms,
|
|
125
|
+
pixel_size=self._output.pixel_size,
|
|
126
|
+
roi=roi,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def post_consolidate(self):
|
|
130
|
+
self._output.consolidate()
|
|
131
|
+
|
|
132
|
+
def iter_as_numpy(
|
|
133
|
+
self,
|
|
134
|
+
) -> Generator[tuple[np.ndarray, Callable[[np.ndarray], None]]]:
|
|
135
|
+
"""Create an iterator over the pixels of the ROIs as Dask arrays.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Generator[tuple[da.Array, DaskWriter]]: An iterator the input
|
|
139
|
+
image as Dask arrays and a writer to write the output
|
|
140
|
+
to the label image.
|
|
141
|
+
"""
|
|
142
|
+
return super().iter_as_numpy()
|
|
143
|
+
|
|
144
|
+
def map_as_numpy(self, func: Callable[[np.ndarray], np.ndarray]) -> None:
|
|
145
|
+
"""Apply a transformation function to the ROI pixels."""
|
|
146
|
+
return super().map_as_numpy(func)
|
|
147
|
+
|
|
148
|
+
def iter_as_dask(self) -> Generator[tuple[da.Array, Callable[[da.Array], None]]]:
|
|
149
|
+
"""Create an iterator over the pixels of the ROIs as Dask arrays.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Generator[tuple[da.Array, DaskWriter]]: An iterator the input
|
|
153
|
+
image as Dask arrays and a writer to write the output
|
|
154
|
+
to the label image.
|
|
155
|
+
"""
|
|
156
|
+
return super().iter_as_dask()
|
|
157
|
+
|
|
158
|
+
def map_as_dask(self, func: Callable[[da.Array], da.Array]) -> None:
|
|
159
|
+
"""Apply a transformation function to the ROI pixels."""
|
|
160
|
+
return super().map_as_dask(func)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class MaskedSegmentationIterator(SegmentationIterator):
|
|
164
|
+
"""Base class for iterators over ROIs."""
|
|
165
|
+
|
|
166
|
+
def __init__(
|
|
167
|
+
self,
|
|
168
|
+
input_image: MaskedImage,
|
|
169
|
+
output_label: Label,
|
|
170
|
+
channel_selection: ChannelSlicingInputType = None,
|
|
171
|
+
axes_order: Sequence[str] | None = None,
|
|
172
|
+
input_transforms: Sequence[TransformProtocol] | None = None,
|
|
173
|
+
output_transforms: Sequence[TransformProtocol] | None = None,
|
|
174
|
+
) -> None:
|
|
175
|
+
"""Initialize the iterator with a ROI table and input/output images.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
input_image (MaskedImage): The input image to be used as input for the
|
|
179
|
+
segmentation.
|
|
180
|
+
output_label (Label): The label image where the ROIs will be written.
|
|
181
|
+
channel_selection (ChannelSlicingInputType): Optional
|
|
182
|
+
selection of channels to use for the segmentation.
|
|
183
|
+
axes_order (Sequence[str] | None): Optional axes order for the
|
|
184
|
+
segmentation.
|
|
185
|
+
input_transforms (Sequence[TransformProtocol] | None): Optional
|
|
186
|
+
transforms to apply to the input image.
|
|
187
|
+
output_transforms (Sequence[TransformProtocol] | None): Optional
|
|
188
|
+
transforms to apply to the output label.
|
|
189
|
+
"""
|
|
190
|
+
self._input = input_image
|
|
191
|
+
self._output = output_label
|
|
192
|
+
|
|
193
|
+
self._ref_image = input_image
|
|
194
|
+
self._set_rois(input_image._masking_roi_table.rois())
|
|
195
|
+
|
|
196
|
+
# Set iteration parameters
|
|
197
|
+
self._input_slicing_kwargs = add_channel_selection_to_slicing_dict(
|
|
198
|
+
image=self._input, channel_selection=channel_selection, slicing_dict={}
|
|
199
|
+
)
|
|
200
|
+
self._channel_selection = channel_selection
|
|
201
|
+
self._axes_order = axes_order
|
|
202
|
+
self._input_transforms = input_transforms
|
|
203
|
+
self._output_transforms = output_transforms
|
|
204
|
+
|
|
205
|
+
# Check compatibility between input and output images
|
|
206
|
+
if not self._input.dimensions.is_compatible_with(self._output.dimensions):
|
|
207
|
+
raise NgioValidationError(
|
|
208
|
+
"Input image and output label have incompatible dimensions. "
|
|
209
|
+
f"Input: {self._input.dimensions}, Output: {self._output.dimensions}."
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
def get_init_kwargs(self) -> dict:
|
|
213
|
+
"""Return the initialization arguments for the iterator."""
|
|
214
|
+
return {
|
|
215
|
+
"input_image": self._input,
|
|
216
|
+
"output_label": self._output,
|
|
217
|
+
"channel_selection": self._channel_selection,
|
|
218
|
+
"axes_order": self._axes_order,
|
|
219
|
+
"input_transforms": self._input_transforms,
|
|
220
|
+
"output_transforms": self._output_transforms,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
def build_numpy_getter(self, roi: Roi):
|
|
224
|
+
return build_roi_masked_numpy_getter(
|
|
225
|
+
roi=roi,
|
|
226
|
+
zarr_array=self._input.zarr_array,
|
|
227
|
+
dimensions=self._input.dimensions,
|
|
228
|
+
label_zarr_array=self._input._label.zarr_array,
|
|
229
|
+
label_dimensions=self._input._label.dimensions,
|
|
230
|
+
label_pixel_size=self._input._label.pixel_size,
|
|
231
|
+
axes_order=self._axes_order,
|
|
232
|
+
transforms=self._input_transforms,
|
|
233
|
+
pixel_size=self._input.pixel_size,
|
|
234
|
+
slicing_dict=self._input_slicing_kwargs,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
def build_numpy_setter(self, roi: Roi):
|
|
238
|
+
return build_roi_masked_numpy_setter(
|
|
239
|
+
roi=roi,
|
|
240
|
+
zarr_array=self._output.zarr_array,
|
|
241
|
+
dimensions=self._output.dimensions,
|
|
242
|
+
label_zarr_array=self._input._label.zarr_array,
|
|
243
|
+
label_dimensions=self._input._label.dimensions,
|
|
244
|
+
label_pixel_size=self._input._label.pixel_size,
|
|
245
|
+
axes_order=self._axes_order,
|
|
246
|
+
transforms=self._output_transforms,
|
|
247
|
+
pixel_size=self._output.pixel_size,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def build_dask_getter(self, roi: Roi):
|
|
251
|
+
return build_roi_masked_dask_getter(
|
|
252
|
+
roi=roi,
|
|
253
|
+
zarr_array=self._input.zarr_array,
|
|
254
|
+
dimensions=self._input.dimensions,
|
|
255
|
+
label_zarr_array=self._input._label.zarr_array,
|
|
256
|
+
label_dimensions=self._input._label.dimensions,
|
|
257
|
+
label_pixel_size=self._input._label.pixel_size,
|
|
258
|
+
axes_order=self._axes_order,
|
|
259
|
+
transforms=self._input_transforms,
|
|
260
|
+
pixel_size=self._input.pixel_size,
|
|
261
|
+
slicing_dict=self._input_slicing_kwargs,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
def build_dask_setter(self, roi: Roi):
|
|
265
|
+
return build_roi_masked_dask_setter(
|
|
266
|
+
roi=roi,
|
|
267
|
+
zarr_array=self._output.zarr_array,
|
|
268
|
+
dimensions=self._output.dimensions,
|
|
269
|
+
label_zarr_array=self._input._label.zarr_array,
|
|
270
|
+
label_dimensions=self._input._label.dimensions,
|
|
271
|
+
label_pixel_size=self._input._label.pixel_size,
|
|
272
|
+
axes_order=self._axes_order,
|
|
273
|
+
transforms=self._output_transforms,
|
|
274
|
+
pixel_size=self._output.pixel_size,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
def post_consolidate(self):
|
|
278
|
+
self._output.consolidate()
|