ngio 0.1.6__py3-none-any.whl → 0.2.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.
Files changed (84) hide show
  1. ngio/__init__.py +31 -5
  2. ngio/common/__init__.py +44 -0
  3. ngio/common/_array_pipe.py +160 -0
  4. ngio/common/_axes_transforms.py +63 -0
  5. ngio/common/_common_types.py +5 -0
  6. ngio/common/_dimensions.py +113 -0
  7. ngio/common/_pyramid.py +222 -0
  8. ngio/{core/roi.py → common/_roi.py} +22 -23
  9. ngio/common/_slicer.py +97 -0
  10. ngio/{pipes/_zoom_utils.py → common/_zoom.py} +2 -78
  11. ngio/hcs/__init__.py +60 -0
  12. ngio/images/__init__.py +23 -0
  13. ngio/images/abstract_image.py +240 -0
  14. ngio/images/create.py +251 -0
  15. ngio/images/image.py +383 -0
  16. ngio/images/label.py +96 -0
  17. ngio/images/omezarr_container.py +512 -0
  18. ngio/ome_zarr_meta/__init__.py +35 -0
  19. ngio/ome_zarr_meta/_generic_handlers.py +320 -0
  20. ngio/ome_zarr_meta/_meta_handlers.py +142 -0
  21. ngio/ome_zarr_meta/ngio_specs/__init__.py +63 -0
  22. ngio/ome_zarr_meta/ngio_specs/_axes.py +481 -0
  23. ngio/ome_zarr_meta/ngio_specs/_channels.py +378 -0
  24. ngio/ome_zarr_meta/ngio_specs/_dataset.py +134 -0
  25. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +5 -0
  26. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +434 -0
  27. ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +84 -0
  28. ngio/ome_zarr_meta/v04/__init__.py +11 -0
  29. ngio/ome_zarr_meta/v04/_meta_handlers.py +54 -0
  30. ngio/ome_zarr_meta/v04/_v04_spec_utils.py +412 -0
  31. ngio/tables/__init__.py +21 -5
  32. ngio/tables/_validators.py +192 -0
  33. ngio/tables/backends/__init__.py +8 -0
  34. ngio/tables/backends/_abstract_backend.py +71 -0
  35. ngio/tables/backends/_anndata_utils.py +194 -0
  36. ngio/tables/backends/_anndata_v1.py +75 -0
  37. ngio/tables/backends/_json_v1.py +56 -0
  38. ngio/tables/backends/_table_backends.py +102 -0
  39. ngio/tables/tables_container.py +300 -0
  40. ngio/tables/v1/__init__.py +6 -5
  41. ngio/tables/v1/_feature_table.py +161 -0
  42. ngio/tables/v1/_generic_table.py +99 -182
  43. ngio/tables/v1/_masking_roi_table.py +175 -0
  44. ngio/tables/v1/_roi_table.py +226 -0
  45. ngio/utils/__init__.py +23 -10
  46. ngio/utils/_datasets.py +51 -0
  47. ngio/utils/_errors.py +10 -4
  48. ngio/utils/_zarr_utils.py +378 -0
  49. {ngio-0.1.6.dist-info → ngio-0.2.0a1.dist-info}/METADATA +18 -39
  50. ngio-0.2.0a1.dist-info/RECORD +53 -0
  51. ngio/core/__init__.py +0 -7
  52. ngio/core/dimensions.py +0 -122
  53. ngio/core/image_handler.py +0 -228
  54. ngio/core/image_like_handler.py +0 -549
  55. ngio/core/label_handler.py +0 -410
  56. ngio/core/ngff_image.py +0 -387
  57. ngio/core/utils.py +0 -287
  58. ngio/io/__init__.py +0 -19
  59. ngio/io/_zarr.py +0 -88
  60. ngio/io/_zarr_array_utils.py +0 -0
  61. ngio/io/_zarr_group_utils.py +0 -60
  62. ngio/iterators/__init__.py +0 -1
  63. ngio/ngff_meta/__init__.py +0 -27
  64. ngio/ngff_meta/fractal_image_meta.py +0 -1267
  65. ngio/ngff_meta/meta_handler.py +0 -92
  66. ngio/ngff_meta/utils.py +0 -235
  67. ngio/ngff_meta/v04/__init__.py +0 -6
  68. ngio/ngff_meta/v04/specs.py +0 -158
  69. ngio/ngff_meta/v04/zarr_utils.py +0 -376
  70. ngio/pipes/__init__.py +0 -7
  71. ngio/pipes/_slicer_transforms.py +0 -176
  72. ngio/pipes/_transforms.py +0 -33
  73. ngio/pipes/data_pipe.py +0 -52
  74. ngio/tables/_ad_reader.py +0 -80
  75. ngio/tables/_utils.py +0 -301
  76. ngio/tables/tables_group.py +0 -252
  77. ngio/tables/v1/feature_tables.py +0 -182
  78. ngio/tables/v1/masking_roi_tables.py +0 -243
  79. ngio/tables/v1/roi_tables.py +0 -285
  80. ngio/utils/_common_types.py +0 -5
  81. ngio/utils/_pydantic_utils.py +0 -52
  82. ngio-0.1.6.dist-info/RECORD +0 -44
  83. {ngio-0.1.6.dist-info → ngio-0.2.0a1.dist-info}/WHEEL +0 -0
  84. {ngio-0.1.6.dist-info → ngio-0.2.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -4,19 +4,18 @@ These are the interfaces bwteen the ROI tables / masking ROI tables and
4
4
  the ImageLikeHandler.
5
5
  """
6
6
 
7
- from typing import Any
8
-
9
7
  import numpy as np
10
- from pydantic import BaseModel, Field
8
+ from pydantic import BaseModel, ConfigDict, Field
11
9
 
12
- from ngio.core.dimensions import Dimensions
13
- from ngio.ngff_meta.fractal_image_meta import PixelSize, SpaceUnits
10
+ from ngio.common._dimensions import Dimensions
11
+ from ngio.ome_zarr_meta.ngio_specs import PixelSize, SpaceUnits
14
12
 
15
13
 
16
14
  def _to_raster(value: float, pixel_size: float, max_shape: int) -> int:
17
15
  """Convert to raster coordinates."""
18
16
  round_value = int(np.round(value / pixel_size))
19
- return min(round_value, max_shape)
17
+ # Ensure the value is within the image shape boundaries
18
+ return max(0, min(round_value, max_shape))
20
19
 
21
20
 
22
21
  def _to_world(value: int, pixel_size: float) -> float:
@@ -27,6 +26,7 @@ def _to_world(value: int, pixel_size: float) -> float:
27
26
  class WorldCooROI(BaseModel):
28
27
  """Region of interest (ROI) metadata."""
29
28
 
29
+ name: str
30
30
  x_length: float
31
31
  y_length: float
32
32
  z_length: float = 1.0
@@ -34,7 +34,8 @@ class WorldCooROI(BaseModel):
34
34
  y: float = 0.0
35
35
  z: float = 0.0
36
36
  unit: SpaceUnits = Field(SpaceUnits.micrometer, repr=False)
37
- infos: dict[str, Any] = Field(default_factory=dict, repr=False)
37
+
38
+ model_config = ConfigDict(extra="allow")
38
39
 
39
40
  def to_raster_coo(
40
41
  self, pixel_size: PixelSize, dimensions: Dimensions
@@ -42,51 +43,49 @@ class WorldCooROI(BaseModel):
42
43
  """Convert to raster coordinates."""
43
44
  dim_x = dimensions.get("x")
44
45
  dim_y = dimensions.get("y")
45
- dim_z = dimensions.get("z", 1)
46
+ # Will default to 1 if z does not exist
47
+ dim_z = dimensions.get("z", strict=False)
46
48
 
47
49
  return RasterCooROI(
50
+ name=self.name,
48
51
  x=_to_raster(self.x, pixel_size.x, dim_x),
49
52
  y=_to_raster(self.y, pixel_size.y, dim_y),
50
53
  z=_to_raster(self.z, pixel_size.z, dim_z),
51
54
  x_length=_to_raster(self.x_length, pixel_size.x, dim_x),
52
55
  y_length=_to_raster(self.y_length, pixel_size.y, dim_y),
53
56
  z_length=_to_raster(self.z_length, pixel_size.z, dim_z),
54
- original_roi=self,
55
57
  )
56
58
 
57
59
 
58
60
  class RasterCooROI(BaseModel):
59
61
  """Region of interest (ROI) metadata."""
60
62
 
63
+ name: str
61
64
  x: int
62
65
  y: int
63
66
  z: int
64
67
  x_length: int
65
68
  y_length: int
66
69
  z_length: int
67
- original_roi: WorldCooROI = Field(..., repr=False)
70
+ model_config = ConfigDict(extra="allow")
68
71
 
69
72
  def to_world_coo_roi(self, pixel_size: PixelSize) -> WorldCooROI:
70
73
  """Convert to world coordinates."""
71
74
  return WorldCooROI(
75
+ name=self.name,
72
76
  x=_to_world(self.x, pixel_size.x),
73
77
  y=_to_world(self.y, pixel_size.y),
74
78
  z=_to_world(self.z, pixel_size.z),
75
79
  x_length=_to_world(self.x_length, pixel_size.x),
76
80
  y_length=_to_world(self.y_length, pixel_size.y),
77
81
  z_length=_to_world(self.z_length, pixel_size.z),
78
- unit=pixel_size.unit,
79
- infos=self.original_roi.infos,
82
+ unit=pixel_size.space_unit,
80
83
  )
81
84
 
82
- def x_slice(self) -> slice:
83
- """Return the slice for the x-axis."""
84
- return slice(self.x, self.x + self.x_length)
85
-
86
- def y_slice(self) -> slice:
87
- """Return the slice for the y-axis."""
88
- return slice(self.y, self.y + self.y_length)
89
-
90
- def z_slice(self) -> slice:
91
- """Return the slice for the z-axis."""
92
- return slice(self.z, self.z + self.z_length)
85
+ def to_slices(self) -> dict[str, slice]:
86
+ """Return the slices for the ROI."""
87
+ return {
88
+ "x": slice(self.x, self.x + self.x_length),
89
+ "y": slice(self.y, self.y + self.y_length),
90
+ "z": slice(self.z, self.z + self.z_length),
91
+ }
ngio/common/_slicer.py ADDED
@@ -0,0 +1,97 @@
1
+ # %%
2
+ from collections.abc import Iterable
3
+
4
+ import dask.array as da
5
+ import numpy as np
6
+ import zarr
7
+
8
+ from ngio.common._dimensions import Dimensions
9
+ from ngio.ome_zarr_meta.ngio_specs import AxesTransformation
10
+ from ngio.utils import NgioValueError
11
+
12
+
13
+ def _validate_int(value: int, shape: int) -> int:
14
+ if not isinstance(value, int):
15
+ raise NgioValueError(f"Invalid value {value} of type {type(value)}")
16
+ if value < 0 or value >= shape:
17
+ raise NgioValueError(
18
+ f"Invalid value {value}. Index out of bounds for axis of shape {shape}"
19
+ )
20
+ return value
21
+
22
+
23
+ def _validate_iter_of_ints(value: Iterable[int], shape: int) -> list[int]:
24
+ if not isinstance(value, list):
25
+ raise NgioValueError(f"Invalid value {value} of type {type(value)}")
26
+ value = [_validate_int(v, shape=shape) for v in value]
27
+ return value
28
+
29
+
30
+ def _validate_slice(value: slice, shape: int) -> slice:
31
+ start = value.start if value.start is not None else 0
32
+ start = max(start, 0)
33
+ stop = value.stop if value.stop is not None else shape
34
+ return slice(start, stop)
35
+
36
+
37
+ class SliceTransform(AxesTransformation):
38
+ slices: tuple[slice | tuple[int, ...], ...]
39
+
40
+
41
+ def compute_and_slices(
42
+ *,
43
+ dimensions: Dimensions,
44
+ **slice_kwargs: slice | int | Iterable[int],
45
+ ) -> SliceTransform:
46
+ _slices = {}
47
+ axes_names = dimensions._axes_mapper.on_disk_axes_names
48
+ for axis_name, slice_ in slice_kwargs.items():
49
+ axis = dimensions._axes_mapper.get_axis(axis_name)
50
+ if axis is None:
51
+ raise NgioValueError(
52
+ f"Invalid axis {axis_name}. "
53
+ f"Not found on the on-disk axes {axes_names}. "
54
+ "If you want to get/set a singletorn value include "
55
+ "it in the axes_order parameter."
56
+ )
57
+
58
+ shape = dimensions.get(axis.on_disk_name)
59
+
60
+ if isinstance(slice_, int):
61
+ slice_ = _validate_int(slice_, shape)
62
+ slice_ = slice(slice_, slice_ + 1)
63
+
64
+ elif isinstance(slice_, Iterable):
65
+ slice_ = _validate_iter_of_ints(slice_, shape)
66
+ slice_ = tuple(slice_)
67
+
68
+ elif isinstance(slice_, slice):
69
+ slice_ = _validate_slice(slice_, shape)
70
+
71
+ elif not isinstance(slice_, slice):
72
+ raise ValueError(
73
+ f"Invalid slice definition {slice_} of type {type(slice_)}"
74
+ )
75
+ _slices[axis.on_disk_name] = slice_
76
+
77
+ slices = tuple(_slices.get(axis, slice(None)) for axis in axes_names)
78
+ return SliceTransform(slices=slices)
79
+
80
+
81
+ def numpy_get_slice(array: zarr.Array, slices: SliceTransform) -> np.ndarray:
82
+ return array[slices.slices]
83
+
84
+
85
+ def dask_get_slice(array: zarr.Array, slices: SliceTransform) -> da.Array:
86
+ da_array = da.from_zarr(array)
87
+ return da_array[slices.slices]
88
+
89
+
90
+ def numpy_set_slice(
91
+ array: zarr.Array, patch: np.ndarray, slices: SliceTransform
92
+ ) -> None:
93
+ array[slices.slices] = patch
94
+
95
+
96
+ def dask_set_slice(array: zarr.Array, patch: da.Array, slices: SliceTransform) -> None:
97
+ da.to_zarr(arr=patch, url=array, region=slices.slices)
@@ -3,7 +3,6 @@ from typing import Literal
3
3
 
4
4
  import dask.array as da
5
5
  import numpy as np
6
- import zarr
7
6
  from scipy.ndimage import zoom as scipy_zoom
8
7
 
9
8
 
@@ -76,7 +75,7 @@ def _zoom_inputs_check(
76
75
  return _scale, _target_shape
77
76
 
78
77
 
79
- def _dask_zoom(
78
+ def dask_zoom(
80
79
  source_array: da.Array,
81
80
  scale: tuple[int, ...] | None = None,
82
81
  target_shape: tuple[int, ...] | None = None,
@@ -127,7 +126,7 @@ def _dask_zoom(
127
126
  return out_array
128
127
 
129
128
 
130
- def _numpy_zoom(
129
+ def numpy_zoom(
131
130
  source_array: np.ndarray,
132
131
  scale: tuple[int, ...] | None = None,
133
132
  target_shape: tuple[int, ...] | None = None,
@@ -155,78 +154,3 @@ def _numpy_zoom(
155
154
  )
156
155
  assert isinstance(out_array, np.ndarray)
157
156
  return out_array
158
-
159
-
160
- def on_disk_zoom(
161
- source: zarr.Array,
162
- target: zarr.Array,
163
- order: Literal[0, 1, 2] = 1,
164
- mode: Literal["dask", "numpy"] = "dask",
165
- ) -> None:
166
- """Apply a zoom operation from a source zarr array to a target zarr array.
167
-
168
- Args:
169
- source (zarr.Array): The source array to zoom.
170
- target (zarr.Array): The target array to save the zoomed result to.
171
- order (Literal[0, 1, 2]): The order of interpolation. Defaults to 1.
172
- mode (Literal["dask", "numpy"]): The mode to use. Defaults to "dask".
173
- """
174
- if not isinstance(source, zarr.Array):
175
- raise ValueError("source must be a zarr array")
176
-
177
- if not isinstance(target, zarr.Array):
178
- raise ValueError("target must be a zarr array")
179
-
180
- if source.dtype != target.dtype:
181
- raise ValueError("source and target must have the same dtype")
182
-
183
- assert mode in ["dask", "numpy"], "mode must be either 'dask' or 'numpy'"
184
-
185
- if mode == "numpy":
186
- target[...] = _numpy_zoom(source[...], target_shape=target.shape, order=order)
187
- return None
188
-
189
- source_array = da.from_zarr(source)
190
- target_array = _dask_zoom(source_array, target_shape=target.shape, order=order)
191
-
192
- target_array = target_array.rechunk(target.chunks)
193
- target_array.compute_chunk_sizes()
194
- target_array.to_zarr(target)
195
-
196
-
197
- def on_disk_coarsen(
198
- source: zarr.Array,
199
- target: zarr.Array,
200
- aggregation_function: np.ufunc,
201
- ) -> None:
202
- """Apply a coarsening operation from a source zarr array to a target zarr array.
203
-
204
- Args:
205
- source (zarr.Array): The source array to coarsen.
206
- target (zarr.Array): The target array to save the coarsened result to.
207
- aggregation_function (np.ufunc): The aggregation function to use.
208
- """
209
- source_array = da.from_zarr(source)
210
-
211
- _scale, _target_shape = _zoom_inputs_check(
212
- source_array=source_array, scale=None, target_shape=target.shape
213
- )
214
-
215
- assert (
216
- _target_shape == target.shape
217
- ), "Target shape must match the target array shape"
218
- coarsening_setup = {}
219
- for i, s in enumerate(_scale):
220
- factor = 1 / s
221
- if factor.is_integer():
222
- coarsening_setup[i] = int(factor)
223
- else:
224
- raise ValueError(
225
- f"Coarsening factor must be an integer, got {factor} on axis {i}"
226
- )
227
-
228
- out_target = da.coarsen(
229
- aggregation_function, source_array, coarsening_setup, trim_excess=True
230
- )
231
- out_target = out_target.rechunk(target.chunks)
232
- out_target.to_zarr(target)
ngio/hcs/__init__.py ADDED
@@ -0,0 +1,60 @@
1
+ """OME-Zarr HCS objects models."""
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ngio.images import OmeZarrContainer
7
+
8
+
9
+ class OmeZarrPlate:
10
+ """Placeholder for the OME-Zarr image object."""
11
+
12
+ def __init__(self, *args, **kwargs):
13
+ """Initialize the OME-Zarr plate."""
14
+ raise NotImplementedError
15
+
16
+ def wells(self) -> list[str]:
17
+ """Return the wells."""
18
+ raise NotImplementedError
19
+
20
+ def columns(self) -> list[str]:
21
+ """Return the number of columns."""
22
+ raise NotImplementedError
23
+
24
+ def rows(self) -> list[str]:
25
+ """Return the number of rows."""
26
+ raise NotImplementedError
27
+
28
+ def get_omezarr_well(self, *args, **kwargs) -> "OmeZarrWell":
29
+ """Return the OME-Zarr well."""
30
+ raise NotImplementedError
31
+
32
+ def get_omezarr_image(self, *args, **kwargs) -> "OmeZarrContainer":
33
+ """Return the OME-Zarr image."""
34
+ raise NotImplementedError
35
+
36
+
37
+ class OmeZarrWell:
38
+ """Placeholder for the Image object."""
39
+
40
+ def __init__(self, *args, **kwargs):
41
+ """Initialize the OME-Zarr well."""
42
+ raise NotImplementedError
43
+
44
+ def acquisitions(self) -> list[str]:
45
+ """Return the acquisition."""
46
+ raise NotImplementedError
47
+
48
+ def get_ome_zarr_image(self, *args, **kwargs) -> "OmeZarrContainer":
49
+ """Return the OME-Zarr image."""
50
+ raise NotImplementedError
51
+
52
+
53
+ def open_omezarr_plate(*args, **kwargs):
54
+ """Open an OME-Zarr plate."""
55
+ return OmeZarrPlate(*args, **kwargs)
56
+
57
+
58
+ def open_omezarr_well(*args, **kwargs):
59
+ """Open an OME-Zarr well."""
60
+ return OmeZarrWell(*args, **kwargs)
@@ -0,0 +1,23 @@
1
+ """OME-Zarr object models."""
2
+
3
+ from ngio.images.image import Image, ImagesContainer
4
+ from ngio.images.label import Label, LabelsContainer
5
+ from ngio.images.omezarr_container import (
6
+ OmeZarrContainer,
7
+ create_empty_omezarr,
8
+ create_omezarr_from_array,
9
+ open_image,
10
+ open_omezarr_container,
11
+ )
12
+
13
+ __all__ = [
14
+ "Image",
15
+ "ImagesContainer",
16
+ "Label",
17
+ "LabelsContainer",
18
+ "OmeZarrContainer",
19
+ "create_empty_omezarr",
20
+ "create_omezarr_from_array",
21
+ "open_image",
22
+ "open_omezarr_container",
23
+ ]
@@ -0,0 +1,240 @@
1
+ """Generic class to handle Image-like data in a OME-NGFF file."""
2
+
3
+ from collections.abc import Collection, Iterable
4
+ from typing import Generic, Literal, TypeVar
5
+
6
+ import zarr
7
+
8
+ from ngio.common import (
9
+ ArrayLike,
10
+ Dimensions,
11
+ WorldCooROI,
12
+ consolidate_pyramid,
13
+ get_pipe,
14
+ set_pipe,
15
+ )
16
+ from ngio.ome_zarr_meta import (
17
+ Dataset,
18
+ ImageMetaHandler,
19
+ LabelMetaHandler,
20
+ PixelSize,
21
+ )
22
+ from ngio.utils import (
23
+ NgioFileExistsError,
24
+ ZarrGroupHandler,
25
+ )
26
+
27
+ _image_handler = TypeVar("_image_handler", ImageMetaHandler, LabelMetaHandler)
28
+
29
+
30
+ class AbstractImage(Generic[_image_handler]):
31
+ """A class to handle a single image (or level) in an OME-Zarr image.
32
+
33
+ This class is meant to be subclassed by specific image types.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ group_handler: ZarrGroupHandler,
39
+ path: str,
40
+ meta_handler: _image_handler,
41
+ ) -> None:
42
+ """Initialize the Image at a single level.
43
+
44
+ Args:
45
+ group_handler: The Zarr group handler.
46
+ path: The path to the image in the omezarr file.
47
+ meta_handler: The image metadata handler.
48
+
49
+ """
50
+ self._path = path
51
+ self._group_handler = group_handler
52
+ self._meta_handler = meta_handler
53
+
54
+ self._dataset = self._meta_handler.meta.get_dataset(path=path)
55
+ self._pixel_size = self._dataset.pixel_size
56
+
57
+ try:
58
+ self._zarr_array = self._group_handler.get_array(self._dataset.path)
59
+ except NgioFileExistsError as e:
60
+ raise NgioFileExistsError(f"Could not find the dataset at {path}.") from e
61
+
62
+ self._dimensions = Dimensions(
63
+ shape=self._zarr_array.shape, axes_mapper=self._dataset.axes_mapper
64
+ )
65
+
66
+ self._axer_mapper = self._dataset.axes_mapper
67
+
68
+ def __repr__(self) -> str:
69
+ """Return a string representation of the image."""
70
+ return f"Image(path={self.path}, {self.dimensions})"
71
+
72
+ @property
73
+ def meta_handler(self) -> _image_handler:
74
+ """Return the metadata."""
75
+ return self._meta_handler
76
+
77
+ @property
78
+ def zarr_array(self) -> zarr.Array:
79
+ """Return the Zarr array."""
80
+ return self._zarr_array
81
+
82
+ @property
83
+ def shape(self) -> tuple[int, ...]:
84
+ """Return the shape of the image."""
85
+ return self.zarr_array.shape
86
+
87
+ @property
88
+ def dtype(self) -> str:
89
+ """Return the dtype of the image."""
90
+ return str(self.zarr_array.dtype)
91
+
92
+ @property
93
+ def chunks(self) -> tuple[int, ...]:
94
+ """Return the chunks of the image."""
95
+ return self.zarr_array.chunks
96
+
97
+ @property
98
+ def dimensions(self) -> Dimensions:
99
+ """Return the dimensions of the image."""
100
+ return self._dimensions
101
+
102
+ @property
103
+ def pixel_size(self) -> PixelSize:
104
+ """Return the pixel size of the image."""
105
+ return self._pixel_size
106
+
107
+ @property
108
+ def dataset(self) -> Dataset:
109
+ """Return the dataset of the image."""
110
+ return self._dataset
111
+
112
+ @property
113
+ def path(self) -> str:
114
+ """Return the path of the image."""
115
+ return self._dataset.path
116
+
117
+ def get_array(
118
+ self,
119
+ axes_order: Collection[str] | None = None,
120
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
121
+ **slice_kwargs: slice | int | Iterable[int],
122
+ ) -> ArrayLike:
123
+ """Get a slice of the image.
124
+
125
+ Args:
126
+ axes_order: The order of the axes to return the array.
127
+ mode: The mode to return the array.
128
+ **slice_kwargs: The slices to get the array.
129
+
130
+ Returns:
131
+ The array of the region of interest.
132
+ """
133
+ return get_pipe(
134
+ array=self.zarr_array,
135
+ dimensions=self.dimensions,
136
+ axes_order=axes_order,
137
+ mode=mode,
138
+ **slice_kwargs,
139
+ )
140
+
141
+ def get_roi(
142
+ self,
143
+ roi: WorldCooROI,
144
+ axes_order: Collection[str] | None = None,
145
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
146
+ **slice_kwargs: slice | int | Iterable[int],
147
+ ) -> ArrayLike:
148
+ """Get a slice of the image.
149
+
150
+ Args:
151
+ roi: The region of interest to get the array.
152
+ axes_order: The order of the axes to return the array.
153
+ mode: The mode to return the array.
154
+ **slice_kwargs: The slices to get the array.
155
+
156
+ Returns:
157
+ The array of the region of interest.
158
+ """
159
+ raster_roi = roi.to_raster_coo(
160
+ pixel_size=self.pixel_size, dimensions=self.dimensions
161
+ ).to_slices()
162
+
163
+ for key in slice_kwargs.keys():
164
+ if key in raster_roi:
165
+ raise ValueError(
166
+ f"Key {key} is already in the slice_kwargs. "
167
+ "Ambiguous which one to use: "
168
+ f"{key}={slice_kwargs[key]} or roi_{key}={raster_roi[key]}"
169
+ )
170
+ return self.get_array(
171
+ axes_order=axes_order, mode=mode, **raster_roi, **slice_kwargs
172
+ )
173
+
174
+ def set_array(
175
+ self,
176
+ patch: ArrayLike,
177
+ axes_order: Collection[str] | None = None,
178
+ **slice_kwargs: slice | int | Iterable[int],
179
+ ) -> None:
180
+ """Set a slice of the image.
181
+
182
+ Args:
183
+ patch: The patch to set.
184
+ axes_order: The order of the axes to set the patch.
185
+ **slice_kwargs: The slices to set the patch.
186
+
187
+ """
188
+ set_pipe(
189
+ array=self.zarr_array,
190
+ patch=patch,
191
+ dimensions=self.dimensions,
192
+ axes_order=axes_order,
193
+ **slice_kwargs,
194
+ )
195
+
196
+ def set_roi(
197
+ self,
198
+ roi: WorldCooROI,
199
+ patch: ArrayLike,
200
+ axes_order: Collection[str] | None = None,
201
+ **slice_kwargs: slice | int | Iterable[int],
202
+ ) -> None:
203
+ """Set a slice of the image.
204
+
205
+ Args:
206
+ roi: The region of interest to set the patch.
207
+ patch: The patch to set.
208
+ axes_order: The order of the axes to set the patch.
209
+ **slice_kwargs: The slices to set the patch.
210
+
211
+ """
212
+ raster_roi = roi.to_raster_coo(
213
+ pixel_size=self.pixel_size, dimensions=self.dimensions
214
+ ).to_slices()
215
+
216
+ for key in slice_kwargs.keys():
217
+ if key in raster_roi:
218
+ raise ValueError(
219
+ f"Key {key} is already in the slice_kwargs. "
220
+ "Ambiguous which one to use: "
221
+ f"{key}={slice_kwargs[key]} or roi_{key}={raster_roi[key]}"
222
+ )
223
+ self.set_array(patch=patch, axes_order=axes_order, **raster_roi, **slice_kwargs)
224
+
225
+
226
+ def consolidate_image(
227
+ image: AbstractImage,
228
+ order: Literal[0, 1, 2] = 1,
229
+ mode: Literal["dask", "numpy", "coarsen"] = "dask",
230
+ ) -> None:
231
+ """Consolidate the image on disk."""
232
+ target_paths = image._meta_handler.meta.paths
233
+ targets = [
234
+ image._group_handler.get_array(path)
235
+ for path in target_paths
236
+ if path != image.path
237
+ ]
238
+ consolidate_pyramid(
239
+ source=image.zarr_array, targets=targets, order=order, mode=mode
240
+ )