ngio 0.1.6__py3-none-any.whl → 0.2.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 (87) hide show
  1. ngio/__init__.py +33 -5
  2. ngio/common/__init__.py +54 -0
  3. ngio/common/_array_pipe.py +265 -0
  4. ngio/common/_axes_transforms.py +64 -0
  5. ngio/common/_common_types.py +5 -0
  6. ngio/common/_dimensions.py +120 -0
  7. ngio/common/_masking_roi.py +158 -0
  8. ngio/common/_pyramid.py +228 -0
  9. ngio/common/_roi.py +165 -0
  10. ngio/common/_slicer.py +96 -0
  11. ngio/{pipes/_zoom_utils.py → common/_zoom.py} +7 -81
  12. ngio/hcs/__init__.py +5 -0
  13. ngio/hcs/plate.py +448 -0
  14. ngio/images/__init__.py +23 -0
  15. ngio/images/abstract_image.py +349 -0
  16. ngio/images/create.py +270 -0
  17. ngio/images/image.py +453 -0
  18. ngio/images/label.py +285 -0
  19. ngio/images/masked_image.py +273 -0
  20. ngio/images/ome_zarr_container.py +738 -0
  21. ngio/ome_zarr_meta/__init__.py +47 -0
  22. ngio/ome_zarr_meta/_meta_handlers.py +791 -0
  23. ngio/ome_zarr_meta/ngio_specs/__init__.py +71 -0
  24. ngio/ome_zarr_meta/ngio_specs/_axes.py +481 -0
  25. ngio/ome_zarr_meta/ngio_specs/_channels.py +389 -0
  26. ngio/ome_zarr_meta/ngio_specs/_dataset.py +134 -0
  27. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +377 -0
  28. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +489 -0
  29. ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +116 -0
  30. ngio/ome_zarr_meta/v04/__init__.py +23 -0
  31. ngio/ome_zarr_meta/v04/_v04_spec_utils.py +485 -0
  32. ngio/tables/__init__.py +24 -6
  33. ngio/tables/_validators.py +190 -0
  34. ngio/tables/backends/__init__.py +8 -0
  35. ngio/tables/backends/_abstract_backend.py +71 -0
  36. ngio/tables/backends/_anndata_utils.py +198 -0
  37. ngio/tables/backends/_anndata_v1.py +76 -0
  38. ngio/tables/backends/_json_v1.py +56 -0
  39. ngio/tables/backends/_table_backends.py +102 -0
  40. ngio/tables/tables_container.py +310 -0
  41. ngio/tables/v1/__init__.py +5 -5
  42. ngio/tables/v1/_feature_table.py +182 -0
  43. ngio/tables/v1/_generic_table.py +160 -179
  44. ngio/tables/v1/_roi_table.py +366 -0
  45. ngio/utils/__init__.py +26 -10
  46. ngio/utils/_datasets.py +53 -0
  47. ngio/utils/_errors.py +10 -4
  48. ngio/utils/_fractal_fsspec_store.py +13 -0
  49. ngio/utils/_logger.py +3 -1
  50. ngio/utils/_zarr_utils.py +401 -0
  51. {ngio-0.1.6.dist-info → ngio-0.2.0.dist-info}/METADATA +30 -43
  52. ngio-0.2.0.dist-info/RECORD +54 -0
  53. ngio/core/__init__.py +0 -7
  54. ngio/core/dimensions.py +0 -122
  55. ngio/core/image_handler.py +0 -228
  56. ngio/core/image_like_handler.py +0 -549
  57. ngio/core/label_handler.py +0 -410
  58. ngio/core/ngff_image.py +0 -387
  59. ngio/core/roi.py +0 -92
  60. ngio/core/utils.py +0 -287
  61. ngio/io/__init__.py +0 -19
  62. ngio/io/_zarr.py +0 -88
  63. ngio/io/_zarr_array_utils.py +0 -0
  64. ngio/io/_zarr_group_utils.py +0 -60
  65. ngio/iterators/__init__.py +0 -1
  66. ngio/ngff_meta/__init__.py +0 -27
  67. ngio/ngff_meta/fractal_image_meta.py +0 -1267
  68. ngio/ngff_meta/meta_handler.py +0 -92
  69. ngio/ngff_meta/utils.py +0 -235
  70. ngio/ngff_meta/v04/__init__.py +0 -6
  71. ngio/ngff_meta/v04/specs.py +0 -158
  72. ngio/ngff_meta/v04/zarr_utils.py +0 -376
  73. ngio/pipes/__init__.py +0 -7
  74. ngio/pipes/_slicer_transforms.py +0 -176
  75. ngio/pipes/_transforms.py +0 -33
  76. ngio/pipes/data_pipe.py +0 -52
  77. ngio/tables/_ad_reader.py +0 -80
  78. ngio/tables/_utils.py +0 -301
  79. ngio/tables/tables_group.py +0 -252
  80. ngio/tables/v1/feature_tables.py +0 -182
  81. ngio/tables/v1/masking_roi_tables.py +0 -243
  82. ngio/tables/v1/roi_tables.py +0 -285
  83. ngio/utils/_common_types.py +0 -5
  84. ngio/utils/_pydantic_utils.py +0 -52
  85. ngio-0.1.6.dist-info/RECORD +0 -44
  86. {ngio-0.1.6.dist-info → ngio-0.2.0.dist-info}/WHEEL +0 -0
  87. {ngio-0.1.6.dist-info → ngio-0.2.0.dist-info}/licenses/LICENSE +0 -0
ngio/__init__.py CHANGED
@@ -2,14 +2,42 @@
2
2
 
3
3
  from importlib.metadata import PackageNotFoundError, version
4
4
 
5
- from ngio.core import Image, Label, NgffImage
6
-
7
- __all__ = ["Image", "Label", "NgffImage"]
8
-
9
-
10
5
  try:
11
6
  __version__ = version("ngio")
12
7
  except PackageNotFoundError: # pragma: no cover
13
8
  __version__ = "uninstalled"
14
9
  __author__ = "Lorenzo Cerrone"
15
10
  __email__ = "lorenzo.cerrone@uzh.ch"
11
+
12
+ from ngio.common import ArrayLike, Dimensions, Roi, RoiPixels
13
+ from ngio.hcs import OmeZarrPlate, create_empty_plate, open_ome_zarr_plate
14
+ from ngio.images import (
15
+ Image,
16
+ Label,
17
+ OmeZarrContainer,
18
+ create_empty_ome_zarr,
19
+ create_ome_zarr_from_array,
20
+ open_image,
21
+ open_ome_zarr_container,
22
+ )
23
+ from ngio.ome_zarr_meta.ngio_specs import AxesSetup, ImageInWellPath, PixelSize
24
+
25
+ __all__ = [
26
+ "ArrayLike",
27
+ "AxesSetup",
28
+ "Dimensions",
29
+ "Image",
30
+ "ImageInWellPath",
31
+ "Label",
32
+ "OmeZarrContainer",
33
+ "OmeZarrPlate",
34
+ "PixelSize",
35
+ "Roi",
36
+ "RoiPixels",
37
+ "create_empty_ome_zarr",
38
+ "create_empty_plate",
39
+ "create_ome_zarr_from_array",
40
+ "open_image",
41
+ "open_ome_zarr_container",
42
+ "open_ome_zarr_plate",
43
+ ]
@@ -0,0 +1,54 @@
1
+ """Common classes and functions that are used across the package."""
2
+
3
+ from ngio.common._array_pipe import (
4
+ get_masked_pipe,
5
+ get_pipe,
6
+ set_masked_pipe,
7
+ set_pipe,
8
+ )
9
+ from ngio.common._axes_transforms import (
10
+ transform_dask_array,
11
+ transform_list,
12
+ transform_numpy_array,
13
+ )
14
+ from ngio.common._common_types import ArrayLike
15
+ from ngio.common._dimensions import Dimensions
16
+ from ngio.common._masking_roi import compute_masking_roi
17
+ from ngio.common._pyramid import consolidate_pyramid, init_empty_pyramid, on_disk_zoom
18
+ from ngio.common._roi import Roi, RoiPixels, roi_to_slice_kwargs
19
+ from ngio.common._slicer import (
20
+ SliceTransform,
21
+ compute_and_slices,
22
+ dask_get_slice,
23
+ dask_set_slice,
24
+ numpy_get_slice,
25
+ numpy_set_slice,
26
+ )
27
+ from ngio.common._zoom import dask_zoom, numpy_zoom
28
+
29
+ __all__ = [
30
+ "ArrayLike",
31
+ "Dimensions",
32
+ "Roi",
33
+ "RoiPixels",
34
+ "SliceTransform",
35
+ "compute_and_slices",
36
+ "compute_masking_roi",
37
+ "consolidate_pyramid",
38
+ "dask_get_slice",
39
+ "dask_set_slice",
40
+ "dask_zoom",
41
+ "get_masked_pipe",
42
+ "get_pipe",
43
+ "init_empty_pyramid",
44
+ "numpy_get_slice",
45
+ "numpy_set_slice",
46
+ "numpy_zoom",
47
+ "on_disk_zoom",
48
+ "roi_to_slice_kwargs",
49
+ "set_masked_pipe",
50
+ "set_pipe",
51
+ "transform_dask_array",
52
+ "transform_list",
53
+ "transform_numpy_array",
54
+ ]
@@ -0,0 +1,265 @@
1
+ from collections.abc import Collection, Iterable
2
+ from typing import Literal
3
+
4
+ import dask
5
+ import dask.delayed
6
+ import numpy as np
7
+ import zarr
8
+
9
+ from ngio.common._axes_transforms import transform_dask_array, transform_numpy_array
10
+ from ngio.common._common_types import ArrayLike
11
+ from ngio.common._dimensions import Dimensions
12
+ from ngio.common._slicer import (
13
+ SliceTransform,
14
+ compute_and_slices,
15
+ dask_get_slice,
16
+ dask_set_slice,
17
+ numpy_get_slice,
18
+ numpy_set_slice,
19
+ )
20
+ from ngio.ome_zarr_meta.ngio_specs import AxesTransformation
21
+ from ngio.utils import NgioValueError
22
+
23
+
24
+ def _compute_from_disk_transforms(
25
+ *,
26
+ dimensions: Dimensions,
27
+ axes_order: Collection[str] | None = None,
28
+ **slice_kwargs: slice | int | Iterable[int],
29
+ ) -> tuple[SliceTransform, tuple[AxesTransformation, ...]]:
30
+ slices = compute_and_slices(dimensions=dimensions, **slice_kwargs)
31
+
32
+ if axes_order is None:
33
+ return slices, ()
34
+
35
+ additional_transformations = dimensions._axes_mapper.to_order(axes_order)
36
+ return slices, additional_transformations
37
+
38
+
39
+ def _compute_to_disk_transforms(
40
+ *,
41
+ dimensions: Dimensions,
42
+ axes_order: Collection[str] | None = None,
43
+ **slice_kwargs: slice | int | Iterable[int],
44
+ ) -> tuple[SliceTransform, tuple[AxesTransformation, ...]]:
45
+ slices = compute_and_slices(dimensions=dimensions, **slice_kwargs)
46
+ if axes_order is None:
47
+ return slices, ()
48
+
49
+ additional_transformations = dimensions._axes_mapper.from_order(axes_order)
50
+ return slices, additional_transformations
51
+
52
+
53
+ def _numpy_get_pipe(
54
+ array: zarr.Array,
55
+ slices: SliceTransform,
56
+ transformations: tuple[AxesTransformation, ...],
57
+ ) -> np.ndarray:
58
+ array = numpy_get_slice(array, slices)
59
+ return transform_numpy_array(array, transformations)
60
+
61
+
62
+ def _delayed_numpy_get_pipe(
63
+ array: zarr.Array,
64
+ slices: SliceTransform,
65
+ transformations: tuple[AxesTransformation, ...],
66
+ ) -> dask.delayed:
67
+ array = dask.delayed(numpy_get_slice)(array, slices)
68
+ return dask.delayed(transform_numpy_array)(array, transformations)
69
+
70
+
71
+ def _dask_get_pipe(
72
+ array: zarr.Array,
73
+ slices: SliceTransform,
74
+ transformations: tuple[AxesTransformation, ...],
75
+ ) -> dask.array:
76
+ array = dask_get_slice(array, slices)
77
+ return transform_dask_array(array, transformations)
78
+
79
+
80
+ def _numpy_set_pipe(
81
+ array: zarr.Array,
82
+ patch: np.ndarray,
83
+ slices: SliceTransform,
84
+ transformations: tuple[AxesTransformation, ...],
85
+ ) -> None:
86
+ patch = transform_numpy_array(patch, transformations)
87
+ numpy_set_slice(array, patch, slices)
88
+
89
+
90
+ def _dask_set_pipe(
91
+ array: zarr.Array,
92
+ patch: np.ndarray,
93
+ slices: SliceTransform,
94
+ transformations: tuple[AxesTransformation, ...],
95
+ ) -> None:
96
+ patch = transform_dask_array(patch, transformations)
97
+ dask_set_slice(array, patch, slices)
98
+
99
+
100
+ def _delayed_numpy_set_pipe(
101
+ array: zarr.Array,
102
+ patch: np.ndarray,
103
+ slices: SliceTransform,
104
+ transformations: tuple[AxesTransformation, ...],
105
+ ) -> dask.delayed:
106
+ patch = dask.delayed(transform_numpy_array)(patch, transformations)
107
+ return dask.delayed(numpy_set_slice)(array, patch, slices)
108
+
109
+
110
+ def get_pipe(
111
+ array: zarr.Array,
112
+ *,
113
+ dimensions: Dimensions,
114
+ axes_order: Collection[str] | None = None,
115
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
116
+ **slice_kwargs: slice | int | Iterable[int],
117
+ ):
118
+ slices, transformations = _compute_from_disk_transforms(
119
+ dimensions=dimensions, axes_order=axes_order, **slice_kwargs
120
+ )
121
+ match mode:
122
+ case "numpy":
123
+ return _numpy_get_pipe(array, slices, transformations)
124
+ case "dask":
125
+ return _dask_get_pipe(array, slices, transformations)
126
+
127
+ case "delayed":
128
+ return _delayed_numpy_get_pipe(array, slices, transformations)
129
+
130
+ case _:
131
+ raise NgioValueError(
132
+ f"Unknown get pipe mode {mode}, expected 'numpy', 'dask' or 'delayed'."
133
+ )
134
+
135
+
136
+ def set_pipe(
137
+ array: zarr.Array,
138
+ patch: ArrayLike,
139
+ *,
140
+ dimensions: Dimensions,
141
+ axes_order: Collection[str] | None = None,
142
+ **slice_kwargs: slice | int | Iterable[int],
143
+ ):
144
+ slices, transformations = _compute_to_disk_transforms(
145
+ dimensions=dimensions, axes_order=axes_order, **slice_kwargs
146
+ )
147
+ if isinstance(patch, dask.array.Array):
148
+ _dask_set_pipe(
149
+ array=array, patch=patch, slices=slices, transformations=transformations
150
+ )
151
+ elif isinstance(patch, np.ndarray):
152
+ _numpy_set_pipe(
153
+ array=array, patch=patch, slices=slices, transformations=transformations
154
+ )
155
+ elif isinstance(patch, dask.delayed.Delayed):
156
+ _delayed_numpy_set_pipe(
157
+ array=array, patch=patch, slices=slices, transformations=transformations
158
+ )
159
+ else:
160
+ raise NgioValueError("Unknown patch type, expected numpy, dask or delayed.")
161
+
162
+
163
+ def _mask_pipe_common(
164
+ array: zarr.Array,
165
+ label_array: zarr.Array,
166
+ label: int,
167
+ *,
168
+ dimensions_array: Dimensions,
169
+ dimensions_label: Dimensions,
170
+ axes_order: Collection[str] | None = None,
171
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
172
+ **slice_kwargs: slice | int | Iterable[int],
173
+ ):
174
+ array_patch = get_pipe(
175
+ array,
176
+ dimensions=dimensions_array,
177
+ axes_order=axes_order,
178
+ mode=mode,
179
+ **slice_kwargs,
180
+ )
181
+
182
+ if not dimensions_label.has_axis("c"):
183
+ # Remove the 'c' from the slice_kwargs
184
+ # This will not work if the query uses non-default
185
+ # axes names for channel
186
+ slice_kwargs = {k: v for k, v in slice_kwargs.items() if k != "c"}
187
+
188
+ label_patch = get_pipe(
189
+ label_array,
190
+ dimensions=dimensions_label,
191
+ axes_order=axes_order,
192
+ mode=mode,
193
+ **slice_kwargs,
194
+ )
195
+
196
+ if isinstance(array_patch, np.ndarray):
197
+ label_patch = np.broadcast_to(label_patch, array_patch.shape)
198
+ elif isinstance(array_patch, dask.array.Array):
199
+ label_patch = dask.array.broadcast_to(label_patch, array_patch.shape)
200
+ else:
201
+ raise NgioValueError(f"Mode {mode} not yet supported for masked array.")
202
+
203
+ mask = label_patch == label
204
+ return array_patch, mask
205
+
206
+
207
+ def get_masked_pipe(
208
+ array: zarr.Array,
209
+ label_array: zarr.Array,
210
+ label: int,
211
+ *,
212
+ dimensions_array: Dimensions,
213
+ dimensions_label: Dimensions,
214
+ axes_order: Collection[str] | None = None,
215
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
216
+ **slice_kwargs: slice | int | Iterable[int],
217
+ ):
218
+ array_patch, mask = _mask_pipe_common(
219
+ array=array,
220
+ label_array=label_array,
221
+ label=label,
222
+ dimensions_array=dimensions_array,
223
+ dimensions_label=dimensions_label,
224
+ axes_order=axes_order,
225
+ mode=mode,
226
+ **slice_kwargs,
227
+ )
228
+ array_patch[~mask] = 0
229
+ return array_patch
230
+
231
+
232
+ def set_masked_pipe(
233
+ array: zarr.Array,
234
+ label_array: zarr.Array,
235
+ label: int,
236
+ patch: ArrayLike,
237
+ *,
238
+ dimensions_array: Dimensions,
239
+ dimensions_label: Dimensions,
240
+ axes_order: Collection[str] | None = None,
241
+ **slice_kwargs: slice | int | Iterable[int],
242
+ ):
243
+ if isinstance(patch, dask.array.Array):
244
+ mode = "dask"
245
+ elif isinstance(patch, np.ndarray):
246
+ mode = "numpy"
247
+ else:
248
+ raise NgioValueError(
249
+ "Mode not yet supported for masked array. Expected a numpy or dask array."
250
+ )
251
+
252
+ array_patch, mask = _mask_pipe_common(
253
+ array=array,
254
+ label_array=label_array,
255
+ label=label,
256
+ dimensions_array=dimensions_array,
257
+ dimensions_label=dimensions_label,
258
+ axes_order=axes_order,
259
+ mode=mode,
260
+ **slice_kwargs,
261
+ )
262
+ patch = np.where(mask, patch, array_patch)
263
+ set_pipe(
264
+ array, patch, dimensions=dimensions_array, axes_order=axes_order, **slice_kwargs
265
+ )
@@ -0,0 +1,64 @@
1
+ from typing import TypeVar
2
+
3
+ import dask.array as da
4
+ import numpy as np
5
+
6
+ from ngio.ome_zarr_meta.ngio_specs._axes import (
7
+ AxesExpand,
8
+ AxesSqueeze,
9
+ AxesTransformation,
10
+ AxesTranspose,
11
+ )
12
+ from ngio.utils import NgioValueError
13
+
14
+ T = TypeVar("T")
15
+
16
+
17
+ def transform_list(
18
+ input_list: list[T], default: T, operations: tuple[AxesTransformation, ...]
19
+ ) -> list[T]:
20
+ if isinstance(input_list, tuple):
21
+ input_list = list(input_list)
22
+
23
+ for operation in operations:
24
+ if isinstance(operation, AxesTranspose):
25
+ input_list = [input_list[i] for i in operation.axes]
26
+
27
+ if isinstance(operation, AxesExpand):
28
+ for ax in operation.axes:
29
+ input_list.insert(ax, default)
30
+ elif isinstance(operation, AxesSqueeze):
31
+ for offset, ax in enumerate(operation.axes):
32
+ input_list.pop(ax - offset)
33
+
34
+ return input_list
35
+
36
+
37
+ def transform_numpy_array(
38
+ array: np.ndarray, operations: tuple[AxesTransformation, ...]
39
+ ) -> np.ndarray:
40
+ for operation in operations:
41
+ if isinstance(operation, AxesTranspose):
42
+ array = np.transpose(array, operation.axes)
43
+ elif isinstance(operation, AxesExpand):
44
+ array = np.expand_dims(array, axis=operation.axes)
45
+ elif isinstance(operation, AxesSqueeze):
46
+ array = np.squeeze(array, axis=operation.axes)
47
+ else:
48
+ raise NgioValueError(f"Unknown operation {operation}")
49
+ return array
50
+
51
+
52
+ def transform_dask_array(
53
+ array: da.Array, operations: tuple[AxesTransformation, ...]
54
+ ) -> da.Array:
55
+ for operation in operations:
56
+ if isinstance(operation, AxesTranspose):
57
+ array = da.transpose(array, axes=operation.axes)
58
+ elif isinstance(operation, AxesExpand):
59
+ array = da.expand_dims(array, axis=operation.axes)
60
+ elif isinstance(operation, AxesSqueeze):
61
+ array = da.squeeze(array, axis=operation.axes)
62
+ else:
63
+ raise NgioValueError(f"Unknown operation {operation}")
64
+ return array
@@ -0,0 +1,5 @@
1
+ import numpy as np
2
+ import zarr
3
+ from dask import array as da
4
+
5
+ ArrayLike = np.ndarray | da.core.Array | zarr.Array # type: ignore
@@ -0,0 +1,120 @@
1
+ """Dimension metadata.
2
+
3
+ This is not related to the NGFF metadata,
4
+ but it is based on the actual metadata of the image data.
5
+ """
6
+
7
+ from collections.abc import Collection
8
+
9
+ from ngio.common._axes_transforms import transform_list
10
+ from ngio.ome_zarr_meta import AxesMapper
11
+ from ngio.utils import NgioValidationError, NgioValueError
12
+
13
+
14
+ class Dimensions:
15
+ """Dimension metadata."""
16
+
17
+ def __init__(
18
+ self,
19
+ shape: tuple[int, ...],
20
+ axes_mapper: AxesMapper,
21
+ ) -> None:
22
+ """Create a Dimension object from a Zarr array.
23
+
24
+ Args:
25
+ shape: The shape of the Zarr array.
26
+ axes_mapper: The axes mapper object.
27
+ """
28
+ self._shape = shape
29
+ self._axes_mapper = axes_mapper
30
+
31
+ if len(self._shape) != len(self._axes_mapper.on_disk_axes):
32
+ raise NgioValidationError(
33
+ "The number of dimensions must match the number of axes. "
34
+ f"Expected Axis {self._axes_mapper.on_disk_axes_names} but got shape "
35
+ f"{self._shape}."
36
+ )
37
+
38
+ def __str__(self) -> str:
39
+ """Return the string representation of the object."""
40
+ dims = ", ".join(
41
+ f"{ax.on_disk_name}: {s}"
42
+ for ax, s in zip(self._axes_mapper.on_disk_axes, self._shape, strict=True)
43
+ )
44
+ return f"Dimensions({dims})"
45
+
46
+ def get(self, axis_name: str, strict: bool = True) -> int:
47
+ """Return the dimension of the given axis name.
48
+
49
+ Args:
50
+ axis_name: The name of the axis (either canonical or non-canonical).
51
+ strict: If True, raise an error if the axis does not exist.
52
+ """
53
+ index = self._axes_mapper.get_index(axis_name)
54
+ if index is None and strict:
55
+ raise NgioValueError(f"Axis {axis_name} does not exist.")
56
+ elif index is None:
57
+ return 1
58
+ return self._shape[index]
59
+
60
+ def has_axis(self, axis_name: str) -> bool:
61
+ """Return whether the axis exists."""
62
+ index = self._axes_mapper.get_axis(axis_name)
63
+ if index is None:
64
+ return False
65
+ return True
66
+
67
+ def get_shape(self, axes_order: Collection[str]) -> tuple[int, ...]:
68
+ """Return the shape in the given axes order."""
69
+ transforms = self._axes_mapper.to_order(axes_order)
70
+ return tuple(transform_list(list(self._shape), 1, transforms))
71
+
72
+ def get_canonical_shape(self) -> tuple[int, ...]:
73
+ """Return the shape in the canonical order."""
74
+ transforms = self._axes_mapper.to_canonical()
75
+ return tuple(transform_list(list(self._shape), 1, transforms))
76
+
77
+ def __repr__(self) -> str:
78
+ """Return the string representation of the object."""
79
+ return str(self)
80
+
81
+ @property
82
+ def on_disk_shape(self) -> tuple[int, ...]:
83
+ """Return the shape as a tuple."""
84
+ return tuple(self._shape)
85
+
86
+ @property
87
+ def is_time_series(self) -> bool:
88
+ """Return whether the data is a time series."""
89
+ if self.get("t", strict=False) == 1:
90
+ return False
91
+ return True
92
+
93
+ @property
94
+ def is_2d(self) -> bool:
95
+ """Return whether the data is 2D."""
96
+ if self.get("z", strict=False) != 1:
97
+ return False
98
+ return True
99
+
100
+ @property
101
+ def is_2d_time_series(self) -> bool:
102
+ """Return whether the data is a 2D time series."""
103
+ return self.is_2d and self.is_time_series
104
+
105
+ @property
106
+ def is_3d(self) -> bool:
107
+ """Return whether the data is 3D."""
108
+ return not self.is_2d
109
+
110
+ @property
111
+ def is_3d_time_series(self) -> bool:
112
+ """Return whether the data is a 3D time series."""
113
+ return self.is_3d and self.is_time_series
114
+
115
+ @property
116
+ def is_multi_channels(self) -> bool:
117
+ """Return whether the data has multiple channels."""
118
+ if self.get("c", strict=False) == 1:
119
+ return False
120
+ return True