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
ngio/images/create.py CHANGED
@@ -5,11 +5,11 @@ from typing import TypeVar
5
5
 
6
6
  from ngio.common._pyramid import init_empty_pyramid
7
7
  from ngio.ome_zarr_meta import (
8
- ImplementedImageMetaHandlers,
9
- ImplementedLabelMetaHandlers,
10
8
  NgioImageMeta,
11
9
  NgioLabelMeta,
12
10
  PixelSize,
11
+ get_image_meta_handler,
12
+ get_label_meta_handler,
13
13
  )
14
14
  from ngio.ome_zarr_meta.ngio_specs import (
15
15
  SpaceUnits,
@@ -17,19 +17,19 @@ from ngio.ome_zarr_meta.ngio_specs import (
17
17
  canonical_axes_order,
18
18
  canonical_label_axes_order,
19
19
  )
20
- from ngio.utils import StoreOrGroup, ZarrGroupHandler
20
+ from ngio.utils import NgioValueError, StoreOrGroup, ZarrGroupHandler
21
21
 
22
22
  _image_or_label_meta = TypeVar("_image_or_label_meta", NgioImageMeta, NgioLabelMeta)
23
23
 
24
24
 
25
25
  def _init_generic_meta(
26
26
  meta_type: type[_image_or_label_meta],
27
- xy_pixelsize: float,
27
+ pixelsize: float,
28
28
  axes_names: Collection[str],
29
29
  z_spacing: float = 1.0,
30
30
  time_spacing: float = 1.0,
31
31
  levels: int | list[str] = 5,
32
- xy_scaling_factor: float = 2.0,
32
+ yx_scaling_factor: float | tuple[float, float] = 2.0,
33
33
  z_scaling_factor: float = 1.0,
34
34
  space_unit: SpaceUnits | str | None = None,
35
35
  time_unit: TimeUnits | str | None = None,
@@ -41,8 +41,16 @@ def _init_generic_meta(
41
41
  for ax in axes_names:
42
42
  if ax == "z":
43
43
  scaling_factors.append(z_scaling_factor)
44
- elif ax in ["x", "y"]:
45
- scaling_factors.append(xy_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)
46
54
  else:
47
55
  scaling_factors.append(1.0)
48
56
 
@@ -51,18 +59,18 @@ def _init_generic_meta(
51
59
  elif isinstance(space_unit, str):
52
60
  space_unit = SpaceUnits(space_unit)
53
61
  elif not isinstance(space_unit, SpaceUnits):
54
- raise ValueError(f"space_unit can not be {type(space_unit)}.")
62
+ raise NgioValueError(f"space_unit can not be {type(space_unit)}.")
55
63
 
56
64
  if time_unit is None:
57
65
  time_unit = TimeUnits.seconds
58
66
  elif isinstance(time_unit, str):
59
67
  time_unit = TimeUnits(time_unit)
60
68
  elif not isinstance(time_unit, TimeUnits):
61
- raise ValueError(f"time_units can not be {type(time_unit)}.")
69
+ raise NgioValueError(f"time_units can not be {type(time_unit)}.")
62
70
 
63
71
  pixel_sizes = PixelSize(
64
- x=xy_pixelsize,
65
- y=xy_pixelsize,
72
+ x=pixelsize,
73
+ y=pixelsize,
66
74
  z=z_spacing,
67
75
  t=time_spacing,
68
76
  space_unit=space_unit,
@@ -83,11 +91,11 @@ def _init_generic_meta(
83
91
  def _create_empty_label(
84
92
  store: StoreOrGroup,
85
93
  shape: Collection[int],
86
- xy_pixelsize: float,
94
+ pixelsize: float,
87
95
  z_spacing: float = 1.0,
88
96
  time_spacing: float = 1.0,
89
97
  levels: int | list[str] = 5,
90
- xy_scaling_factor: float = 2.0,
98
+ yx_scaling_factor: float | tuple[float, float] = 2.0,
91
99
  z_scaling_factor: float = 1.0,
92
100
  space_unit: SpaceUnits | str | None = None,
93
101
  time_unit: TimeUnits | str | None = None,
@@ -103,13 +111,13 @@ def _create_empty_label(
103
111
  Args:
104
112
  store (StoreOrGroup): The Zarr store or group to create the image in.
105
113
  shape (Collection[int]): The shape of the image.
106
- xy_pixelsize (float): The pixel size in x and y dimensions.
114
+ pixelsize (float): The pixel size in x and y dimensions.
107
115
  z_spacing (float, optional): The spacing between z slices. Defaults to 1.0.
108
116
  time_spacing (float, optional): The spacing between time points.
109
117
  Defaults to 1.0.
110
118
  levels (int | list[str], optional): The number of levels in the pyramid or a
111
119
  list of level names. Defaults to 5.
112
- xy_scaling_factor (float, optional): The down-scaling factor in x and y
120
+ yx_scaling_factor (float, optional): The down-scaling factor in x and y
113
121
  dimensions. Defaults to 2.0.
114
122
  z_scaling_factor (float, optional): The down-scaling factor in z dimension.
115
123
  Defaults to 1.0.
@@ -132,13 +140,19 @@ def _create_empty_label(
132
140
  if axes_names is None:
133
141
  axes_names = canonical_label_axes_order()[-len(shape) :]
134
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
+
135
149
  meta, scaling_factors = _init_generic_meta(
136
150
  meta_type=NgioLabelMeta,
137
- xy_pixelsize=xy_pixelsize,
151
+ pixelsize=pixelsize,
138
152
  z_spacing=z_spacing,
139
153
  time_spacing=time_spacing,
140
154
  levels=levels,
141
- xy_scaling_factor=xy_scaling_factor,
155
+ yx_scaling_factor=yx_scaling_factor,
142
156
  z_scaling_factor=z_scaling_factor,
143
157
  space_unit=space_unit,
144
158
  time_unit=time_unit,
@@ -149,9 +163,7 @@ def _create_empty_label(
149
163
 
150
164
  mode = "w" if overwrite else "w-"
151
165
  group_handler = ZarrGroupHandler(store=store, mode=mode, cache=False)
152
- image_handler = ImplementedLabelMetaHandlers().get_handler(
153
- version=version, group_handler=group_handler
154
- )
166
+ image_handler = get_label_meta_handler(version=version, group_handler=group_handler)
155
167
  image_handler.write_meta(meta)
156
168
 
157
169
  init_empty_pyramid(
@@ -163,17 +175,18 @@ def _create_empty_label(
163
175
  dtype=dtype,
164
176
  mode="a",
165
177
  )
178
+ group_handler._mode = "r+"
166
179
  return group_handler
167
180
 
168
181
 
169
182
  def _create_empty_image(
170
183
  store: StoreOrGroup,
171
184
  shape: Collection[int],
172
- xy_pixelsize: float,
185
+ pixelsize: float,
173
186
  z_spacing: float = 1.0,
174
187
  time_spacing: float = 1.0,
175
188
  levels: int | list[str] = 5,
176
- xy_scaling_factor: float = 2,
189
+ yx_scaling_factor: float | tuple[float, float] = 2,
177
190
  z_scaling_factor: float = 1.0,
178
191
  space_unit: SpaceUnits | str | None = None,
179
192
  time_unit: TimeUnits | str | None = None,
@@ -189,13 +202,13 @@ def _create_empty_image(
189
202
  Args:
190
203
  store (StoreOrGroup): The Zarr store or group to create the image in.
191
204
  shape (Collection[int]): The shape of the image.
192
- xy_pixelsize (float): The pixel size in x and y dimensions.
205
+ pixelsize (float): The pixel size in x and y dimensions.
193
206
  z_spacing (float, optional): The spacing between z slices. Defaults to 1.0.
194
207
  time_spacing (float, optional): The spacing between time points.
195
208
  Defaults to 1.0.
196
209
  levels (int | list[str], optional): The number of levels in the pyramid or a
197
210
  list of level names. Defaults to 5.
198
- xy_scaling_factor (float, optional): The down-scaling factor in x and y
211
+ yx_scaling_factor (float, optional): The down-scaling factor in x and y
199
212
  dimensions. Defaults to 2.0.
200
213
  z_scaling_factor (float, optional): The down-scaling factor in z dimension.
201
214
  Defaults to 1.0.
@@ -218,13 +231,19 @@ def _create_empty_image(
218
231
  if axes_names is None:
219
232
  axes_names = canonical_axes_order()[-len(shape) :]
220
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
+
221
240
  meta, scaling_factors = _init_generic_meta(
222
241
  meta_type=NgioImageMeta,
223
- xy_pixelsize=xy_pixelsize,
242
+ pixelsize=pixelsize,
224
243
  z_spacing=z_spacing,
225
244
  time_spacing=time_spacing,
226
245
  levels=levels,
227
- xy_scaling_factor=xy_scaling_factor,
246
+ yx_scaling_factor=yx_scaling_factor,
228
247
  z_scaling_factor=z_scaling_factor,
229
248
  space_unit=space_unit,
230
249
  time_unit=time_unit,
@@ -234,9 +253,7 @@ def _create_empty_image(
234
253
  )
235
254
  mode = "w" if overwrite else "w-"
236
255
  group_handler = ZarrGroupHandler(store=store, mode=mode, cache=False)
237
- image_handler = ImplementedImageMetaHandlers().get_handler(
238
- version=version, group_handler=group_handler
239
- )
256
+ image_handler = get_image_meta_handler(version=version, group_handler=group_handler)
240
257
  image_handler.write_meta(meta)
241
258
 
242
259
  init_empty_pyramid(
@@ -248,4 +265,6 @@ def _create_empty_image(
248
265
  dtype=dtype,
249
266
  mode="a",
250
267
  )
268
+
269
+ group_handler._mode = "r+"
251
270
  return group_handler
ngio/images/image.py CHANGED
@@ -10,9 +10,9 @@ from ngio.images.abstract_image import AbstractImage, consolidate_image
10
10
  from ngio.images.create import _create_empty_image
11
11
  from ngio.ome_zarr_meta import (
12
12
  ImageMetaHandler,
13
- ImplementedImageMetaHandlers,
14
13
  NgioImageMeta,
15
14
  PixelSize,
15
+ find_image_meta_handler,
16
16
  )
17
17
  from ngio.ome_zarr_meta.ngio_specs import Channel, ChannelsMeta, ChannelVisualisation
18
18
  from ngio.utils import (
@@ -60,9 +60,7 @@ class Image(AbstractImage[ImageMetaHandler]):
60
60
 
61
61
  """
62
62
  if meta_handler is None:
63
- meta_handler = ImplementedImageMetaHandlers().find_meta_handler(
64
- group_handler
65
- )
63
+ meta_handler = find_image_meta_handler(group_handler)
66
64
  super().__init__(
67
65
  group_handler=group_handler, path=path, meta_handler=meta_handler
68
66
  )
@@ -109,9 +107,7 @@ class ImagesContainer:
109
107
  def __init__(self, group_handler: ZarrGroupHandler) -> None:
110
108
  """Initialize the LabelGroupHandler."""
111
109
  self._group_handler = group_handler
112
- self._meta_handler = ImplementedImageMetaHandlers().find_meta_handler(
113
- group_handler
114
- )
110
+ self._meta_handler = find_image_meta_handler(group_handler)
115
111
 
116
112
  @property
117
113
  def meta(self) -> NgioImageMeta:
@@ -239,20 +235,40 @@ class ImagesContainer:
239
235
  store: StoreOrGroup,
240
236
  ref_path: str | None = None,
241
237
  shape: Collection[int] | None = None,
238
+ labels: Collection[str] | None = None,
239
+ pixel_size: PixelSize | None = None,
240
+ axes_names: Collection[str] | None = None,
242
241
  chunks: Collection[int] | None = None,
243
- xy_scaling_factor: float = 2.0,
244
- z_scaling_factor: float = 1.0,
242
+ dtype: str | None = None,
245
243
  overwrite: bool = False,
246
244
  ) -> "ImagesContainer":
247
- """Create an OME-Zarr image from a numpy array."""
245
+ """Create an empty OME-Zarr image from an existing image.
246
+
247
+ Args:
248
+ store (StoreOrGroup): The Zarr store or group to create the image in.
249
+ ref_path (str | None): The path to the reference image in
250
+ the image container.
251
+ shape (Collection[int] | None): The shape of the new image.
252
+ labels (Collection[str] | None): The labels of the new image.
253
+ pixel_size (PixelSize | None): The pixel size of the new image.
254
+ axes_names (Collection[str] | None): The axes names of the new image.
255
+ chunks (Collection[int] | None): The chunk shape of the new image.
256
+ dtype (str | None): The data type of the new image.
257
+ overwrite (bool): Whether to overwrite an existing image.
258
+
259
+ Returns:
260
+ ImagesContainer: The new image
261
+ """
248
262
  return derive_image_container(
249
263
  image_container=self,
250
264
  store=store,
251
265
  ref_path=ref_path,
252
266
  shape=shape,
267
+ labels=labels,
268
+ pixel_size=pixel_size,
269
+ axes_names=axes_names,
253
270
  chunks=chunks,
254
- xy_scaling_factor=xy_scaling_factor,
255
- z_scaling_factor=z_scaling_factor,
271
+ dtype=dtype,
256
272
  overwrite=overwrite,
257
273
  )
258
274
 
@@ -319,7 +335,6 @@ def compute_image_percentile(
319
335
 
320
336
  starts.append(float(_s_perc))
321
337
  ends.append(float(_e_perc))
322
-
323
338
  return starts, ends
324
339
 
325
340
 
@@ -328,12 +343,31 @@ def derive_image_container(
328
343
  store: StoreOrGroup,
329
344
  ref_path: str | None = None,
330
345
  shape: Collection[int] | None = None,
346
+ labels: Collection[str] | None = None,
347
+ pixel_size: PixelSize | None = None,
348
+ axes_names: Collection[str] | None = None,
331
349
  chunks: Collection[int] | None = None,
332
- xy_scaling_factor: float = 2.0,
333
- z_scaling_factor: float = 1.0,
350
+ dtype: str | None = None,
334
351
  overwrite: bool = False,
335
352
  ) -> ImagesContainer:
336
- """Create an OME-Zarr image from a numpy array."""
353
+ """Create an empty OME-Zarr image from an existing image.
354
+
355
+ Args:
356
+ image_container (ImagesContainer): The image container to derive the new image.
357
+ store (StoreOrGroup): The Zarr store or group to create the image in.
358
+ ref_path (str | None): The path to the reference image in the image container.
359
+ shape (Collection[int] | None): The shape of the new image.
360
+ labels (Collection[str] | None): The labels of the new image.
361
+ pixel_size (PixelSize | None): The pixel size of the new image.
362
+ axes_names (Collection[str] | None): The axes names of the new image.
363
+ chunks (Collection[int] | None): The chunk shape of the new image.
364
+ dtype (str | None): The data type of the new image.
365
+ overwrite (bool): Whether to overwrite an existing image.
366
+
367
+ Returns:
368
+ ImagesContainer: The new image
369
+
370
+ """
337
371
  if ref_path is None:
338
372
  ref_image = image_container.get()
339
373
  else:
@@ -343,58 +377,77 @@ def derive_image_container(
343
377
 
344
378
  if shape is None:
345
379
  shape = ref_image.shape
346
- else:
347
- if len(shape) != len(ref_image.shape):
348
- raise NgioValidationError(
349
- "The shape of the new image does not match the reference image."
350
- )
380
+
381
+ if pixel_size is None:
382
+ pixel_size = ref_image.pixel_size
383
+
384
+ if axes_names is None:
385
+ axes_names = ref_meta.axes_mapper.on_disk_axes_names
386
+
387
+ if len(axes_names) != len(shape):
388
+ raise NgioValidationError(
389
+ "The axes names of the new image does not match the reference image."
390
+ f"Got {axes_names} for shape {shape}."
391
+ )
351
392
 
352
393
  if chunks is None:
353
394
  chunks = ref_image.chunks
354
- else:
355
- if len(chunks) != len(ref_image.chunks):
356
- raise NgioValidationError(
357
- "The chunks of the new image does not match the reference image."
358
- )
359
395
 
396
+ if len(chunks) != len(shape):
397
+ raise NgioValidationError(
398
+ "The chunks of the new image does not match the reference image."
399
+ f"Got {chunks} for shape {shape}."
400
+ )
401
+
402
+ if dtype is None:
403
+ dtype = ref_image.dtype
360
404
  handler = _create_empty_image(
361
405
  store=store,
362
406
  shape=shape,
363
- xy_pixelsize=ref_image.pixel_size.x,
364
- z_spacing=ref_image.pixel_size.z,
365
- time_spacing=ref_image.pixel_size.t,
407
+ pixelsize=pixel_size.x,
408
+ z_spacing=pixel_size.z,
409
+ time_spacing=pixel_size.t,
366
410
  levels=ref_meta.levels,
367
- xy_scaling_factor=xy_scaling_factor,
368
- z_scaling_factor=z_scaling_factor,
369
- time_unit=ref_image.pixel_size.time_unit,
370
- space_unit=ref_image.pixel_size.space_unit,
371
- axes_names=ref_image.dataset.axes_mapper.on_disk_axes_names,
411
+ yx_scaling_factor=ref_meta.yx_scaling(),
412
+ z_scaling_factor=ref_meta.z_scaling(),
413
+ time_unit=pixel_size.time_unit,
414
+ space_unit=pixel_size.space_unit,
415
+ axes_names=axes_names,
372
416
  chunks=chunks,
373
- dtype=ref_image.dtype,
417
+ dtype=dtype,
374
418
  overwrite=overwrite,
375
419
  version=ref_meta.version,
376
420
  )
377
-
378
421
  image_container = ImagesContainer(handler)
379
422
 
380
423
  if ref_image.num_channels == image_container.num_channels:
381
- labels = ref_image.channel_labels
424
+ _labels = ref_image.channel_labels
382
425
  wavelength_id = ref_image.wavelength_ids
426
+
383
427
  colors = [
384
428
  c.channel_visualisation.color for c in ref_image._channels_meta.channels
385
429
  ]
386
430
  active = [
387
431
  c.channel_visualisation.active for c in ref_image._channels_meta.channels
388
432
  ]
389
-
390
- image_container.initialize_channel_meta(
391
- labels=labels,
392
- wavelength_id=wavelength_id,
393
- percentiles=None,
394
- colors=colors,
395
- active=active,
396
- )
397
433
  else:
398
- image_container.initialize_channel_meta()
434
+ _labels = None
435
+ wavelength_id = None
436
+ colors = None
437
+ active = None
399
438
 
439
+ if labels is not None:
440
+ if len(labels) != image_container.num_channels:
441
+ raise NgioValidationError(
442
+ "The number of labels does not match the number of channels."
443
+ )
444
+ _labels = labels
445
+
446
+ image_container.initialize_channel_meta(
447
+ labels=_labels,
448
+ wavelength_id=wavelength_id,
449
+ percentiles=None,
450
+ colors=colors,
451
+ active=active,
452
+ )
400
453
  return image_container