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.
Files changed (73) hide show
  1. ngio/__init__.py +7 -2
  2. ngio/common/__init__.py +5 -52
  3. ngio/common/_dimensions.py +270 -55
  4. ngio/common/_masking_roi.py +38 -10
  5. ngio/common/_pyramid.py +51 -30
  6. ngio/common/_roi.py +269 -82
  7. ngio/common/_synt_images_utils.py +101 -0
  8. ngio/common/_zoom.py +49 -19
  9. ngio/experimental/__init__.py +5 -0
  10. ngio/experimental/iterators/__init__.py +15 -0
  11. ngio/experimental/iterators/_abstract_iterator.py +390 -0
  12. ngio/experimental/iterators/_feature.py +189 -0
  13. ngio/experimental/iterators/_image_processing.py +130 -0
  14. ngio/experimental/iterators/_mappers.py +48 -0
  15. ngio/experimental/iterators/_rois_utils.py +127 -0
  16. ngio/experimental/iterators/_segmentation.py +235 -0
  17. ngio/hcs/_plate.py +41 -36
  18. ngio/images/__init__.py +22 -1
  19. ngio/images/_abstract_image.py +403 -176
  20. ngio/images/_create.py +31 -15
  21. ngio/images/_create_synt_container.py +138 -0
  22. ngio/images/_image.py +452 -63
  23. ngio/images/_label.py +56 -30
  24. ngio/images/_masked_image.py +387 -129
  25. ngio/images/_ome_zarr_container.py +237 -67
  26. ngio/{common → images}/_table_ops.py +41 -41
  27. ngio/io_pipes/__init__.py +75 -0
  28. ngio/io_pipes/_io_pipes.py +361 -0
  29. ngio/io_pipes/_io_pipes_masked.py +488 -0
  30. ngio/io_pipes/_io_pipes_roi.py +152 -0
  31. ngio/io_pipes/_io_pipes_types.py +56 -0
  32. ngio/io_pipes/_match_shape.py +376 -0
  33. ngio/io_pipes/_ops_axes.py +344 -0
  34. ngio/io_pipes/_ops_slices.py +446 -0
  35. ngio/io_pipes/_ops_slices_utils.py +196 -0
  36. ngio/io_pipes/_ops_transforms.py +104 -0
  37. ngio/io_pipes/_zoom_transform.py +175 -0
  38. ngio/ome_zarr_meta/__init__.py +4 -2
  39. ngio/ome_zarr_meta/ngio_specs/__init__.py +4 -10
  40. ngio/ome_zarr_meta/ngio_specs/_axes.py +186 -175
  41. ngio/ome_zarr_meta/ngio_specs/_channels.py +55 -18
  42. ngio/ome_zarr_meta/ngio_specs/_dataset.py +48 -122
  43. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +3 -3
  44. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +38 -87
  45. ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +17 -1
  46. ngio/ome_zarr_meta/v04/_v04_spec_utils.py +34 -31
  47. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/mask.png +0 -0
  48. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
  49. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg +0 -0
  50. ngio/resources/__init__.py +55 -0
  51. ngio/resources/resource_model.py +36 -0
  52. ngio/tables/backends/_abstract_backend.py +5 -6
  53. ngio/tables/backends/_anndata.py +1 -1
  54. ngio/tables/backends/_anndata_utils.py +3 -3
  55. ngio/tables/backends/_non_zarr_backends.py +1 -1
  56. ngio/tables/backends/_table_backends.py +0 -1
  57. ngio/tables/backends/_utils.py +3 -3
  58. ngio/tables/v1/_roi_table.py +165 -70
  59. ngio/transforms/__init__.py +5 -0
  60. ngio/transforms/_zoom.py +19 -0
  61. ngio/utils/__init__.py +2 -3
  62. ngio/utils/_datasets.py +5 -0
  63. ngio/utils/_logger.py +19 -0
  64. ngio/utils/_zarr_utils.py +6 -6
  65. {ngio-0.3.5.dist-info → ngio-0.4.0.dist-info}/METADATA +16 -14
  66. ngio-0.4.0.dist-info/RECORD +85 -0
  67. ngio/common/_array_pipe.py +0 -288
  68. ngio/common/_axes_transforms.py +0 -64
  69. ngio/common/_common_types.py +0 -5
  70. ngio/common/_slicer.py +0 -96
  71. ngio-0.3.5.dist-info/RECORD +0 -61
  72. {ngio-0.3.5.dist-info → ngio-0.4.0.dist-info}/WHEEL +0 -0
  73. {ngio-0.3.5.dist-info → ngio-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -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 Collection, Iterable
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
- get_pipe,
15
- roi_to_slice_kwargs,
16
- set_pipe,
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
- AxesMapper,
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._dataset.path)
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.meta_handler.meta.space_unit
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.meta_handler.meta.time_unit
172
+ return self.axes_handler.time_unit
147
173
 
148
- @property
149
- def pixel_size(self) -> PixelSize:
150
- """Return the pixel size of the image."""
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
- @property
154
- def dataset(self) -> Dataset:
155
- """Return the dataset of the image."""
156
- return self._dataset
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
- @property
159
- def path(self) -> str:
160
- """Return the path of the image."""
161
- return self._dataset.path
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
- def has_axis(self, axis: str) -> bool:
164
- """Return True if the image has the given axis."""
165
- self.axes_mapper.get_index("x")
166
- return self.dimensions.has_axis(axis)
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 get_array(
203
+ def _get_roi_as_numpy(
169
204
  self,
170
- axes_order: Collection[str] | None = None,
171
- mode: Literal["numpy", "dask", "delayed"] = "numpy",
172
- **slice_kwargs: slice | int | Iterable[int],
173
- ) -> ArrayLike:
174
- """Get a slice of the image.
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
- mode: The mode to return the array.
179
- **slice_kwargs: The slices to get the array.
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
- return get_pipe(
185
- array=self.zarr_array,
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
- mode=mode,
189
- **slice_kwargs,
226
+ transforms=transforms,
227
+ slicing_dict=slicing_kwargs,
190
228
  )
229
+ return numpy_roi_getter()
191
230
 
192
- def get_roi(
231
+ def _get_as_dask(
193
232
  self,
194
- roi: Roi,
195
- axes_order: Collection[str] | None = None,
196
- mode: Literal["numpy", "dask", "delayed"] = "numpy",
197
- **slice_kwargs: slice | int | Iterable[int],
198
- ) -> ArrayLike:
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
- return get_roi_pipe(
211
- image=self, roi=roi, axes_order=axes_order, mode=mode, **slice_kwargs
212
- )
213
-
214
- def set_array(
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: ArrayLike,
217
- axes_order: Collection[str] | None = None,
218
- **slice_kwargs: slice | int | Iterable[int],
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
- **slice_kwargs: The slices to set the patch.
356
+ transforms: The transforms to apply to the patch.
357
+ **slicing_kwargs: The slices to set the patch.
226
358
 
227
359
  """
228
- set_pipe(
229
- array=self.zarr_array,
230
- patch=patch,
231
- dimensions=self.dimensions,
232
- axes_order=axes_order,
233
- **slice_kwargs,
234
- )
235
-
236
- def set_roi(
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: ArrayLike,
240
- axes_order: Collection[str] | None = None,
241
- **slice_kwargs: slice | int | Iterable[int],
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
- **slice_kwargs: The slices to set the patch.
400
+ transforms: The transforms to apply to the patch.
401
+ **slicing_kwargs: The slices to set the patch.
250
402
 
251
403
  """
252
- return set_roi_pipe(
253
- image=self, roi=roi, patch=patch, axes_order=axes_order, **slice_kwargs
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 build_image_roi_table(self, name: str = "image") -> RoiTable:
257
- """Build the ROI table for an image."""
258
- return build_image_roi_table(image=self, name=name)
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: Literal[0, 1, 2] = 1,
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)])