ngio 0.3.5__py3-none-any.whl → 0.4.0a2__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 (61) hide show
  1. ngio/__init__.py +6 -0
  2. ngio/common/__init__.py +50 -48
  3. ngio/common/_array_io_pipes.py +554 -0
  4. ngio/common/_array_io_utils.py +508 -0
  5. ngio/common/_dimensions.py +63 -27
  6. ngio/common/_masking_roi.py +38 -10
  7. ngio/common/_pyramid.py +9 -7
  8. ngio/common/_roi.py +583 -72
  9. ngio/common/_synt_images_utils.py +101 -0
  10. ngio/common/_zoom.py +17 -12
  11. ngio/common/transforms/__init__.py +5 -0
  12. ngio/common/transforms/_label.py +12 -0
  13. ngio/common/transforms/_zoom.py +109 -0
  14. ngio/experimental/__init__.py +5 -0
  15. ngio/experimental/iterators/__init__.py +17 -0
  16. ngio/experimental/iterators/_abstract_iterator.py +170 -0
  17. ngio/experimental/iterators/_feature.py +151 -0
  18. ngio/experimental/iterators/_image_processing.py +169 -0
  19. ngio/experimental/iterators/_rois_utils.py +127 -0
  20. ngio/experimental/iterators/_segmentation.py +282 -0
  21. ngio/hcs/_plate.py +41 -36
  22. ngio/images/__init__.py +22 -1
  23. ngio/images/_abstract_image.py +247 -117
  24. ngio/images/_create.py +15 -15
  25. ngio/images/_create_synt_container.py +128 -0
  26. ngio/images/_image.py +425 -62
  27. ngio/images/_label.py +33 -30
  28. ngio/images/_masked_image.py +396 -122
  29. ngio/images/_ome_zarr_container.py +203 -66
  30. ngio/{common → images}/_table_ops.py +41 -41
  31. ngio/ome_zarr_meta/ngio_specs/__init__.py +2 -8
  32. ngio/ome_zarr_meta/ngio_specs/_axes.py +151 -128
  33. ngio/ome_zarr_meta/ngio_specs/_channels.py +55 -18
  34. ngio/ome_zarr_meta/ngio_specs/_dataset.py +7 -7
  35. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +3 -3
  36. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +11 -68
  37. ngio/ome_zarr_meta/v04/_v04_spec_utils.py +1 -1
  38. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/mask.png +0 -0
  39. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
  40. ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg +0 -0
  41. ngio/resources/__init__.py +54 -0
  42. ngio/resources/resource_model.py +35 -0
  43. ngio/tables/backends/_abstract_backend.py +5 -6
  44. ngio/tables/backends/_anndata.py +1 -1
  45. ngio/tables/backends/_anndata_utils.py +3 -3
  46. ngio/tables/backends/_non_zarr_backends.py +1 -1
  47. ngio/tables/backends/_table_backends.py +0 -1
  48. ngio/tables/backends/_utils.py +3 -3
  49. ngio/tables/v1/_roi_table.py +156 -69
  50. ngio/utils/__init__.py +2 -3
  51. ngio/utils/_logger.py +19 -0
  52. ngio/utils/_zarr_utils.py +1 -5
  53. {ngio-0.3.5.dist-info → ngio-0.4.0a2.dist-info}/METADATA +3 -1
  54. ngio-0.4.0a2.dist-info/RECORD +76 -0
  55. ngio/common/_array_pipe.py +0 -288
  56. ngio/common/_axes_transforms.py +0 -64
  57. ngio/common/_common_types.py +0 -5
  58. ngio/common/_slicer.py +0 -96
  59. ngio-0.3.5.dist-info/RECORD +0 -61
  60. {ngio-0.3.5.dist-info → ngio-0.4.0a2.dist-info}/WHEEL +0 -0
  61. {ngio-0.3.5.dist-info → ngio-0.4.0a2.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,10 @@
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 (
@@ -10,10 +12,17 @@ from ngio.common import (
10
12
  Dimensions,
11
13
  Roi,
12
14
  RoiPixels,
15
+ SlicingInputType,
16
+ TransformProtocol,
17
+ build_dask_getter,
18
+ build_dask_setter,
19
+ build_numpy_getter,
20
+ build_numpy_setter,
21
+ build_roi_dask_getter,
22
+ build_roi_dask_setter,
23
+ build_roi_numpy_getter,
24
+ build_roi_numpy_setter,
13
25
  consolidate_pyramid,
14
- get_pipe,
15
- roi_to_slice_kwargs,
16
- set_pipe,
17
26
  )
18
27
  from ngio.ome_zarr_meta import (
19
28
  AxesMapper,
@@ -165,80 +174,223 @@ class AbstractImage(Generic[_image_handler]):
165
174
  self.axes_mapper.get_index("x")
166
175
  return self.dimensions.has_axis(axis)
167
176
 
168
- def get_array(
177
+ def _get_as_numpy(
169
178
  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.
179
+ axes_order: Sequence[str] | None = None,
180
+ transforms: Sequence[TransformProtocol] | None = None,
181
+ **slicing_kwargs: SlicingInputType,
182
+ ) -> np.ndarray:
183
+ """Get the image as a numpy array.
175
184
 
176
185
  Args:
177
186
  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.
187
+ transforms: The transforms to apply to the array.
188
+ **slicing_kwargs: The slices to get the array.
180
189
 
181
190
  Returns:
182
191
  The array of the region of interest.
183
192
  """
184
- return get_pipe(
185
- array=self.zarr_array,
193
+ numpy_getter = build_numpy_getter(
194
+ zarr_array=self.zarr_array,
186
195
  dimensions=self.dimensions,
187
196
  axes_order=axes_order,
188
- mode=mode,
189
- **slice_kwargs,
197
+ transforms=transforms,
198
+ slicing_dict=slicing_kwargs,
190
199
  )
200
+ return numpy_getter()
201
+
202
+ def _get_roi_as_numpy(
203
+ self,
204
+ roi: Roi | RoiPixels,
205
+ axes_order: Sequence[str] | None = None,
206
+ transforms: Sequence[TransformProtocol] | None = None,
207
+ **slicing_kwargs: SlicingInputType,
208
+ ) -> np.ndarray:
209
+ """Get the image as a numpy array for a region of interest.
210
+
211
+ Args:
212
+ roi: The region of interest to get the array.
213
+ axes_order: The order of the axes to return the array.
214
+ transforms: The transforms to apply to the array.
215
+ **slicing_kwargs: The slices to get the array.
216
+
217
+ Returns:
218
+ The array of the region of interest.
219
+ """
220
+ numpy_roi_getter = build_roi_numpy_getter(
221
+ zarr_array=self.zarr_array,
222
+ dimensions=self.dimensions,
223
+ roi=roi,
224
+ pixel_size=self.pixel_size,
225
+ axes_order=axes_order,
226
+ transforms=transforms,
227
+ slicing_dict=slicing_kwargs,
228
+ )
229
+ return numpy_roi_getter()
230
+
231
+ def _get_as_dask(
232
+ self,
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 = build_dask_getter(
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 = build_roi_dask_getter(
269
+ zarr_array=self.zarr_array,
270
+ dimensions=self.dimensions,
271
+ roi=roi,
272
+ pixel_size=self.pixel_size,
273
+ axes_order=axes_order,
274
+ transforms=transforms,
275
+ slicing_dict=slicing_kwargs,
276
+ )
277
+ return roi_dask_getter()
278
+
279
+ def _get_array(
280
+ self,
281
+ axes_order: Sequence[str] | None = None,
282
+ transforms: Sequence[TransformProtocol] | None = None,
283
+ mode: Literal["numpy", "dask"] = "numpy",
284
+ **slicing_kwargs: SlicingInputType,
285
+ ) -> ArrayLike:
286
+ """Get a slice of the image.
287
+
288
+ Args:
289
+ axes_order: The order of the axes to return the array.
290
+ transforms: The transforms to apply to the array.
291
+ mode: The object type to return.
292
+ Can be "dask", "numpy".
293
+ **slicing_kwargs: The slices to get the array.
191
294
 
192
- def get_roi(
295
+ Returns:
296
+ The array of the region of interest.
297
+ """
298
+ if mode == "numpy":
299
+ return self._get_as_numpy(
300
+ axes_order=axes_order, transforms=transforms, **slicing_kwargs
301
+ )
302
+ elif mode == "dask":
303
+ return self._get_as_dask(
304
+ axes_order=axes_order, transforms=transforms, **slicing_kwargs
305
+ )
306
+ else:
307
+ raise ValueError(
308
+ f"Unknown mode: {mode}. Choose from 'numpy', 'dask', or 'delayed'."
309
+ )
310
+
311
+ def _get_roi(
193
312
  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],
313
+ roi: Roi | RoiPixels,
314
+ axes_order: Sequence[str] | None = None,
315
+ transforms: Sequence[TransformProtocol] | None = None,
316
+ mode: Literal["numpy", "dask"] = "numpy",
317
+ **slice_kwargs: SlicingInputType,
198
318
  ) -> ArrayLike:
199
319
  """Get a slice of the image.
200
320
 
201
321
  Args:
202
322
  roi: The region of interest to get the array.
203
323
  axes_order: The order of the axes to return the array.
324
+ transforms: The transforms to apply to the array.
204
325
  mode: The mode to return the array.
326
+ Can be "dask", "numpy".
205
327
  **slice_kwargs: The slices to get the array.
206
328
 
207
329
  Returns:
208
330
  The array of the region of interest.
209
331
  """
210
- return get_roi_pipe(
211
- image=self, roi=roi, axes_order=axes_order, mode=mode, **slice_kwargs
212
- )
213
-
214
- def set_array(
332
+ if mode == "numpy":
333
+ return self._get_roi_as_numpy(
334
+ roi=roi, axes_order=axes_order, transforms=transforms, **slice_kwargs
335
+ )
336
+ elif mode == "dask":
337
+ return self._get_roi_as_dask(
338
+ roi=roi, axes_order=axes_order, transforms=transforms, **slice_kwargs
339
+ )
340
+ else:
341
+ raise ValueError(
342
+ f"Unknown mode: {mode}. Choose from 'numpy', 'dask', or 'delayed'."
343
+ )
344
+
345
+ def _set_array(
215
346
  self,
216
347
  patch: ArrayLike,
217
- axes_order: Collection[str] | None = None,
218
- **slice_kwargs: slice | int | Iterable[int],
348
+ axes_order: Sequence[str] | None = None,
349
+ transforms: Sequence[TransformProtocol] | None = None,
350
+ **slicing_kwargs: SlicingInputType,
219
351
  ) -> None:
220
352
  """Set a slice of the image.
221
353
 
222
354
  Args:
223
355
  patch: The patch to set.
224
356
  axes_order: The order of the axes to set the patch.
225
- **slice_kwargs: The slices to set the patch.
357
+ transforms: The transforms to apply to the patch.
358
+ **slicing_kwargs: The slices to set the patch.
226
359
 
227
360
  """
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(
361
+ if isinstance(patch, np.ndarray):
362
+ numpy_setter = build_numpy_setter(
363
+ zarr_array=self.zarr_array,
364
+ dimensions=self.dimensions,
365
+ axes_order=axes_order,
366
+ transforms=transforms,
367
+ slicing_dict=slicing_kwargs,
368
+ )
369
+ numpy_setter(patch)
370
+
371
+ elif isinstance(patch, da.Array):
372
+ dask_setter = build_dask_setter(
373
+ zarr_array=self.zarr_array,
374
+ dimensions=self.dimensions,
375
+ axes_order=axes_order,
376
+ transforms=transforms,
377
+ slicing_dict=slicing_kwargs,
378
+ )
379
+ dask_setter(patch)
380
+ else:
381
+ raise TypeError(
382
+ f"Unsupported patch type: {type(patch)}. "
383
+ "Supported types are: "
384
+ "numpy.ndarray, dask.array.Array."
385
+ )
386
+
387
+ def _set_roi(
237
388
  self,
238
- roi: Roi,
389
+ roi: Roi | RoiPixels,
239
390
  patch: ArrayLike,
240
- axes_order: Collection[str] | None = None,
241
- **slice_kwargs: slice | int | Iterable[int],
391
+ axes_order: Sequence[str] | None = None,
392
+ transforms: Sequence[TransformProtocol] | None = None,
393
+ **slicing_kwargs: SlicingInputType,
242
394
  ) -> None:
243
395
  """Set a slice of the image.
244
396
 
@@ -246,12 +398,52 @@ class AbstractImage(Generic[_image_handler]):
246
398
  roi: The region of interest to set the patch.
247
399
  patch: The patch to set.
248
400
  axes_order: The order of the axes to set the patch.
249
- **slice_kwargs: The slices to set the patch.
401
+ transforms: The transforms to apply to the patch.
402
+ **slicing_kwargs: The slices to set the patch.
250
403
 
251
404
  """
252
- return set_roi_pipe(
253
- image=self, roi=roi, patch=patch, axes_order=axes_order, **slice_kwargs
254
- )
405
+ if isinstance(patch, np.ndarray):
406
+ roi_numpy_setter = build_roi_numpy_setter(
407
+ zarr_array=self.zarr_array,
408
+ dimensions=self.dimensions,
409
+ roi=roi,
410
+ pixel_size=self.pixel_size,
411
+ axes_order=axes_order,
412
+ transforms=transforms,
413
+ slicing_dict=slicing_kwargs,
414
+ )
415
+ roi_numpy_setter(patch)
416
+
417
+ elif isinstance(patch, da.Array):
418
+ roi_dask_setter = build_roi_dask_setter(
419
+ zarr_array=self.zarr_array,
420
+ dimensions=self.dimensions,
421
+ roi=roi,
422
+ pixel_size=self.pixel_size,
423
+ axes_order=axes_order,
424
+ transforms=transforms,
425
+ slicing_dict=slicing_kwargs,
426
+ )
427
+ roi_dask_setter(patch)
428
+ else:
429
+ raise TypeError(
430
+ f"Unsupported patch type: {type(patch)}. "
431
+ "Supported types are: "
432
+ "numpy.ndarray, dask.array.Array."
433
+ )
434
+
435
+ def _consolidate(
436
+ self,
437
+ order: Literal[0, 1, 2] = 1,
438
+ mode: Literal["dask", "numpy", "coarsen"] = "dask",
439
+ ) -> None:
440
+ """Consolidate the image on disk.
441
+
442
+ Args:
443
+ order: The order of the consolidation.
444
+ mode: The mode of the consolidation.
445
+ """
446
+ consolidate_image(image=self, order=order, mode=mode)
255
447
 
256
448
  def build_image_roi_table(self, name: str = "image") -> RoiTable:
257
449
  """Build the ROI table for an image."""
@@ -275,86 +467,24 @@ def consolidate_image(
275
467
  )
276
468
 
277
469
 
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
470
  def build_image_roi_table(image: AbstractImage, name: str = "image") -> RoiTable:
345
471
  """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
- )
472
+ dim_x = image.dimensions.get("x")
473
+ dim_y = image.dimensions.get("y")
474
+ assert dim_x is not None and dim_y is not None
475
+ dim_z = image.dimensions.get("z")
476
+ z = None if dim_z is None else 0
477
+ dim_t = image.dimensions.get("t")
478
+ t = None if dim_t is None else 0
351
479
  image_roi = RoiPixels(
352
480
  name=name,
353
481
  x=0,
354
482
  y=0,
355
- z=0,
483
+ z=z,
484
+ t=t,
356
485
  x_length=dim_x,
357
486
  y_length=dim_y,
358
487
  z_length=dim_z,
488
+ t_length=dim_t,
359
489
  )
360
490
  return RoiTable(rois=[image_roi.to_roi(pixel_size=image.pixel_size)])
ngio/images/_create.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Utility functions for working with OME-Zarr images."""
2
2
 
3
- from collections.abc import Collection
3
+ from collections.abc import Sequence
4
4
  from typing import TypeVar
5
5
 
6
6
  from ngio.common._pyramid import init_empty_pyramid
@@ -29,7 +29,7 @@ _image_or_label_meta = TypeVar("_image_or_label_meta", NgioImageMeta, NgioLabelM
29
29
  def _init_generic_meta(
30
30
  meta_type: type[_image_or_label_meta],
31
31
  pixelsize: float,
32
- axes_names: Collection[str],
32
+ axes_names: Sequence[str],
33
33
  z_spacing: float = 1.0,
34
34
  time_spacing: float = 1.0,
35
35
  levels: int | list[str] = 5,
@@ -80,7 +80,7 @@ def _init_generic_meta(
80
80
 
81
81
  def create_empty_label_container(
82
82
  store: StoreOrGroup,
83
- shape: Collection[int],
83
+ shape: Sequence[int],
84
84
  pixelsize: float,
85
85
  z_spacing: float = 1.0,
86
86
  time_spacing: float = 1.0,
@@ -89,10 +89,10 @@ def create_empty_label_container(
89
89
  z_scaling_factor: float = 1.0,
90
90
  space_unit: SpaceUnits | str | None = DefaultSpaceUnit,
91
91
  time_unit: TimeUnits | str | None = DefaultTimeUnit,
92
- axes_names: Collection[str] | None = None,
92
+ axes_names: Sequence[str] | None = None,
93
93
  name: str | None = None,
94
- chunks: Collection[int] | None = None,
95
- dtype: str = "uint16",
94
+ chunks: Sequence[int] | None = None,
95
+ dtype: str = "uint32",
96
96
  overwrite: bool = False,
97
97
  version: NgffVersions = DefaultNgffVersion,
98
98
  ) -> ZarrGroupHandler:
@@ -100,7 +100,7 @@ def create_empty_label_container(
100
100
 
101
101
  Args:
102
102
  store (StoreOrGroup): The Zarr store or group to create the image in.
103
- shape (Collection[int]): The shape of the image.
103
+ shape (Sequence[int]): The shape of the image.
104
104
  pixelsize (float): The pixel size in x and y dimensions.
105
105
  z_spacing (float, optional): The spacing between z slices. Defaults to 1.0.
106
106
  time_spacing (float, optional): The spacing between time points.
@@ -115,10 +115,10 @@ def create_empty_label_container(
115
115
  DefaultSpaceUnit.
116
116
  time_unit (TimeUnits, optional): The unit of time. Defaults to
117
117
  DefaultTimeUnit.
118
- axes_names (Collection[str] | None, optional): The names of the axes.
118
+ axes_names (Sequence[str] | None, optional): The names of the axes.
119
119
  If None the canonical names are used. Defaults to None.
120
120
  name (str | None, optional): The name of the image. Defaults to None.
121
- chunks (Collection[int] | None, optional): The chunk shape. If None the shape
121
+ chunks (Sequence[int] | None, optional): The chunk shape. If None the shape
122
122
  is used. Defaults to None.
123
123
  dtype (str, optional): The data type of the image. Defaults to "uint16".
124
124
  overwrite (bool, optional): Whether to overwrite an existing image.
@@ -171,7 +171,7 @@ def create_empty_label_container(
171
171
 
172
172
  def create_empty_image_container(
173
173
  store: StoreOrGroup,
174
- shape: Collection[int],
174
+ shape: Sequence[int],
175
175
  pixelsize: float,
176
176
  z_spacing: float = 1.0,
177
177
  time_spacing: float = 1.0,
@@ -180,9 +180,9 @@ def create_empty_image_container(
180
180
  z_scaling_factor: float = 1.0,
181
181
  space_unit: SpaceUnits | str | None = DefaultSpaceUnit,
182
182
  time_unit: TimeUnits | str | None = DefaultTimeUnit,
183
- axes_names: Collection[str] | None = None,
183
+ axes_names: Sequence[str] | None = None,
184
184
  name: str | None = None,
185
- chunks: Collection[int] | None = None,
185
+ chunks: Sequence[int] | None = None,
186
186
  dtype: str = "uint16",
187
187
  overwrite: bool = False,
188
188
  version: NgffVersions = DefaultNgffVersion,
@@ -191,7 +191,7 @@ def create_empty_image_container(
191
191
 
192
192
  Args:
193
193
  store (StoreOrGroup): The Zarr store or group to create the image in.
194
- shape (Collection[int]): The shape of the image.
194
+ shape (Sequence[int]): The shape of the image.
195
195
  pixelsize (float): The pixel size in x and y dimensions.
196
196
  z_spacing (float, optional): The spacing between z slices. Defaults to 1.0.
197
197
  time_spacing (float, optional): The spacing between time points.
@@ -206,10 +206,10 @@ def create_empty_image_container(
206
206
  DefaultSpaceUnit.
207
207
  time_unit (TimeUnits, optional): The unit of time. Defaults to
208
208
  DefaultTimeUnit.
209
- axes_names (Collection[str] | None, optional): The names of the axes.
209
+ axes_names (Sequence[str] | None, optional): The names of the axes.
210
210
  If None the canonical names are used. Defaults to None.
211
211
  name (str | None, optional): The name of the image. Defaults to None.
212
- chunks (Collection[int] | None, optional): The chunk shape. If None the shape
212
+ chunks (Sequence[int] | None, optional): The chunk shape. If None the shape
213
213
  is used. Defaults to None.
214
214
  dtype (str, optional): The data type of the image. Defaults to "uint16".
215
215
  overwrite (bool, optional): Whether to overwrite an existing image.
@@ -0,0 +1,128 @@
1
+ """Abstract class for handling OME-NGFF images."""
2
+
3
+ from collections.abc import Sequence
4
+
5
+ import numpy as np
6
+ import PIL.Image
7
+
8
+ from ngio.common._synt_images_utils import fit_to_shape
9
+ from ngio.images._ome_zarr_container import OmeZarrContainer, create_ome_zarr_from_array
10
+ from ngio.ome_zarr_meta.ngio_specs import (
11
+ DefaultNgffVersion,
12
+ NgffVersions,
13
+ )
14
+ from ngio.resources import AVAILABLE_SAMPLES, SampleInfo, get_sample_info
15
+ from ngio.tables import (
16
+ DefaultTableBackend,
17
+ TableBackend,
18
+ )
19
+ from ngio.utils import (
20
+ StoreOrGroup,
21
+ )
22
+
23
+
24
+ def create_synthetic_ome_zarr(
25
+ store: StoreOrGroup,
26
+ shape: Sequence[int],
27
+ reference_sample: AVAILABLE_SAMPLES | SampleInfo = "Cardiomyocyte",
28
+ levels: int | list[str] = 5,
29
+ xy_scaling_factor: float = 2,
30
+ z_scaling_factor: float = 1.0,
31
+ axes_names: Sequence[str] | None = None,
32
+ chunks: Sequence[int] | None = None,
33
+ channel_labels: list[str] | None = None,
34
+ channel_wavelengths: list[str] | None = None,
35
+ channel_colors: Sequence[str] | None = None,
36
+ channel_active: Sequence[bool] | None = None,
37
+ table_backend: TableBackend = DefaultTableBackend,
38
+ overwrite: bool = False,
39
+ version: NgffVersions = DefaultNgffVersion,
40
+ ) -> OmeZarrContainer:
41
+ """Create an empty OME-Zarr image with the given shape and metadata.
42
+
43
+ Args:
44
+ store (StoreOrGroup): The Zarr store or group to create the image in.
45
+ shape (Sequence[int]): The shape of the image.
46
+ reference_sample (AVAILABLE_SAMPLES | SampleInfo): The reference sample to use.
47
+ levels (int | list[str], optional): The number of levels in the pyramid or a
48
+ list of level names. Defaults to 5.
49
+ xy_scaling_factor (float, optional): The down-scaling factor in x and y
50
+ dimensions. Defaults to 2.0.
51
+ z_scaling_factor (float, optional): The down-scaling factor in z dimension.
52
+ Defaults to 1.0.
53
+ axes_names (Sequence[str] | None, optional): The names of the axes.
54
+ If None the canonical names are used. Defaults to None.
55
+ chunks (Sequence[int] | None, optional): The chunk shape. If None the shape
56
+ is used. Defaults to None.
57
+ channel_labels (list[str] | None, optional): The labels of the channels.
58
+ Defaults to None.
59
+ channel_wavelengths (list[str] | None, optional): The wavelengths of the
60
+ channels. Defaults to None.
61
+ channel_colors (Sequence[str] | None, optional): The colors of the channels.
62
+ Defaults to None.
63
+ channel_active (Sequence[bool] | None, optional): Whether the channels are
64
+ active. Defaults to None.
65
+ table_backend (TableBackend): Table backend to be used to store tables
66
+ overwrite (bool, optional): Whether to overwrite an existing image.
67
+ Defaults to True.
68
+ version (NgffVersion, optional): The version of the OME-Zarr specification.
69
+ Defaults to DefaultNgffVersion.
70
+ """
71
+ if isinstance(reference_sample, str):
72
+ sample_info = get_sample_info(reference_sample)
73
+ else:
74
+ sample_info = reference_sample
75
+
76
+ raw = np.asarray(PIL.Image.open(sample_info.img_path))
77
+ raw = fit_to_shape(arr=raw, out_shape=tuple(shape))
78
+ raw = raw / np.max(raw) * (2**16 - 1)
79
+ raw = raw.astype(np.uint16)
80
+ ome_zarr = create_ome_zarr_from_array(
81
+ store=store,
82
+ array=raw,
83
+ xy_pixelsize=sample_info.xy_pixelsize,
84
+ z_spacing=sample_info.z_spacing,
85
+ time_spacing=sample_info.time_spacing,
86
+ levels=levels,
87
+ xy_scaling_factor=xy_scaling_factor,
88
+ z_scaling_factor=z_scaling_factor,
89
+ space_unit=sample_info.space_unit,
90
+ time_unit=sample_info.time_unit,
91
+ axes_names=axes_names,
92
+ channel_labels=channel_labels,
93
+ channel_wavelengths=channel_wavelengths,
94
+ channel_colors=channel_colors,
95
+ channel_active=channel_active,
96
+ name=sample_info.name,
97
+ chunks=chunks,
98
+ overwrite=overwrite,
99
+ version=version,
100
+ )
101
+
102
+ image = ome_zarr.get_image()
103
+ well_table = image.build_image_roi_table()
104
+ ome_zarr.add_table("well_ROI_table", table=well_table, backend=table_backend)
105
+
106
+ for label_info in sample_info.labels:
107
+ ome_zarr.derive_label(name=label_info.name)
108
+ label = ome_zarr.get_label(name=label_info.name)
109
+
110
+ ref_label = np.asarray(PIL.Image.open(label_info.label_path))
111
+ ref_label = fit_to_shape(
112
+ arr=ref_label,
113
+ out_shape=label.shape,
114
+ ensure_unique_info=label_info.ensure_unique_labels,
115
+ )
116
+ ref_label = ref_label.astype(np.uint32)
117
+ label.set_array(ref_label)
118
+ label.consolidate()
119
+
120
+ if label_info.create_masking_table:
121
+ masking_table = label.build_masking_roi_table()
122
+ ome_zarr.add_table(
123
+ name=f"{label_info.name}_masking_table",
124
+ table=masking_table,
125
+ backend=table_backend,
126
+ )
127
+
128
+ return ome_zarr