ngio 0.2.0a3__py3-none-any.whl → 0.2.0b2__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 (49) hide show
  1. ngio/__init__.py +4 -4
  2. ngio/common/__init__.py +12 -2
  3. ngio/common/_array_pipe.py +106 -0
  4. ngio/common/_axes_transforms.py +3 -2
  5. ngio/common/_dimensions.py +7 -0
  6. ngio/common/_masking_roi.py +158 -0
  7. ngio/common/_pyramid.py +16 -11
  8. ngio/common/_roi.py +74 -0
  9. ngio/common/_slicer.py +1 -2
  10. ngio/common/_zoom.py +5 -3
  11. ngio/hcs/__init__.py +2 -57
  12. ngio/hcs/plate.py +399 -0
  13. ngio/images/abstract_image.py +97 -28
  14. ngio/images/create.py +48 -29
  15. ngio/images/image.py +99 -46
  16. ngio/images/label.py +109 -92
  17. ngio/images/masked_image.py +259 -0
  18. ngio/images/omezarr_container.py +201 -64
  19. ngio/ome_zarr_meta/__init__.py +25 -13
  20. ngio/ome_zarr_meta/_meta_handlers.py +718 -69
  21. ngio/ome_zarr_meta/ngio_specs/__init__.py +8 -0
  22. ngio/ome_zarr_meta/ngio_specs/_channels.py +11 -0
  23. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +374 -2
  24. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +169 -119
  25. ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +35 -3
  26. ngio/ome_zarr_meta/v04/__init__.py +17 -5
  27. ngio/ome_zarr_meta/v04/_v04_spec_utils.py +85 -12
  28. ngio/tables/__init__.py +2 -0
  29. ngio/tables/_validators.py +2 -4
  30. ngio/tables/backends/_anndata_utils.py +5 -1
  31. ngio/tables/backends/_anndata_v1.py +2 -1
  32. ngio/tables/backends/_json_v1.py +1 -1
  33. ngio/tables/tables_container.py +12 -2
  34. ngio/tables/v1/__init__.py +1 -2
  35. ngio/tables/v1/_feature_table.py +7 -5
  36. ngio/tables/v1/_generic_table.py +65 -11
  37. ngio/tables/v1/_roi_table.py +145 -27
  38. ngio/utils/_datasets.py +4 -2
  39. ngio/utils/_fractal_fsspec_store.py +3 -2
  40. ngio/utils/_logger.py +3 -1
  41. ngio/utils/_zarr_utils.py +25 -2
  42. {ngio-0.2.0a3.dist-info → ngio-0.2.0b2.dist-info}/METADATA +6 -2
  43. ngio-0.2.0b2.dist-info/RECORD +54 -0
  44. ngio/ome_zarr_meta/_generic_handlers.py +0 -320
  45. ngio/ome_zarr_meta/v04/_meta_handlers.py +0 -54
  46. ngio/tables/v1/_masking_roi_table.py +0 -175
  47. ngio-0.2.0a3.dist-info/RECORD +0 -54
  48. {ngio-0.2.0a3.dist-info → ngio-0.2.0b2.dist-info}/WHEEL +0 -0
  49. {ngio-0.2.0a3.dist-info → ngio-0.2.0b2.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,5 @@
1
1
  """Abstract class for handling OME-NGFF images."""
2
2
 
3
- # %%
4
3
  from collections.abc import Collection
5
4
  from typing import Literal, overload
6
5
 
@@ -9,6 +8,7 @@ import numpy as np
9
8
  from ngio.images.create import _create_empty_image
10
9
  from ngio.images.image import Image, ImagesContainer
11
10
  from ngio.images.label import Label, LabelsContainer
11
+ from ngio.images.masked_image import MaskedImage, MaskedLabel
12
12
  from ngio.ome_zarr_meta import (
13
13
  NgioImageMeta,
14
14
  PixelSize,
@@ -19,6 +19,7 @@ from ngio.ome_zarr_meta.ngio_specs import (
19
19
  )
20
20
  from ngio.tables import (
21
21
  FeatureTable,
22
+ GenericRoiTable,
22
23
  MaskingROITable,
23
24
  RoiTable,
24
25
  Table,
@@ -57,23 +58,16 @@ class OmeZarrContainer:
57
58
 
58
59
  def __init__(
59
60
  self,
60
- store: StoreOrGroup,
61
- cache: bool = False,
62
- mode: AccessModeLiteral = "r+",
61
+ group_handler: ZarrGroupHandler,
63
62
  table_container: TablesContainer | None = None,
64
63
  label_container: LabelsContainer | None = None,
65
64
  validate_arrays: bool = True,
66
65
  ) -> None:
67
66
  """Initialize the OmeZarrContainer."""
68
- self._group_handler = ZarrGroupHandler(store, cache, mode)
67
+ self._group_handler = group_handler
69
68
  self._images_container = ImagesContainer(self._group_handler)
70
69
 
71
- if label_container is None:
72
- label_container = _default_label_container(self._group_handler)
73
70
  self._labels_container = label_container
74
-
75
- if table_container is None:
76
- table_container = _default_table_container(self._group_handler)
77
71
  self._tables_container = table_container
78
72
 
79
73
  def __repr__(self) -> str:
@@ -102,14 +96,18 @@ class OmeZarrContainer:
102
96
  def labels_container(self) -> LabelsContainer:
103
97
  """Return the labels container."""
104
98
  if self._labels_container is None:
105
- raise NgioValidationError("No labels found in the image.")
99
+ self._labels_container = _default_label_container(self._group_handler)
100
+ if self._labels_container is None:
101
+ raise NgioValidationError("No labels found in the image.")
106
102
  return self._labels_container
107
103
 
108
104
  @property
109
105
  def tables_container(self) -> TablesContainer:
110
106
  """Return the tables container."""
111
107
  if self._tables_container is None:
112
- raise NgioValidationError("No tables found in the image.")
108
+ self._tables_container = _default_table_container(self._group_handler)
109
+ if self._tables_container is None:
110
+ raise NgioValidationError("No tables found in the image.")
113
111
  return self._tables_container
114
112
 
115
113
  @property
@@ -176,47 +174,117 @@ class OmeZarrContainer:
176
174
  path=path, pixel_size=pixel_size, strict=strict
177
175
  )
178
176
 
177
+ def get_masked_image(
178
+ self,
179
+ masking_label_name: str,
180
+ masking_table_name: str | None = None,
181
+ path: str | None = None,
182
+ pixel_size: PixelSize | None = None,
183
+ strict: bool = False,
184
+ ) -> MaskedImage:
185
+ """Get a masked image at a specific level.
186
+
187
+ Args:
188
+ masking_label_name (str): The name of the label.
189
+ masking_table_name (str | None): The name of the masking table.
190
+ path (str | None): The path to the image in the omezarr file.
191
+ pixel_size (PixelSize | None): The pixel size of the image.
192
+ strict (bool): Only used if the pixel size is provided. If True, the
193
+ pixel size must match the image pixel size exactly. If False, the
194
+ closest pixel size level will be returned.
195
+ """
196
+ image = self.get_image(path=path, pixel_size=pixel_size, strict=strict)
197
+ masking_label = self.get_label(
198
+ name=masking_label_name, path=path, pixel_size=pixel_size, strict=strict
199
+ )
200
+ if masking_table_name is None:
201
+ masking_table = masking_label.build_masking_roi_table()
202
+ else:
203
+ masking_table = self.get_table(
204
+ masking_table_name, check_type="masking_roi_table"
205
+ )
206
+
207
+ return MaskedImage(
208
+ group_handler=image._group_handler,
209
+ path=masking_label.path,
210
+ meta_handler=image.meta_handler,
211
+ label=masking_label,
212
+ masking_roi_table=masking_table,
213
+ )
214
+
179
215
  def derive_image(
180
216
  self,
181
217
  store: StoreOrGroup,
182
218
  ref_path: str | None = None,
183
219
  shape: Collection[int] | None = None,
220
+ labels: Collection[str] | None = None,
221
+ pixel_size: PixelSize | None = None,
222
+ axes_names: Collection[str] | None = None,
184
223
  chunks: Collection[int] | None = None,
185
- xy_scaling_factor: float = 2.0,
186
- z_scaling_factor: float = 1.0,
187
- copy_tables: bool = False,
224
+ dtype: str | None = None,
188
225
  copy_labels: bool = False,
226
+ copy_tables: bool = False,
189
227
  overwrite: bool = False,
190
228
  ) -> "OmeZarrContainer":
191
- """Derive a new image from the current image."""
192
- if copy_labels:
193
- raise NotImplementedError("Copying labels is not yet implemented.")
229
+ """Create an empty OME-Zarr container from an existing image.
194
230
 
195
- if copy_tables:
196
- raise NotImplementedError("Copying tables is not yet implemented.")
231
+ Args:
232
+ store (StoreOrGroup): The Zarr store or group to create the image in.
233
+ ref_path (str | None): The path to the reference image in
234
+ the image container.
235
+ shape (Collection[int] | None): The shape of the new image.
236
+ labels (Collection[str] | None): The labels of the new image.
237
+ pixel_size (PixelSize | None): The pixel size of the new image.
238
+ axes_names (Collection[str] | None): The axes names of the new image.
239
+ chunks (Collection[int] | None): The chunk shape of the new image.
240
+ dtype (str | None): The data type of the new image.
241
+ copy_labels (bool): Whether to copy the labels from the reference image.
242
+ copy_tables (bool): Whether to copy the tables from the reference image.
243
+ overwrite (bool): Whether to overwrite an existing image.
244
+
245
+ Returns:
246
+ OmeZarrContainer: The new image container.
197
247
 
248
+ """
198
249
  _ = self._images_container.derive(
199
250
  store=store,
200
251
  ref_path=ref_path,
201
252
  shape=shape,
253
+ labels=labels,
254
+ pixel_size=pixel_size,
255
+ axes_names=axes_names,
202
256
  chunks=chunks,
203
- xy_scaling_factor=xy_scaling_factor,
204
- z_scaling_factor=z_scaling_factor,
257
+ dtype=dtype,
205
258
  overwrite=overwrite,
206
259
  )
207
- return OmeZarrContainer(
208
- store=store,
209
- cache=False,
210
- mode="r+",
211
- table_container=None,
212
- label_container=None,
260
+
261
+ handler = ZarrGroupHandler(
262
+ store, cache=self._group_handler.use_cache, mode=self._group_handler.mode
213
263
  )
214
264
 
265
+ new_omezarr = OmeZarrContainer(
266
+ group_handler=handler,
267
+ validate_arrays=False,
268
+ )
269
+
270
+ if copy_labels:
271
+ self.labels_container._group_handler.copy_handler(
272
+ new_omezarr.labels_container._group_handler
273
+ )
274
+
275
+ if copy_tables:
276
+ self.tables_container._group_handler.copy_handler(
277
+ new_omezarr.tables_container._group_handler
278
+ )
279
+ return new_omezarr
280
+
215
281
  def list_tables(self) -> list[str]:
216
282
  """List all tables in the image."""
217
- if self._tables_container is None:
218
- return []
219
- return self._tables_container.list()
283
+ return self.tables_container.list()
284
+
285
+ def list_roi_tables(self) -> list[str]:
286
+ """List all ROI tables in the image."""
287
+ return self.tables_container.list_roi_tables()
220
288
 
221
289
  @overload
222
290
  def get_table(self, name: str, check_type: None) -> Table: ...
@@ -234,12 +302,14 @@ class OmeZarrContainer:
234
302
  self, name: str, check_type: Literal["feature_table"]
235
303
  ) -> FeatureTable: ...
236
304
 
305
+ @overload
306
+ def get_table(
307
+ self, name: str, check_type: Literal["generic_roi_table"]
308
+ ) -> GenericRoiTable: ...
309
+
237
310
  def get_table(self, name: str, check_type: TypedTable | None = None) -> Table:
238
311
  """Get a table from the image."""
239
- if self._tables_container is None:
240
- raise NgioValidationError("No tables found in the image.")
241
-
242
- table = self._tables_container.get(name)
312
+ table = self.tables_container.get(name)
243
313
  match check_type:
244
314
  case "roi_table":
245
315
  if not isinstance(table, RoiTable):
@@ -254,6 +324,15 @@ class OmeZarrContainer:
254
324
  f"Found type: {table.type()}"
255
325
  )
256
326
  return table
327
+
328
+ case "generic_roi_table":
329
+ if not isinstance(table, GenericRoiTable):
330
+ raise NgioValueError(
331
+ f"Table '{name}' is not a generic ROI table. "
332
+ f"Found type: {table.type()}"
333
+ )
334
+ return table
335
+
257
336
  case "feature_table":
258
337
  if not isinstance(table, FeatureTable):
259
338
  raise NgioValueError(
@@ -266,6 +345,14 @@ class OmeZarrContainer:
266
345
  case _:
267
346
  raise NgioValueError(f"Unknown check_type: {check_type}")
268
347
 
348
+ def build_image_roi_table(self, name: str = "image") -> RoiTable:
349
+ """Compute the ROI table for an image."""
350
+ return self.get_image().build_image_roi_table(name=name)
351
+
352
+ def build_masking_roi_table(self, label: str) -> MaskingROITable:
353
+ """Compute the masking ROI table for a label."""
354
+ return self.get_label(label).build_masking_roi_table()
355
+
269
356
  def add_table(
270
357
  self,
271
358
  name: str,
@@ -274,17 +361,13 @@ class OmeZarrContainer:
274
361
  overwrite: bool = False,
275
362
  ) -> None:
276
363
  """Add a table to the image."""
277
- if self._tables_container is None:
278
- raise NgioValidationError("No tables found in the image.")
279
- self._tables_container.add(
364
+ self.tables_container.add(
280
365
  name=name, table=table, backend=backend, overwrite=overwrite
281
366
  )
282
367
 
283
368
  def list_labels(self) -> list[str]:
284
369
  """List all labels in the image."""
285
- if self._labels_container is None:
286
- return []
287
- return self._labels_container.list()
370
+ return self.labels_container.list()
288
371
 
289
372
  def get_label(
290
373
  self,
@@ -303,40 +386,95 @@ class OmeZarrContainer:
303
386
  pixel size must match the image pixel size exactly. If False, the
304
387
  closest pixel size level will be returned.
305
388
  """
306
- if self._labels_container is None:
307
- raise NgioValidationError("No labels found in the image.")
308
- return self._labels_container.get(
389
+ return self.labels_container.get(
309
390
  name=name, path=path, pixel_size=pixel_size, strict=strict
310
391
  )
311
392
 
393
+ def get_masked_label(
394
+ self,
395
+ label_name: str,
396
+ masking_label_name: str,
397
+ masking_table_name: str | None = None,
398
+ path: str | None = None,
399
+ pixel_size: PixelSize | None = None,
400
+ strict: bool = False,
401
+ ) -> MaskedLabel:
402
+ """Get a masked image at a specific level.
403
+
404
+ Args:
405
+ label_name (str): The name of the label.
406
+ masking_label_name (str): The name of the masking label.
407
+ masking_table_name (str | None): The name of the masking table.
408
+ path (str | None): The path to the image in the omezarr file.
409
+ pixel_size (PixelSize | None): The pixel size of the image.
410
+ strict (bool): Only used if the pixel size is provided. If True, the
411
+ pixel size must match the image pixel size exactly. If False, the
412
+ closest pixel size level will be returned.
413
+ """
414
+ label = self.get_label(
415
+ name=label_name, path=path, pixel_size=pixel_size, strict=strict
416
+ )
417
+ masking_label = self.get_label(
418
+ name=masking_label_name, path=path, pixel_size=pixel_size, strict=strict
419
+ )
420
+ if masking_table_name is None:
421
+ masking_table = masking_label.build_masking_roi_table()
422
+ else:
423
+ masking_table = self.get_table(
424
+ masking_table_name, check_type="masking_roi_table"
425
+ )
426
+
427
+ return MaskedLabel(
428
+ group_handler=label._group_handler,
429
+ path=label.path,
430
+ meta_handler=label.meta_handler,
431
+ label=masking_label,
432
+ masking_roi_table=masking_table,
433
+ )
434
+
312
435
  def derive_label(
313
436
  self,
314
437
  name: str,
315
438
  ref_image: Image | None = None,
316
439
  shape: Collection[int] | None = None,
440
+ pixel_size: PixelSize | None = None,
441
+ axes_names: Collection[str] | None = None,
317
442
  chunks: Collection[int] | None = None,
318
- dtype: str = "uint16",
319
- xy_scaling_factor=2.0,
320
- z_scaling_factor=1.0,
443
+ dtype: str | None = None,
321
444
  overwrite: bool = False,
322
- ) -> Label:
323
- """Derive a label from an image."""
324
- if self._labels_container is None:
325
- raise NgioValidationError("No labels found in the image.")
445
+ ) -> "Label":
446
+ """Create an empty OME-Zarr label from a reference image.
447
+
448
+ And add the label to the /labels group.
326
449
 
450
+ Args:
451
+ store (StoreOrGroup): The Zarr store or group to create the image in.
452
+ ref_image (Image): The reference image.
453
+ name (str): The name of the new image.
454
+ shape (Collection[int] | None): The shape of the new image.
455
+ pixel_size (PixelSize | None): The pixel size of the new image.
456
+ axes_names (Collection[str] | None): The axes names of the new image.
457
+ For labels, the channel axis is not allowed.
458
+ chunks (Collection[int] | None): The chunk shape of the new image.
459
+ dtype (str | None): The data type of the new image.
460
+ overwrite (bool): Whether to overwrite an existing image.
461
+
462
+ Returns:
463
+ Label: The new label.
464
+
465
+ """
327
466
  if ref_image is None:
328
467
  ref_image = self.get_image()
329
- self._labels_container.derive(
468
+ return self.labels_container.derive(
330
469
  name=name,
331
470
  ref_image=ref_image,
332
471
  shape=shape,
472
+ pixel_size=pixel_size,
473
+ axes_names=axes_names,
333
474
  chunks=chunks,
334
475
  dtype=dtype,
335
- xy_scaling_factor=xy_scaling_factor,
336
- z_scaling_factor=z_scaling_factor,
337
476
  overwrite=overwrite,
338
477
  )
339
- return self.get_label(name, path="0")
340
478
 
341
479
 
342
480
  def open_omezarr_container(
@@ -346,10 +484,9 @@ def open_omezarr_container(
346
484
  validate_arrays: bool = True,
347
485
  ) -> OmeZarrContainer:
348
486
  """Open an OME-Zarr image."""
487
+ handler = ZarrGroupHandler(store=store, cache=cache, mode=mode)
349
488
  return OmeZarrContainer(
350
- store=store,
351
- cache=cache,
352
- mode=mode,
489
+ group_handler=handler,
353
490
  validate_arrays=validate_arrays,
354
491
  )
355
492
 
@@ -450,11 +587,11 @@ def create_empty_omezarr(
450
587
  handler = _create_empty_image(
451
588
  store=store,
452
589
  shape=shape,
453
- xy_pixelsize=xy_pixelsize,
590
+ pixelsize=xy_pixelsize,
454
591
  z_spacing=z_spacing,
455
592
  time_spacing=time_spacing,
456
593
  levels=levels,
457
- xy_scaling_factor=xy_scaling_factor,
594
+ yx_scaling_factor=xy_scaling_factor,
458
595
  z_scaling_factor=z_scaling_factor,
459
596
  space_unit=space_unit,
460
597
  time_unit=time_unit,
@@ -466,7 +603,7 @@ def create_empty_omezarr(
466
603
  version=version,
467
604
  )
468
605
 
469
- omezarr = OmeZarrContainer(store=handler.store, mode="r+")
606
+ omezarr = OmeZarrContainer(group_handler=handler)
470
607
  omezarr.initialize_channel_meta(
471
608
  labels=channel_labels,
472
609
  wavelength_id=channel_wavelengths,
@@ -541,11 +678,11 @@ def create_omezarr_from_array(
541
678
  handler = _create_empty_image(
542
679
  store=store,
543
680
  shape=array.shape,
544
- xy_pixelsize=xy_pixelsize,
681
+ pixelsize=xy_pixelsize,
545
682
  z_spacing=z_spacing,
546
683
  time_spacing=time_spacing,
547
684
  levels=levels,
548
- xy_scaling_factor=xy_scaling_factor,
685
+ yx_scaling_factor=xy_scaling_factor,
549
686
  z_scaling_factor=z_scaling_factor,
550
687
  space_unit=space_unit,
551
688
  time_unit=time_unit,
@@ -557,7 +694,7 @@ def create_omezarr_from_array(
557
694
  version=version,
558
695
  )
559
696
 
560
- omezarr = OmeZarrContainer(store=handler.store, mode="r+")
697
+ omezarr = OmeZarrContainer(group_handler=handler)
561
698
  image = omezarr.get_image()
562
699
  image.set_array(array)
563
700
  image.consolidate()
@@ -1,35 +1,47 @@
1
1
  """Utilities for reading and writing OME-Zarr metadata."""
2
2
 
3
- from ngio.ome_zarr_meta._generic_handlers import (
4
- BaseImageMetaHandler,
5
- BaseLabelMetaHandler,
3
+ from ngio.ome_zarr_meta._meta_handlers import (
6
4
  ImageMetaHandler,
7
5
  LabelMetaHandler,
8
- )
9
- from ngio.ome_zarr_meta._meta_handlers import (
10
- ImplementedImageMetaHandlers,
11
- ImplementedLabelMetaHandlers,
12
- open_image_meta_handler,
6
+ find_image_meta_handler,
7
+ find_label_meta_handler,
8
+ find_plate_meta_handler,
9
+ find_well_meta_handler,
10
+ get_image_meta_handler,
11
+ get_label_meta_handler,
12
+ get_plate_meta_handler,
13
+ get_well_meta_handler,
13
14
  )
14
15
  from ngio.ome_zarr_meta.ngio_specs import (
15
16
  AxesMapper,
16
17
  Dataset,
18
+ ImageInWellPath,
17
19
  NgioImageMeta,
18
20
  NgioLabelMeta,
21
+ NgioPlateMeta,
22
+ NgioWellMeta,
19
23
  PixelSize,
20
24
  )
21
25
 
22
26
  __all__ = [
23
27
  "AxesMapper",
24
- "BaseImageMetaHandler",
25
- "BaseLabelMetaHandler",
26
28
  "Dataset",
29
+ "ImageInWellPath",
27
30
  "ImageMetaHandler",
28
- "ImplementedImageMetaHandlers",
29
- "ImplementedLabelMetaHandlers",
31
+ "ImageMetaHandler",
32
+ "LabelMetaHandler",
30
33
  "LabelMetaHandler",
31
34
  "NgioImageMeta",
32
35
  "NgioLabelMeta",
36
+ "NgioPlateMeta",
37
+ "NgioWellMeta",
33
38
  "PixelSize",
34
- "open_image_meta_handler",
39
+ "find_image_meta_handler",
40
+ "find_label_meta_handler",
41
+ "find_plate_meta_handler",
42
+ "find_well_meta_handler",
43
+ "get_image_meta_handler",
44
+ "get_label_meta_handler",
45
+ "get_plate_meta_handler",
46
+ "get_well_meta_handler",
35
47
  ]