ngio 0.1.5__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} +51 -83
  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.5.dist-info → ngio-0.2.0.dist-info}/METADATA +31 -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 -61
  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.5.dist-info/RECORD +0 -44
  86. {ngio-0.1.5.dist-info → ngio-0.2.0.dist-info}/WHEEL +0 -0
  87. {ngio-0.1.5.dist-info → ngio-0.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,349 @@
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
+ Roi,
12
+ RoiPixels,
13
+ consolidate_pyramid,
14
+ get_pipe,
15
+ roi_to_slice_kwargs,
16
+ set_pipe,
17
+ )
18
+ from ngio.ome_zarr_meta import (
19
+ AxesMapper,
20
+ Dataset,
21
+ ImageMetaHandler,
22
+ LabelMetaHandler,
23
+ PixelSize,
24
+ )
25
+ from ngio.tables import RoiTable
26
+ from ngio.utils import NgioFileExistsError, ZarrGroupHandler
27
+
28
+ _image_handler = TypeVar("_image_handler", ImageMetaHandler, LabelMetaHandler)
29
+
30
+
31
+ class AbstractImage(Generic[_image_handler]):
32
+ """A class to handle a single image (or level) in an OME-Zarr image.
33
+
34
+ This class is meant to be subclassed by specific image types.
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ group_handler: ZarrGroupHandler,
40
+ path: str,
41
+ meta_handler: _image_handler,
42
+ ) -> None:
43
+ """Initialize the Image at a single level.
44
+
45
+ Args:
46
+ group_handler: The Zarr group handler.
47
+ path: The path to the image in the ome_zarr file.
48
+ meta_handler: The image metadata handler.
49
+
50
+ """
51
+ self._path = path
52
+ self._group_handler = group_handler
53
+ self._meta_handler = meta_handler
54
+
55
+ self._dataset = self._meta_handler.meta.get_dataset(path=path)
56
+ self._pixel_size = self._dataset.pixel_size
57
+
58
+ try:
59
+ self._zarr_array = self._group_handler.get_array(self._dataset.path)
60
+ except NgioFileExistsError as e:
61
+ raise NgioFileExistsError(f"Could not find the dataset at {path}.") from e
62
+
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
+ def __repr__(self) -> str:
70
+ """Return a string representation of the image."""
71
+ return f"Image(path={self.path}, {self.dimensions})"
72
+
73
+ @property
74
+ def meta_handler(self) -> _image_handler:
75
+ """Return the metadata."""
76
+ return self._meta_handler
77
+
78
+ @property
79
+ def zarr_array(self) -> zarr.Array:
80
+ """Return the Zarr array."""
81
+ return self._zarr_array
82
+
83
+ @property
84
+ def shape(self) -> tuple[int, ...]:
85
+ """Return the shape of the image."""
86
+ return self.zarr_array.shape
87
+
88
+ @property
89
+ def dtype(self) -> str:
90
+ """Return the dtype of the image."""
91
+ return str(self.zarr_array.dtype)
92
+
93
+ @property
94
+ def chunks(self) -> tuple[int, ...]:
95
+ """Return the chunks of the image."""
96
+ return self.zarr_array.chunks
97
+
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
+ @property
109
+ def is_3d(self) -> bool:
110
+ """Return True if the image is 3D."""
111
+ return self.dimensions.is_3d
112
+
113
+ @property
114
+ def is_2d(self) -> bool:
115
+ """Return True if the image is 2D."""
116
+ return self.dimensions.is_2d
117
+
118
+ @property
119
+ def is_time_series(self) -> bool:
120
+ """Return True if the image is a time series."""
121
+ return self.dimensions.is_time_series
122
+
123
+ @property
124
+ def is_2d_time_series(self) -> bool:
125
+ """Return True if the image is a 2D time series."""
126
+ return self.dimensions.is_2d_time_series
127
+
128
+ @property
129
+ def is_3d_time_series(self) -> bool:
130
+ """Return True if the image is a 3D time series."""
131
+ return self.dimensions.is_3d_time_series
132
+
133
+ @property
134
+ def is_multi_channels(self) -> bool:
135
+ """Return True if the image is multichannel."""
136
+ return self.dimensions.is_multi_channels
137
+
138
+ @property
139
+ def pixel_size(self) -> PixelSize:
140
+ """Return the pixel size of the image."""
141
+ return self._pixel_size
142
+
143
+ @property
144
+ def dataset(self) -> Dataset:
145
+ """Return the dataset of the image."""
146
+ return self._dataset
147
+
148
+ @property
149
+ def path(self) -> str:
150
+ """Return the path of the image."""
151
+ return self._dataset.path
152
+
153
+ def has_axis(self, axis: str) -> bool:
154
+ """Return True if the image has the given axis."""
155
+ return self.dimensions.has_axis(axis)
156
+
157
+ def get_array(
158
+ self,
159
+ axes_order: Collection[str] | None = None,
160
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
161
+ **slice_kwargs: slice | int | Iterable[int],
162
+ ) -> ArrayLike:
163
+ """Get a slice of the image.
164
+
165
+ Args:
166
+ axes_order: The order of the axes to return the array.
167
+ mode: The mode to return the array.
168
+ **slice_kwargs: The slices to get the array.
169
+
170
+ Returns:
171
+ The array of the region of interest.
172
+ """
173
+ return get_pipe(
174
+ array=self.zarr_array,
175
+ dimensions=self.dimensions,
176
+ axes_order=axes_order,
177
+ mode=mode,
178
+ **slice_kwargs,
179
+ )
180
+
181
+ def get_roi(
182
+ self,
183
+ roi: Roi,
184
+ axes_order: Collection[str] | None = None,
185
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
186
+ **slice_kwargs: slice | int | Iterable[int],
187
+ ) -> ArrayLike:
188
+ """Get a slice of the image.
189
+
190
+ Args:
191
+ roi: The region of interest to get the array.
192
+ axes_order: The order of the axes to return the array.
193
+ mode: The mode to return the array.
194
+ **slice_kwargs: The slices to get the array.
195
+
196
+ Returns:
197
+ The array of the region of interest.
198
+ """
199
+ return get_roi_pipe(
200
+ image=self, roi=roi, axes_order=axes_order, mode=mode, **slice_kwargs
201
+ )
202
+
203
+ def set_array(
204
+ self,
205
+ patch: ArrayLike,
206
+ axes_order: Collection[str] | None = None,
207
+ **slice_kwargs: slice | int | Iterable[int],
208
+ ) -> None:
209
+ """Set a slice of the image.
210
+
211
+ Args:
212
+ patch: The patch to set.
213
+ axes_order: The order of the axes to set the patch.
214
+ **slice_kwargs: The slices to set the patch.
215
+
216
+ """
217
+ set_pipe(
218
+ array=self.zarr_array,
219
+ patch=patch,
220
+ dimensions=self.dimensions,
221
+ axes_order=axes_order,
222
+ **slice_kwargs,
223
+ )
224
+
225
+ def set_roi(
226
+ self,
227
+ roi: Roi,
228
+ patch: ArrayLike,
229
+ axes_order: Collection[str] | None = None,
230
+ **slice_kwargs: slice | int | Iterable[int],
231
+ ) -> None:
232
+ """Set a slice of the image.
233
+
234
+ Args:
235
+ roi: The region of interest to set the patch.
236
+ patch: The patch to set.
237
+ axes_order: The order of the axes to set the patch.
238
+ **slice_kwargs: The slices to set the patch.
239
+
240
+ """
241
+ return set_roi_pipe(
242
+ image=self, roi=roi, patch=patch, axes_order=axes_order, **slice_kwargs
243
+ )
244
+
245
+ def build_image_roi_table(self, name: str = "image") -> RoiTable:
246
+ """Build the ROI table for an image."""
247
+ return build_image_roi_table(image=self, name=name)
248
+
249
+
250
+ def consolidate_image(
251
+ image: AbstractImage,
252
+ order: Literal[0, 1, 2] = 1,
253
+ mode: Literal["dask", "numpy", "coarsen"] = "dask",
254
+ ) -> None:
255
+ """Consolidate the image on disk."""
256
+ target_paths = image._meta_handler.meta.paths
257
+ targets = [
258
+ image._group_handler.get_array(path)
259
+ for path in target_paths
260
+ if path != image.path
261
+ ]
262
+ consolidate_pyramid(
263
+ source=image.zarr_array, targets=targets, order=order, mode=mode
264
+ )
265
+
266
+
267
+ def get_roi_pipe(
268
+ image: AbstractImage,
269
+ roi: Roi,
270
+ axes_order: Collection[str] | None = None,
271
+ mode: Literal["numpy", "dask", "delayed"] = "numpy",
272
+ **slice_kwargs: slice | int | Iterable[int],
273
+ ) -> ArrayLike:
274
+ """Get a slice of the image.
275
+
276
+ Args:
277
+ image: The image to get the ROI.
278
+ roi: The region of interest to get the array.
279
+ axes_order: The order of the axes to return the array.
280
+ mode: The mode to return the array.
281
+ **slice_kwargs: The slices to get the array.
282
+
283
+ Returns:
284
+ The array of the region of interest.
285
+ """
286
+ slice_kwargs = roi_to_slice_kwargs(
287
+ roi=roi,
288
+ pixel_size=image.pixel_size,
289
+ dimensions=image.dimensions,
290
+ **slice_kwargs,
291
+ )
292
+ return get_pipe(
293
+ array=image.zarr_array,
294
+ dimensions=image.dimensions,
295
+ axes_order=axes_order,
296
+ mode=mode,
297
+ **slice_kwargs,
298
+ )
299
+
300
+
301
+ def set_roi_pipe(
302
+ image: AbstractImage,
303
+ roi: Roi,
304
+ patch: ArrayLike,
305
+ axes_order: Collection[str] | None = None,
306
+ **slice_kwargs: slice | int | Iterable[int],
307
+ ) -> None:
308
+ """Set a slice of the image.
309
+
310
+ Args:
311
+ image: The image to set the ROI.
312
+ roi: The region of interest to set the patch.
313
+ patch: The patch to set.
314
+ axes_order: The order of the axes to set the patch.
315
+ **slice_kwargs: The slices to set the patch.
316
+
317
+ """
318
+ slice_kwargs = roi_to_slice_kwargs(
319
+ roi=roi,
320
+ pixel_size=image.pixel_size,
321
+ dimensions=image.dimensions,
322
+ **slice_kwargs,
323
+ )
324
+ set_pipe(
325
+ array=image.zarr_array,
326
+ patch=patch,
327
+ dimensions=image.dimensions,
328
+ axes_order=axes_order,
329
+ **slice_kwargs,
330
+ )
331
+
332
+
333
+ def build_image_roi_table(image: AbstractImage, name: str = "image") -> RoiTable:
334
+ """Build the ROI table for an image."""
335
+ dim_z, dim_y, dim_x = (
336
+ image.dimensions.get("z", strict=False),
337
+ image.dimensions.get("y"),
338
+ image.dimensions.get("x"),
339
+ )
340
+ image_roi = RoiPixels(
341
+ name=name,
342
+ x=0,
343
+ y=0,
344
+ z=0,
345
+ x_length=dim_x,
346
+ y_length=dim_y,
347
+ z_length=dim_z,
348
+ )
349
+ return RoiTable(rois=[image_roi.to_roi(pixel_size=image.pixel_size)])
ngio/images/create.py ADDED
@@ -0,0 +1,270 @@
1
+ """Utility functions for working with OME-Zarr images."""
2
+
3
+ from collections.abc import Collection
4
+ from typing import TypeVar
5
+
6
+ from ngio.common._pyramid import init_empty_pyramid
7
+ from ngio.ome_zarr_meta import (
8
+ NgioImageMeta,
9
+ NgioLabelMeta,
10
+ PixelSize,
11
+ get_image_meta_handler,
12
+ get_label_meta_handler,
13
+ )
14
+ from ngio.ome_zarr_meta.ngio_specs import (
15
+ SpaceUnits,
16
+ TimeUnits,
17
+ canonical_axes_order,
18
+ canonical_label_axes_order,
19
+ )
20
+ from ngio.utils import NgioValueError, StoreOrGroup, ZarrGroupHandler
21
+
22
+ _image_or_label_meta = TypeVar("_image_or_label_meta", NgioImageMeta, NgioLabelMeta)
23
+
24
+
25
+ def _init_generic_meta(
26
+ meta_type: type[_image_or_label_meta],
27
+ pixelsize: float,
28
+ axes_names: Collection[str],
29
+ z_spacing: float = 1.0,
30
+ time_spacing: float = 1.0,
31
+ levels: int | list[str] = 5,
32
+ yx_scaling_factor: float | tuple[float, float] = 2.0,
33
+ z_scaling_factor: float = 1.0,
34
+ space_unit: SpaceUnits | str | None = None,
35
+ time_unit: TimeUnits | str | None = None,
36
+ name: str | None = None,
37
+ version: str = "0.4",
38
+ ) -> tuple[_image_or_label_meta, list[float]]:
39
+ """Initialize the metadata for an image or label."""
40
+ scaling_factors = []
41
+ for ax in axes_names:
42
+ if ax == "z":
43
+ scaling_factors.append(z_scaling_factor)
44
+ elif ax in ["x"]:
45
+ if isinstance(yx_scaling_factor, tuple):
46
+ scaling_factors.append(yx_scaling_factor[1])
47
+ else:
48
+ scaling_factors.append(yx_scaling_factor)
49
+ elif ax in ["y"]:
50
+ if isinstance(yx_scaling_factor, tuple):
51
+ scaling_factors.append(yx_scaling_factor[0])
52
+ else:
53
+ scaling_factors.append(yx_scaling_factor)
54
+ else:
55
+ scaling_factors.append(1.0)
56
+
57
+ if space_unit is None:
58
+ space_unit = SpaceUnits.micrometer
59
+ elif isinstance(space_unit, str):
60
+ space_unit = SpaceUnits(space_unit)
61
+ elif not isinstance(space_unit, SpaceUnits):
62
+ raise NgioValueError(f"space_unit can not be {type(space_unit)}.")
63
+
64
+ if time_unit is None:
65
+ time_unit = TimeUnits.seconds
66
+ elif isinstance(time_unit, str):
67
+ time_unit = TimeUnits(time_unit)
68
+ elif not isinstance(time_unit, TimeUnits):
69
+ raise NgioValueError(f"time_units can not be {type(time_unit)}.")
70
+
71
+ pixel_sizes = PixelSize(
72
+ x=pixelsize,
73
+ y=pixelsize,
74
+ z=z_spacing,
75
+ t=time_spacing,
76
+ space_unit=space_unit,
77
+ time_unit=time_unit,
78
+ )
79
+
80
+ meta = meta_type.default_init(
81
+ name=name,
82
+ levels=levels,
83
+ axes_names=axes_names,
84
+ pixel_size=pixel_sizes,
85
+ scaling_factors=scaling_factors,
86
+ version=version,
87
+ )
88
+ return meta, scaling_factors
89
+
90
+
91
+ def _create_empty_label(
92
+ store: StoreOrGroup,
93
+ shape: Collection[int],
94
+ pixelsize: float,
95
+ z_spacing: float = 1.0,
96
+ time_spacing: float = 1.0,
97
+ levels: int | list[str] = 5,
98
+ yx_scaling_factor: float | tuple[float, float] = 2.0,
99
+ z_scaling_factor: float = 1.0,
100
+ space_unit: SpaceUnits | str | None = None,
101
+ time_unit: TimeUnits | str | None = None,
102
+ axes_names: Collection[str] | None = None,
103
+ name: str | None = None,
104
+ chunks: Collection[int] | None = None,
105
+ dtype: str = "uint16",
106
+ overwrite: bool = False,
107
+ version: str = "0.4",
108
+ ) -> ZarrGroupHandler:
109
+ """Create an empty label with the given shape and metadata.
110
+
111
+ Args:
112
+ store (StoreOrGroup): The Zarr store or group to create the image in.
113
+ shape (Collection[int]): The shape of the image.
114
+ pixelsize (float): The pixel size in x and y dimensions.
115
+ z_spacing (float, optional): The spacing between z slices. Defaults to 1.0.
116
+ time_spacing (float, optional): The spacing between time points.
117
+ Defaults to 1.0.
118
+ levels (int | list[str], optional): The number of levels in the pyramid or a
119
+ list of level names. Defaults to 5.
120
+ yx_scaling_factor (float, optional): The down-scaling factor in x and y
121
+ dimensions. Defaults to 2.0.
122
+ z_scaling_factor (float, optional): The down-scaling factor in z dimension.
123
+ Defaults to 1.0.
124
+ space_unit (SpaceUnits | str | None, optional): The unit of space. Defaults to
125
+ None.
126
+ time_unit (TimeUnits | str | None, optional): The unit of time. Defaults to
127
+ None.
128
+ axes_names (Collection[str] | None, optional): The names of the axes.
129
+ If None the canonical names are used. Defaults to None.
130
+ name (str | None, optional): The name of the image. Defaults to None.
131
+ chunks (Collection[int] | None, optional): The chunk shape. If None the shape
132
+ is used. Defaults to None.
133
+ dtype (str, optional): The data type of the image. Defaults to "uint16".
134
+ overwrite (bool, optional): Whether to overwrite an existing image.
135
+ Defaults to True.
136
+ version (str, optional): The version of the OME-Zarr specification.
137
+ Defaults to "0.4".
138
+
139
+ """
140
+ if axes_names is None:
141
+ axes_names = canonical_label_axes_order()[-len(shape) :]
142
+
143
+ if len(axes_names) != len(shape):
144
+ raise NgioValueError(
145
+ f"Number of axes names {axes_names} does not match the number of "
146
+ f"dimensions {shape}."
147
+ )
148
+
149
+ meta, scaling_factors = _init_generic_meta(
150
+ meta_type=NgioLabelMeta,
151
+ pixelsize=pixelsize,
152
+ z_spacing=z_spacing,
153
+ time_spacing=time_spacing,
154
+ levels=levels,
155
+ yx_scaling_factor=yx_scaling_factor,
156
+ z_scaling_factor=z_scaling_factor,
157
+ space_unit=space_unit,
158
+ time_unit=time_unit,
159
+ axes_names=axes_names,
160
+ name=name,
161
+ version=version,
162
+ )
163
+
164
+ mode = "w" if overwrite else "w-"
165
+ group_handler = ZarrGroupHandler(store=store, mode=mode, cache=False)
166
+ image_handler = get_label_meta_handler(version=version, group_handler=group_handler)
167
+ image_handler.write_meta(meta)
168
+
169
+ init_empty_pyramid(
170
+ store=store,
171
+ paths=meta.paths,
172
+ scaling_factors=scaling_factors,
173
+ ref_shape=shape,
174
+ chunks=chunks,
175
+ dtype=dtype,
176
+ mode="a",
177
+ )
178
+ group_handler._mode = "r+"
179
+ return group_handler
180
+
181
+
182
+ def _create_empty_image(
183
+ store: StoreOrGroup,
184
+ shape: Collection[int],
185
+ pixelsize: float,
186
+ z_spacing: float = 1.0,
187
+ time_spacing: float = 1.0,
188
+ levels: int | list[str] = 5,
189
+ yx_scaling_factor: float | tuple[float, float] = 2,
190
+ z_scaling_factor: float = 1.0,
191
+ space_unit: SpaceUnits | str | None = None,
192
+ time_unit: TimeUnits | str | None = None,
193
+ axes_names: Collection[str] | None = None,
194
+ name: str | None = None,
195
+ chunks: Collection[int] | None = None,
196
+ dtype: str = "uint16",
197
+ overwrite: bool = False,
198
+ version: str = "0.4",
199
+ ) -> ZarrGroupHandler:
200
+ """Create an empty OME-Zarr image with the given shape and metadata.
201
+
202
+ Args:
203
+ store (StoreOrGroup): The Zarr store or group to create the image in.
204
+ shape (Collection[int]): The shape of the image.
205
+ pixelsize (float): The pixel size in x and y dimensions.
206
+ z_spacing (float, optional): The spacing between z slices. Defaults to 1.0.
207
+ time_spacing (float, optional): The spacing between time points.
208
+ Defaults to 1.0.
209
+ levels (int | list[str], optional): The number of levels in the pyramid or a
210
+ list of level names. Defaults to 5.
211
+ yx_scaling_factor (float, optional): The down-scaling factor in x and y
212
+ dimensions. Defaults to 2.0.
213
+ z_scaling_factor (float, optional): The down-scaling factor in z dimension.
214
+ Defaults to 1.0.
215
+ space_unit (SpaceUnits | str | None, optional): The unit of space. Defaults to
216
+ None.
217
+ time_unit (TimeUnits | str | None, optional): The unit of time. Defaults to
218
+ None.
219
+ axes_names (Collection[str] | None, optional): The names of the axes.
220
+ If None the canonical names are used. Defaults to None.
221
+ name (str | None, optional): The name of the image. Defaults to None.
222
+ chunks (Collection[int] | None, optional): The chunk shape. If None the shape
223
+ is used. Defaults to None.
224
+ dtype (str, optional): The data type of the image. Defaults to "uint16".
225
+ overwrite (bool, optional): Whether to overwrite an existing image.
226
+ Defaults to True.
227
+ version (str, optional): The version of the OME-Zarr specification.
228
+ Defaults to "0.4".
229
+
230
+ """
231
+ if axes_names is None:
232
+ axes_names = canonical_axes_order()[-len(shape) :]
233
+
234
+ if len(axes_names) != len(shape):
235
+ raise NgioValueError(
236
+ f"Number of axes names {axes_names} does not match the number of "
237
+ f"dimensions {shape}."
238
+ )
239
+
240
+ meta, scaling_factors = _init_generic_meta(
241
+ meta_type=NgioImageMeta,
242
+ pixelsize=pixelsize,
243
+ z_spacing=z_spacing,
244
+ time_spacing=time_spacing,
245
+ levels=levels,
246
+ yx_scaling_factor=yx_scaling_factor,
247
+ z_scaling_factor=z_scaling_factor,
248
+ space_unit=space_unit,
249
+ time_unit=time_unit,
250
+ axes_names=axes_names,
251
+ name=name,
252
+ version=version,
253
+ )
254
+ mode = "w" if overwrite else "w-"
255
+ group_handler = ZarrGroupHandler(store=store, mode=mode, cache=False)
256
+ image_handler = get_image_meta_handler(version=version, group_handler=group_handler)
257
+ image_handler.write_meta(meta)
258
+
259
+ init_empty_pyramid(
260
+ store=store,
261
+ paths=meta.paths,
262
+ scaling_factors=scaling_factors,
263
+ ref_shape=shape,
264
+ chunks=chunks,
265
+ dtype=dtype,
266
+ mode="a",
267
+ )
268
+
269
+ group_handler._mode = "r+"
270
+ return group_handler