ngio 0.3.5__py3-none-any.whl → 0.4.0__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 +7 -2
- ngio/common/__init__.py +5 -52
- ngio/common/_dimensions.py +270 -55
- ngio/common/_masking_roi.py +38 -10
- ngio/common/_pyramid.py +51 -30
- ngio/common/_roi.py +269 -82
- ngio/common/_synt_images_utils.py +101 -0
- ngio/common/_zoom.py +49 -19
- ngio/experimental/__init__.py +5 -0
- ngio/experimental/iterators/__init__.py +15 -0
- ngio/experimental/iterators/_abstract_iterator.py +390 -0
- ngio/experimental/iterators/_feature.py +189 -0
- ngio/experimental/iterators/_image_processing.py +130 -0
- ngio/experimental/iterators/_mappers.py +48 -0
- ngio/experimental/iterators/_rois_utils.py +127 -0
- ngio/experimental/iterators/_segmentation.py +235 -0
- ngio/hcs/_plate.py +41 -36
- ngio/images/__init__.py +22 -1
- ngio/images/_abstract_image.py +403 -176
- ngio/images/_create.py +31 -15
- ngio/images/_create_synt_container.py +138 -0
- ngio/images/_image.py +452 -63
- ngio/images/_label.py +56 -30
- ngio/images/_masked_image.py +387 -129
- ngio/images/_ome_zarr_container.py +237 -67
- ngio/{common → images}/_table_ops.py +41 -41
- ngio/io_pipes/__init__.py +75 -0
- ngio/io_pipes/_io_pipes.py +361 -0
- ngio/io_pipes/_io_pipes_masked.py +488 -0
- ngio/io_pipes/_io_pipes_roi.py +152 -0
- ngio/io_pipes/_io_pipes_types.py +56 -0
- ngio/io_pipes/_match_shape.py +376 -0
- ngio/io_pipes/_ops_axes.py +344 -0
- ngio/io_pipes/_ops_slices.py +446 -0
- ngio/io_pipes/_ops_slices_utils.py +196 -0
- ngio/io_pipes/_ops_transforms.py +104 -0
- ngio/io_pipes/_zoom_transform.py +175 -0
- ngio/ome_zarr_meta/__init__.py +4 -2
- ngio/ome_zarr_meta/ngio_specs/__init__.py +4 -10
- ngio/ome_zarr_meta/ngio_specs/_axes.py +186 -175
- ngio/ome_zarr_meta/ngio_specs/_channels.py +55 -18
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +48 -122
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +3 -3
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +38 -87
- ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +17 -1
- ngio/ome_zarr_meta/v04/_v04_spec_utils.py +34 -31
- 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 +55 -0
- ngio/resources/resource_model.py +36 -0
- ngio/tables/backends/_abstract_backend.py +5 -6
- ngio/tables/backends/_anndata.py +1 -1
- 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 +165 -70
- ngio/transforms/__init__.py +5 -0
- ngio/transforms/_zoom.py +19 -0
- ngio/utils/__init__.py +2 -3
- ngio/utils/_datasets.py +5 -0
- ngio/utils/_logger.py +19 -0
- ngio/utils/_zarr_utils.py +6 -6
- {ngio-0.3.5.dist-info → ngio-0.4.0.dist-info}/METADATA +16 -14
- ngio-0.4.0.dist-info/RECORD +85 -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.5.dist-info/RECORD +0 -61
- {ngio-0.3.5.dist-info → ngio-0.4.0.dist-info}/WHEEL +0 -0
- {ngio-0.3.5.dist-info → ngio-0.4.0.dist-info}/licenses/LICENSE +0 -0
ngio/images/_abstract_image.py
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
"""Generic class to handle Image-like data in a OME-NGFF file."""
|
|
2
2
|
|
|
3
|
-
from collections.abc import
|
|
3
|
+
from collections.abc import Sequence
|
|
4
4
|
from typing import Generic, Literal, TypeVar
|
|
5
5
|
|
|
6
|
+
import dask.array as da
|
|
7
|
+
import numpy as np
|
|
6
8
|
import zarr
|
|
7
9
|
|
|
8
10
|
from ngio.common import (
|
|
9
|
-
ArrayLike,
|
|
10
11
|
Dimensions,
|
|
12
|
+
InterpolationOrder,
|
|
11
13
|
Roi,
|
|
12
14
|
RoiPixels,
|
|
13
15
|
consolidate_pyramid,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
)
|
|
17
|
+
from ngio.io_pipes import (
|
|
18
|
+
DaskGetter,
|
|
19
|
+
DaskRoiGetter,
|
|
20
|
+
DaskRoiSetter,
|
|
21
|
+
DaskSetter,
|
|
22
|
+
NumpyGetter,
|
|
23
|
+
NumpyRoiGetter,
|
|
24
|
+
NumpyRoiSetter,
|
|
25
|
+
NumpySetter,
|
|
26
|
+
SlicingInputType,
|
|
27
|
+
TransformProtocol,
|
|
17
28
|
)
|
|
18
29
|
from ngio.ome_zarr_meta import (
|
|
19
|
-
|
|
30
|
+
AxesHandler,
|
|
20
31
|
Dataset,
|
|
21
32
|
ImageMetaHandler,
|
|
22
33
|
LabelMetaHandler,
|
|
@@ -52,29 +63,54 @@ class AbstractImage(Generic[_image_handler]):
|
|
|
52
63
|
self._group_handler = group_handler
|
|
53
64
|
self._meta_handler = meta_handler
|
|
54
65
|
|
|
55
|
-
self._dataset = self._meta_handler.meta.get_dataset(path=path)
|
|
56
|
-
self._pixel_size = self._dataset.pixel_size
|
|
57
|
-
|
|
58
66
|
try:
|
|
59
|
-
self._zarr_array = self._group_handler.get_array(self.
|
|
67
|
+
self._zarr_array = self._group_handler.get_array(self._path)
|
|
60
68
|
except NgioFileExistsError as e:
|
|
61
69
|
raise NgioFileExistsError(f"Could not find the dataset at {path}.") from e
|
|
62
70
|
|
|
63
|
-
self._dimensions = Dimensions(
|
|
64
|
-
shape=self._zarr_array.shape, axes_mapper=self._dataset.axes_mapper
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
self._axes_mapper = self._dataset.axes_mapper
|
|
68
|
-
|
|
69
71
|
def __repr__(self) -> str:
|
|
70
72
|
"""Return a string representation of the image."""
|
|
71
73
|
return f"Image(path={self.path}, {self.dimensions})"
|
|
72
74
|
|
|
75
|
+
@property
|
|
76
|
+
def path(self) -> str:
|
|
77
|
+
"""Return the path of the image."""
|
|
78
|
+
return self._path
|
|
79
|
+
|
|
73
80
|
@property
|
|
74
81
|
def meta_handler(self) -> _image_handler:
|
|
75
82
|
"""Return the metadata."""
|
|
76
83
|
return self._meta_handler
|
|
77
84
|
|
|
85
|
+
@property
|
|
86
|
+
def dataset(self) -> Dataset:
|
|
87
|
+
"""Return the dataset of the image."""
|
|
88
|
+
return self.meta_handler.meta.get_dataset(path=self.path)
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def dimensions(self) -> Dimensions:
|
|
92
|
+
"""Return the dimensions of the image."""
|
|
93
|
+
return Dimensions(
|
|
94
|
+
shape=self.zarr_array.shape,
|
|
95
|
+
chunks=self.zarr_array.chunks,
|
|
96
|
+
dataset=self.dataset,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def pixel_size(self) -> PixelSize:
|
|
101
|
+
"""Return the pixel size of the image."""
|
|
102
|
+
return self.dataset.pixel_size
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def axes_handler(self) -> AxesHandler:
|
|
106
|
+
"""Return the axes handler of the image."""
|
|
107
|
+
return self.dataset.axes_handler
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def axes(self) -> tuple[str, ...]:
|
|
111
|
+
"""Return the axes of the image."""
|
|
112
|
+
return self.dimensions.axes
|
|
113
|
+
|
|
78
114
|
@property
|
|
79
115
|
def zarr_array(self) -> zarr.Array:
|
|
80
116
|
"""Return the Zarr array."""
|
|
@@ -95,16 +131,6 @@ class AbstractImage(Generic[_image_handler]):
|
|
|
95
131
|
"""Return the chunks of the image."""
|
|
96
132
|
return self.zarr_array.chunks
|
|
97
133
|
|
|
98
|
-
@property
|
|
99
|
-
def dimensions(self) -> Dimensions:
|
|
100
|
-
"""Return the dimensions of the image."""
|
|
101
|
-
return self._dimensions
|
|
102
|
-
|
|
103
|
-
@property
|
|
104
|
-
def axes_mapper(self) -> AxesMapper:
|
|
105
|
-
"""Return the axes mapper of the image."""
|
|
106
|
-
return self._axes_mapper
|
|
107
|
-
|
|
108
134
|
@property
|
|
109
135
|
def is_3d(self) -> bool:
|
|
110
136
|
"""Return True if the image is 3D."""
|
|
@@ -138,107 +164,232 @@ class AbstractImage(Generic[_image_handler]):
|
|
|
138
164
|
@property
|
|
139
165
|
def space_unit(self) -> str | None:
|
|
140
166
|
"""Return the space unit of the image."""
|
|
141
|
-
return self.
|
|
167
|
+
return self.axes_handler.space_unit
|
|
142
168
|
|
|
143
169
|
@property
|
|
144
170
|
def time_unit(self) -> str | None:
|
|
145
171
|
"""Return the time unit of the image."""
|
|
146
|
-
return self.
|
|
172
|
+
return self.axes_handler.time_unit
|
|
147
173
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return self._pixel_size
|
|
174
|
+
def has_axis(self, axis: str) -> bool:
|
|
175
|
+
"""Return True if the image has the given axis."""
|
|
176
|
+
return self.axes_handler.has_axis(axis)
|
|
152
177
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
178
|
+
def _get_as_numpy(
|
|
179
|
+
self,
|
|
180
|
+
axes_order: Sequence[str] | None = None,
|
|
181
|
+
transforms: Sequence[TransformProtocol] | None = None,
|
|
182
|
+
**slicing_kwargs: SlicingInputType,
|
|
183
|
+
) -> np.ndarray:
|
|
184
|
+
"""Get the image as a numpy array.
|
|
157
185
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
186
|
+
Args:
|
|
187
|
+
axes_order: The order of the axes to return the array.
|
|
188
|
+
transforms: The transforms to apply to the array.
|
|
189
|
+
**slicing_kwargs: The slices to get the array.
|
|
162
190
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
191
|
+
Returns:
|
|
192
|
+
The array of the region of interest.
|
|
193
|
+
"""
|
|
194
|
+
numpy_getter = NumpyGetter(
|
|
195
|
+
zarr_array=self.zarr_array,
|
|
196
|
+
dimensions=self.dimensions,
|
|
197
|
+
axes_order=axes_order,
|
|
198
|
+
transforms=transforms,
|
|
199
|
+
slicing_dict=slicing_kwargs,
|
|
200
|
+
)
|
|
201
|
+
return numpy_getter()
|
|
167
202
|
|
|
168
|
-
def
|
|
203
|
+
def _get_roi_as_numpy(
|
|
169
204
|
self,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
205
|
+
roi: Roi | RoiPixels,
|
|
206
|
+
axes_order: Sequence[str] | None = None,
|
|
207
|
+
transforms: Sequence[TransformProtocol] | None = None,
|
|
208
|
+
**slicing_kwargs: SlicingInputType,
|
|
209
|
+
) -> np.ndarray:
|
|
210
|
+
"""Get the image as a numpy array for a region of interest.
|
|
175
211
|
|
|
176
212
|
Args:
|
|
213
|
+
roi: The region of interest to get the array.
|
|
177
214
|
axes_order: The order of the axes to return the array.
|
|
178
|
-
|
|
179
|
-
**
|
|
215
|
+
transforms: The transforms to apply to the array.
|
|
216
|
+
**slicing_kwargs: The slices to get the array.
|
|
180
217
|
|
|
181
218
|
Returns:
|
|
182
219
|
The array of the region of interest.
|
|
183
220
|
"""
|
|
184
|
-
|
|
185
|
-
|
|
221
|
+
numpy_roi_getter = NumpyRoiGetter(
|
|
222
|
+
zarr_array=self.zarr_array,
|
|
186
223
|
dimensions=self.dimensions,
|
|
224
|
+
roi=roi,
|
|
187
225
|
axes_order=axes_order,
|
|
188
|
-
|
|
189
|
-
|
|
226
|
+
transforms=transforms,
|
|
227
|
+
slicing_dict=slicing_kwargs,
|
|
190
228
|
)
|
|
229
|
+
return numpy_roi_getter()
|
|
191
230
|
|
|
192
|
-
def
|
|
231
|
+
def _get_as_dask(
|
|
193
232
|
self,
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
233
|
+
axes_order: Sequence[str] | None = None,
|
|
234
|
+
transforms: Sequence[TransformProtocol] | None = None,
|
|
235
|
+
**slicing_kwargs: SlicingInputType,
|
|
236
|
+
) -> da.Array:
|
|
237
|
+
"""Get the image as a dask array.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
axes_order: The order of the axes to return the array.
|
|
241
|
+
transforms: The transforms to apply to the array.
|
|
242
|
+
**slicing_kwargs: The slices to get the array.
|
|
243
|
+
"""
|
|
244
|
+
dask_getter = DaskGetter(
|
|
245
|
+
zarr_array=self.zarr_array,
|
|
246
|
+
dimensions=self.dimensions,
|
|
247
|
+
axes_order=axes_order,
|
|
248
|
+
transforms=transforms,
|
|
249
|
+
slicing_dict=slicing_kwargs,
|
|
250
|
+
)
|
|
251
|
+
return dask_getter()
|
|
252
|
+
|
|
253
|
+
def _get_roi_as_dask(
|
|
254
|
+
self,
|
|
255
|
+
roi: Roi | RoiPixels,
|
|
256
|
+
axes_order: Sequence[str] | None = None,
|
|
257
|
+
transforms: Sequence[TransformProtocol] | None = None,
|
|
258
|
+
**slicing_kwargs: SlicingInputType,
|
|
259
|
+
) -> da.Array:
|
|
260
|
+
"""Get the image as a dask array for a region of interest.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
roi: The region of interest to get the array.
|
|
264
|
+
axes_order: The order of the axes to return the array.
|
|
265
|
+
transforms: The transforms to apply to the array.
|
|
266
|
+
**slicing_kwargs: The slices to get the array.
|
|
267
|
+
"""
|
|
268
|
+
roi_dask_getter = DaskRoiGetter(
|
|
269
|
+
zarr_array=self.zarr_array,
|
|
270
|
+
dimensions=self.dimensions,
|
|
271
|
+
roi=roi,
|
|
272
|
+
axes_order=axes_order,
|
|
273
|
+
transforms=transforms,
|
|
274
|
+
slicing_dict=slicing_kwargs,
|
|
275
|
+
)
|
|
276
|
+
return roi_dask_getter()
|
|
277
|
+
|
|
278
|
+
def _get_array(
|
|
279
|
+
self,
|
|
280
|
+
axes_order: Sequence[str] | None = None,
|
|
281
|
+
transforms: Sequence[TransformProtocol] | None = None,
|
|
282
|
+
mode: Literal["numpy", "dask"] = "numpy",
|
|
283
|
+
**slicing_kwargs: SlicingInputType,
|
|
284
|
+
) -> np.ndarray | da.Array:
|
|
285
|
+
"""Get a slice of the image.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
axes_order: The order of the axes to return the array.
|
|
289
|
+
transforms: The transforms to apply to the array.
|
|
290
|
+
mode: The object type to return.
|
|
291
|
+
Can be "dask", "numpy".
|
|
292
|
+
**slicing_kwargs: The slices to get the array.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
The array of the region of interest.
|
|
296
|
+
"""
|
|
297
|
+
if mode == "numpy":
|
|
298
|
+
return self._get_as_numpy(
|
|
299
|
+
axes_order=axes_order, transforms=transforms, **slicing_kwargs
|
|
300
|
+
)
|
|
301
|
+
elif mode == "dask":
|
|
302
|
+
return self._get_as_dask(
|
|
303
|
+
axes_order=axes_order, transforms=transforms, **slicing_kwargs
|
|
304
|
+
)
|
|
305
|
+
else:
|
|
306
|
+
raise ValueError(
|
|
307
|
+
f"Unsupported mode: {mode}. Supported modes are: numpy, dask."
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def _get_roi(
|
|
311
|
+
self,
|
|
312
|
+
roi: Roi | RoiPixels,
|
|
313
|
+
axes_order: Sequence[str] | None = None,
|
|
314
|
+
transforms: Sequence[TransformProtocol] | None = None,
|
|
315
|
+
mode: Literal["numpy", "dask"] = "numpy",
|
|
316
|
+
**slice_kwargs: SlicingInputType,
|
|
317
|
+
) -> np.ndarray | da.Array:
|
|
199
318
|
"""Get a slice of the image.
|
|
200
319
|
|
|
201
320
|
Args:
|
|
202
321
|
roi: The region of interest to get the array.
|
|
203
322
|
axes_order: The order of the axes to return the array.
|
|
323
|
+
transforms: The transforms to apply to the array.
|
|
204
324
|
mode: The mode to return the array.
|
|
325
|
+
Can be "dask", "numpy".
|
|
205
326
|
**slice_kwargs: The slices to get the array.
|
|
206
327
|
|
|
207
328
|
Returns:
|
|
208
329
|
The array of the region of interest.
|
|
209
330
|
"""
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
331
|
+
if mode == "numpy":
|
|
332
|
+
return self._get_roi_as_numpy(
|
|
333
|
+
roi=roi, axes_order=axes_order, transforms=transforms, **slice_kwargs
|
|
334
|
+
)
|
|
335
|
+
elif mode == "dask":
|
|
336
|
+
return self._get_roi_as_dask(
|
|
337
|
+
roi=roi, axes_order=axes_order, transforms=transforms, **slice_kwargs
|
|
338
|
+
)
|
|
339
|
+
else:
|
|
340
|
+
raise ValueError(
|
|
341
|
+
f"Unsupported mode: {mode}. Supported modes are: numpy, dask."
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
def _set_array(
|
|
215
345
|
self,
|
|
216
|
-
patch:
|
|
217
|
-
axes_order:
|
|
218
|
-
|
|
346
|
+
patch: np.ndarray | da.Array,
|
|
347
|
+
axes_order: Sequence[str] | None = None,
|
|
348
|
+
transforms: Sequence[TransformProtocol] | None = None,
|
|
349
|
+
**slicing_kwargs: SlicingInputType,
|
|
219
350
|
) -> None:
|
|
220
351
|
"""Set a slice of the image.
|
|
221
352
|
|
|
222
353
|
Args:
|
|
223
354
|
patch: The patch to set.
|
|
224
355
|
axes_order: The order of the axes to set the patch.
|
|
225
|
-
|
|
356
|
+
transforms: The transforms to apply to the patch.
|
|
357
|
+
**slicing_kwargs: The slices to set the patch.
|
|
226
358
|
|
|
227
359
|
"""
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
360
|
+
if isinstance(patch, np.ndarray):
|
|
361
|
+
numpy_setter = NumpySetter(
|
|
362
|
+
zarr_array=self.zarr_array,
|
|
363
|
+
dimensions=self.dimensions,
|
|
364
|
+
axes_order=axes_order,
|
|
365
|
+
transforms=transforms,
|
|
366
|
+
slicing_dict=slicing_kwargs,
|
|
367
|
+
)
|
|
368
|
+
numpy_setter(patch)
|
|
369
|
+
|
|
370
|
+
elif isinstance(patch, da.Array):
|
|
371
|
+
dask_setter = DaskSetter(
|
|
372
|
+
zarr_array=self.zarr_array,
|
|
373
|
+
dimensions=self.dimensions,
|
|
374
|
+
axes_order=axes_order,
|
|
375
|
+
transforms=transforms,
|
|
376
|
+
slicing_dict=slicing_kwargs,
|
|
377
|
+
)
|
|
378
|
+
dask_setter(patch)
|
|
379
|
+
else:
|
|
380
|
+
raise TypeError(
|
|
381
|
+
f"Unsupported patch type: {type(patch)}. "
|
|
382
|
+
"Supported types are: "
|
|
383
|
+
"numpy.ndarray, dask.array.Array."
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
def _set_roi(
|
|
237
387
|
self,
|
|
238
|
-
roi: Roi,
|
|
239
|
-
patch:
|
|
240
|
-
axes_order:
|
|
241
|
-
|
|
388
|
+
roi: Roi | RoiPixels,
|
|
389
|
+
patch: np.ndarray | da.Array,
|
|
390
|
+
axes_order: Sequence[str] | None = None,
|
|
391
|
+
transforms: Sequence[TransformProtocol] | None = None,
|
|
392
|
+
**slicing_kwargs: SlicingInputType,
|
|
242
393
|
) -> None:
|
|
243
394
|
"""Set a slice of the image.
|
|
244
395
|
|
|
@@ -246,21 +397,182 @@ class AbstractImage(Generic[_image_handler]):
|
|
|
246
397
|
roi: The region of interest to set the patch.
|
|
247
398
|
patch: The patch to set.
|
|
248
399
|
axes_order: The order of the axes to set the patch.
|
|
249
|
-
|
|
400
|
+
transforms: The transforms to apply to the patch.
|
|
401
|
+
**slicing_kwargs: The slices to set the patch.
|
|
250
402
|
|
|
251
403
|
"""
|
|
252
|
-
|
|
253
|
-
|
|
404
|
+
if isinstance(patch, np.ndarray):
|
|
405
|
+
roi_numpy_setter = NumpyRoiSetter(
|
|
406
|
+
zarr_array=self.zarr_array,
|
|
407
|
+
dimensions=self.dimensions,
|
|
408
|
+
roi=roi,
|
|
409
|
+
axes_order=axes_order,
|
|
410
|
+
transforms=transforms,
|
|
411
|
+
slicing_dict=slicing_kwargs,
|
|
412
|
+
)
|
|
413
|
+
roi_numpy_setter(patch)
|
|
414
|
+
|
|
415
|
+
elif isinstance(patch, da.Array):
|
|
416
|
+
roi_dask_setter = DaskRoiSetter(
|
|
417
|
+
zarr_array=self.zarr_array,
|
|
418
|
+
dimensions=self.dimensions,
|
|
419
|
+
roi=roi,
|
|
420
|
+
axes_order=axes_order,
|
|
421
|
+
transforms=transforms,
|
|
422
|
+
slicing_dict=slicing_kwargs,
|
|
423
|
+
)
|
|
424
|
+
roi_dask_setter(patch)
|
|
425
|
+
else:
|
|
426
|
+
raise TypeError(
|
|
427
|
+
f"Unsupported patch type: {type(patch)}. "
|
|
428
|
+
"Supported types are: "
|
|
429
|
+
"numpy.ndarray, dask.array.Array."
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
def _consolidate(
|
|
433
|
+
self,
|
|
434
|
+
order: InterpolationOrder = "linear",
|
|
435
|
+
mode: Literal["dask", "numpy", "coarsen"] = "dask",
|
|
436
|
+
) -> None:
|
|
437
|
+
"""Consolidate the image on disk.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
order: The order of the consolidation.
|
|
441
|
+
mode: The mode of the consolidation.
|
|
442
|
+
"""
|
|
443
|
+
consolidate_image(image=self, order=order, mode=mode)
|
|
444
|
+
|
|
445
|
+
def roi(self, name: str | None = "image") -> Roi:
|
|
446
|
+
"""Return the ROI covering the entire image."""
|
|
447
|
+
dim_x = self.dimensions.get("x")
|
|
448
|
+
dim_y = self.dimensions.get("y")
|
|
449
|
+
assert dim_x is not None and dim_y is not None
|
|
450
|
+
dim_z = self.dimensions.get("z")
|
|
451
|
+
z = None if dim_z is None else 0
|
|
452
|
+
dim_t = self.dimensions.get("t")
|
|
453
|
+
t = None if dim_t is None else 0
|
|
454
|
+
roi_px = RoiPixels(
|
|
455
|
+
name=name,
|
|
456
|
+
x=0,
|
|
457
|
+
y=0,
|
|
458
|
+
z=z,
|
|
459
|
+
t=t,
|
|
460
|
+
x_length=dim_x,
|
|
461
|
+
y_length=dim_y,
|
|
462
|
+
z_length=dim_z,
|
|
463
|
+
t_length=dim_t,
|
|
254
464
|
)
|
|
465
|
+
return roi_px.to_roi(pixel_size=self.pixel_size)
|
|
466
|
+
|
|
467
|
+
def build_image_roi_table(self, name: str | None = "image") -> RoiTable:
|
|
468
|
+
"""Build the ROI table containing the ROI covering the entire image."""
|
|
469
|
+
return RoiTable(rois=[self.roi(name=name)])
|
|
255
470
|
|
|
256
|
-
def
|
|
257
|
-
|
|
258
|
-
|
|
471
|
+
def require_dimensions_match(
|
|
472
|
+
self,
|
|
473
|
+
other: "AbstractImage",
|
|
474
|
+
allow_singleton: bool = False,
|
|
475
|
+
) -> None:
|
|
476
|
+
"""Assert that two images have matching spatial dimensions.
|
|
477
|
+
|
|
478
|
+
Args:
|
|
479
|
+
other: The other image to compare to.
|
|
480
|
+
allow_singleton: If True, allow singleton dimensions to be
|
|
481
|
+
compatible with non-singleton dimensions.
|
|
482
|
+
|
|
483
|
+
Raises:
|
|
484
|
+
NgioValueError: If the images do not have compatible dimensions.
|
|
485
|
+
"""
|
|
486
|
+
self.dimensions.require_dimensions_match(
|
|
487
|
+
other.dimensions, allow_singleton=allow_singleton
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
def check_if_dimensions_match(
|
|
491
|
+
self,
|
|
492
|
+
other: "AbstractImage",
|
|
493
|
+
allow_singleton: bool = False,
|
|
494
|
+
) -> bool:
|
|
495
|
+
"""Check if two images have matching spatial dimensions.
|
|
496
|
+
|
|
497
|
+
Args:
|
|
498
|
+
other: The other image to compare to.
|
|
499
|
+
allow_singleton: If True, allow singleton dimensions to be
|
|
500
|
+
compatible with non-singleton dimensions.
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
bool: True if the images have matching dimensions, False otherwise.
|
|
504
|
+
"""
|
|
505
|
+
return self.dimensions.check_if_dimensions_match(
|
|
506
|
+
other.dimensions, allow_singleton=allow_singleton
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
def require_axes_match(
|
|
510
|
+
self,
|
|
511
|
+
other: "AbstractImage",
|
|
512
|
+
) -> None:
|
|
513
|
+
"""Assert that two images have compatible axes.
|
|
514
|
+
|
|
515
|
+
Args:
|
|
516
|
+
other: The other image to compare to.
|
|
517
|
+
|
|
518
|
+
Raises:
|
|
519
|
+
NgioValueError: If the images do not have compatible axes.
|
|
520
|
+
"""
|
|
521
|
+
self.dimensions.require_axes_match(other.dimensions)
|
|
522
|
+
|
|
523
|
+
def check_if_axes_match(
|
|
524
|
+
self,
|
|
525
|
+
other: "AbstractImage",
|
|
526
|
+
) -> bool:
|
|
527
|
+
"""Check if two images have compatible axes.
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
other: The other image to compare to.
|
|
531
|
+
|
|
532
|
+
Returns:
|
|
533
|
+
bool: True if the images have compatible axes, False otherwise.
|
|
534
|
+
|
|
535
|
+
"""
|
|
536
|
+
return self.dimensions.check_if_axes_match(other.dimensions)
|
|
537
|
+
|
|
538
|
+
def require_rescalable(
|
|
539
|
+
self,
|
|
540
|
+
other: "AbstractImage",
|
|
541
|
+
) -> None:
|
|
542
|
+
"""Assert that two images can be rescaled to each other.
|
|
543
|
+
|
|
544
|
+
For this to be true, the images must have the same axes, and
|
|
545
|
+
the pixel sizes must be compatible (i.e. one can be scaled to the other).
|
|
546
|
+
|
|
547
|
+
Args:
|
|
548
|
+
other: The other image to compare to.
|
|
549
|
+
|
|
550
|
+
Raises:
|
|
551
|
+
NgioValueError: If the images cannot be scaled to each other.
|
|
552
|
+
"""
|
|
553
|
+
self.dimensions.require_rescalable(other.dimensions)
|
|
554
|
+
|
|
555
|
+
def check_if_rescalable(
|
|
556
|
+
self,
|
|
557
|
+
other: "AbstractImage",
|
|
558
|
+
) -> bool:
|
|
559
|
+
"""Check if two images can be rescaled to each other.
|
|
560
|
+
|
|
561
|
+
For this to be true, the images must have the same axes, and
|
|
562
|
+
the pixel sizes must be compatible (i.e. one can be scaled to the other).
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
other: The other image to compare to.
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
bool: True if the images can be rescaled to each other, False otherwise.
|
|
569
|
+
"""
|
|
570
|
+
return self.dimensions.check_if_rescalable(other.dimensions)
|
|
259
571
|
|
|
260
572
|
|
|
261
573
|
def consolidate_image(
|
|
262
574
|
image: AbstractImage,
|
|
263
|
-
order:
|
|
575
|
+
order: InterpolationOrder = "linear",
|
|
264
576
|
mode: Literal["dask", "numpy", "coarsen"] = "dask",
|
|
265
577
|
) -> None:
|
|
266
578
|
"""Consolidate the image on disk."""
|
|
@@ -273,88 +585,3 @@ def consolidate_image(
|
|
|
273
585
|
consolidate_pyramid(
|
|
274
586
|
source=image.zarr_array, targets=targets, order=order, mode=mode
|
|
275
587
|
)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
def get_roi_pipe(
|
|
279
|
-
image: AbstractImage,
|
|
280
|
-
roi: Roi,
|
|
281
|
-
axes_order: Collection[str] | None = None,
|
|
282
|
-
mode: Literal["numpy", "dask", "delayed"] = "numpy",
|
|
283
|
-
**slice_kwargs: slice | int | Iterable[int],
|
|
284
|
-
) -> ArrayLike:
|
|
285
|
-
"""Get a slice of the image.
|
|
286
|
-
|
|
287
|
-
Args:
|
|
288
|
-
image: The image to get the ROI.
|
|
289
|
-
roi: The region of interest to get the array.
|
|
290
|
-
axes_order: The order of the axes to return the array.
|
|
291
|
-
mode: The mode to return the array.
|
|
292
|
-
**slice_kwargs: The slices to get the array.
|
|
293
|
-
|
|
294
|
-
Returns:
|
|
295
|
-
The array of the region of interest.
|
|
296
|
-
"""
|
|
297
|
-
slice_kwargs = roi_to_slice_kwargs(
|
|
298
|
-
roi=roi,
|
|
299
|
-
pixel_size=image.pixel_size,
|
|
300
|
-
dimensions=image.dimensions,
|
|
301
|
-
**slice_kwargs,
|
|
302
|
-
)
|
|
303
|
-
return get_pipe(
|
|
304
|
-
array=image.zarr_array,
|
|
305
|
-
dimensions=image.dimensions,
|
|
306
|
-
axes_order=axes_order,
|
|
307
|
-
mode=mode,
|
|
308
|
-
**slice_kwargs,
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
def set_roi_pipe(
|
|
313
|
-
image: AbstractImage,
|
|
314
|
-
roi: Roi,
|
|
315
|
-
patch: ArrayLike,
|
|
316
|
-
axes_order: Collection[str] | None = None,
|
|
317
|
-
**slice_kwargs: slice | int | Iterable[int],
|
|
318
|
-
) -> None:
|
|
319
|
-
"""Set a slice of the image.
|
|
320
|
-
|
|
321
|
-
Args:
|
|
322
|
-
image: The image to set the ROI.
|
|
323
|
-
roi: The region of interest to set the patch.
|
|
324
|
-
patch: The patch to set.
|
|
325
|
-
axes_order: The order of the axes to set the patch.
|
|
326
|
-
**slice_kwargs: The slices to set the patch.
|
|
327
|
-
|
|
328
|
-
"""
|
|
329
|
-
slice_kwargs = roi_to_slice_kwargs(
|
|
330
|
-
roi=roi,
|
|
331
|
-
pixel_size=image.pixel_size,
|
|
332
|
-
dimensions=image.dimensions,
|
|
333
|
-
**slice_kwargs,
|
|
334
|
-
)
|
|
335
|
-
set_pipe(
|
|
336
|
-
array=image.zarr_array,
|
|
337
|
-
patch=patch,
|
|
338
|
-
dimensions=image.dimensions,
|
|
339
|
-
axes_order=axes_order,
|
|
340
|
-
**slice_kwargs,
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
def build_image_roi_table(image: AbstractImage, name: str = "image") -> RoiTable:
|
|
345
|
-
"""Build the ROI table for an image."""
|
|
346
|
-
dim_z, dim_y, dim_x = (
|
|
347
|
-
image.dimensions.get("z", strict=False),
|
|
348
|
-
image.dimensions.get("y"),
|
|
349
|
-
image.dimensions.get("x"),
|
|
350
|
-
)
|
|
351
|
-
image_roi = RoiPixels(
|
|
352
|
-
name=name,
|
|
353
|
-
x=0,
|
|
354
|
-
y=0,
|
|
355
|
-
z=0,
|
|
356
|
-
x_length=dim_x,
|
|
357
|
-
y_length=dim_y,
|
|
358
|
-
z_length=dim_z,
|
|
359
|
-
)
|
|
360
|
-
return RoiTable(rois=[image_roi.to_roi(pixel_size=image.pixel_size)])
|