ngio 0.4.8__py3-none-any.whl → 0.5.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 (56) hide show
  1. ngio/__init__.py +5 -2
  2. ngio/common/__init__.py +11 -6
  3. ngio/common/_masking_roi.py +34 -54
  4. ngio/common/_pyramid.py +322 -75
  5. ngio/common/_roi.py +258 -330
  6. ngio/experimental/iterators/_feature.py +3 -3
  7. ngio/experimental/iterators/_rois_utils.py +10 -11
  8. ngio/hcs/_plate.py +192 -136
  9. ngio/images/_abstract_image.py +539 -35
  10. ngio/images/_create_synt_container.py +45 -47
  11. ngio/images/_create_utils.py +406 -0
  12. ngio/images/_image.py +524 -248
  13. ngio/images/_label.py +257 -180
  14. ngio/images/_masked_image.py +2 -2
  15. ngio/images/_ome_zarr_container.py +658 -255
  16. ngio/io_pipes/_io_pipes.py +9 -9
  17. ngio/io_pipes/_io_pipes_masked.py +7 -7
  18. ngio/io_pipes/_io_pipes_roi.py +6 -6
  19. ngio/io_pipes/_io_pipes_types.py +3 -3
  20. ngio/io_pipes/_match_shape.py +6 -8
  21. ngio/io_pipes/_ops_slices_utils.py +8 -5
  22. ngio/ome_zarr_meta/__init__.py +29 -18
  23. ngio/ome_zarr_meta/_meta_handlers.py +402 -689
  24. ngio/ome_zarr_meta/ngio_specs/__init__.py +4 -0
  25. ngio/ome_zarr_meta/ngio_specs/_axes.py +152 -51
  26. ngio/ome_zarr_meta/ngio_specs/_dataset.py +13 -22
  27. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +129 -91
  28. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +69 -69
  29. ngio/ome_zarr_meta/v04/__init__.py +5 -1
  30. ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +55 -86
  31. ngio/ome_zarr_meta/v05/__init__.py +27 -0
  32. ngio/ome_zarr_meta/v05/_custom_models.py +18 -0
  33. ngio/ome_zarr_meta/v05/_v05_spec.py +495 -0
  34. ngio/resources/__init__.py +1 -1
  35. ngio/resources/resource_model.py +1 -1
  36. ngio/tables/_tables_container.py +82 -24
  37. ngio/tables/backends/_abstract_backend.py +7 -0
  38. ngio/tables/backends/_anndata.py +60 -7
  39. ngio/tables/backends/_anndata_utils.py +2 -4
  40. ngio/tables/backends/_csv.py +3 -19
  41. ngio/tables/backends/_json.py +10 -13
  42. ngio/tables/backends/_parquet.py +3 -31
  43. ngio/tables/backends/_py_arrow_backends.py +222 -0
  44. ngio/tables/backends/_utils.py +1 -1
  45. ngio/tables/v1/_roi_table.py +41 -24
  46. ngio/utils/__init__.py +8 -12
  47. ngio/utils/_cache.py +48 -0
  48. ngio/utils/_zarr_utils.py +354 -236
  49. {ngio-0.4.8.dist-info → ngio-0.5.0.dist-info}/METADATA +12 -5
  50. ngio-0.5.0.dist-info/RECORD +88 -0
  51. ngio/images/_create.py +0 -276
  52. ngio/tables/backends/_non_zarr_backends.py +0 -196
  53. ngio/utils/_logger.py +0 -50
  54. ngio-0.4.8.dist-info/RECORD +0 -85
  55. {ngio-0.4.8.dist-info → ngio-0.5.0.dist-info}/WHEEL +0 -0
  56. {ngio-0.4.8.dist-info → ngio-0.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,495 @@
1
+ """Utilities for OME-Zarr v05 specs.
2
+
3
+ This module provides a set of classes to internally handle the metadata
4
+ of the OME-Zarr v05 specification.
5
+
6
+ For Images and Labels implements the following functionalities:
7
+ - A function to find if a dict view of the metadata is a valid OME-Zarr v05 metadata.
8
+ - A function to convert a v05 image metadata to a ngio image metadata.
9
+ - A function to convert a ngio image metadata to a v05 image metadata.
10
+ """
11
+
12
+ from ome_zarr_models.common.coordinate_transformations import (
13
+ ValidTransform as ValidTransformV05,
14
+ )
15
+ from ome_zarr_models.common.omero import Channel as ChannelV05
16
+ from ome_zarr_models.common.omero import Omero as OmeroV05
17
+ from ome_zarr_models.common.omero import Window as WindowV05
18
+ from ome_zarr_models.v05.axes import Axis as AxisV05
19
+ from ome_zarr_models.v05.coordinate_transformations import VectorScale as VectorScaleV05
20
+ from ome_zarr_models.v05.coordinate_transformations import (
21
+ VectorTranslation as VectorTranslationV05,
22
+ )
23
+ from ome_zarr_models.v05.hcs import HCSAttrs as HCSAttrsV05
24
+ from ome_zarr_models.v05.image import ImageAttrs as ImageAttrsV05
25
+ from ome_zarr_models.v05.image_label import ImageLabelAttrs as ImageLabelAttrsV05
26
+ from ome_zarr_models.v05.labels import Labels as Labels
27
+ from ome_zarr_models.v05.labels import LabelsAttrs as LabelsAttrsV05
28
+ from ome_zarr_models.v05.multiscales import Dataset as DatasetV05
29
+ from ome_zarr_models.v05.multiscales import Multiscale as MultiscaleV05
30
+ from pydantic import BaseModel
31
+
32
+ from ngio.ome_zarr_meta.ngio_specs import (
33
+ AxesHandler,
34
+ AxesSetup,
35
+ Axis,
36
+ AxisType,
37
+ Channel,
38
+ ChannelsMeta,
39
+ ChannelVisualisation,
40
+ Dataset,
41
+ ImageLabelSource,
42
+ NgioImageMeta,
43
+ NgioLabelMeta,
44
+ NgioLabelsGroupMeta,
45
+ NgioPlateMeta,
46
+ NgioWellMeta,
47
+ default_channel_name,
48
+ )
49
+ from ngio.ome_zarr_meta.v05._custom_models import CustomWellAttrs as WellAttrsV05
50
+
51
+
52
+ class ImageV05AttrsWithOmero(ImageAttrsV05):
53
+ omero: OmeroV05 | None = None
54
+
55
+
56
+ class ImageV05WithOmero(BaseModel):
57
+ ome: ImageV05AttrsWithOmero
58
+
59
+
60
+ class ImageLabelV05(BaseModel):
61
+ ome: ImageLabelAttrsV05
62
+
63
+
64
+ def _v05_omero_to_channels(v05_omero: OmeroV05 | None) -> ChannelsMeta | None:
65
+ if v05_omero is None:
66
+ return None
67
+
68
+ ngio_channels = []
69
+ for idx, v05_channel in enumerate(v05_omero.channels):
70
+ channel_extra = v05_channel.model_extra
71
+
72
+ if channel_extra is None:
73
+ channel_extra = {}
74
+
75
+ if "label" in channel_extra:
76
+ label = channel_extra.pop("label")
77
+ else:
78
+ label = default_channel_name(idx)
79
+
80
+ if "wavelength_id" in channel_extra:
81
+ wavelength_id = channel_extra.pop("wavelength_id")
82
+ else:
83
+ wavelength_id = label
84
+
85
+ if "active" in channel_extra:
86
+ active = channel_extra.pop("active")
87
+ else:
88
+ active = True
89
+
90
+ channel_visualisation = ChannelVisualisation(
91
+ color=v05_channel.color,
92
+ start=v05_channel.window.start,
93
+ end=v05_channel.window.end,
94
+ min=v05_channel.window.min,
95
+ max=v05_channel.window.max,
96
+ active=active,
97
+ **channel_extra,
98
+ )
99
+
100
+ ngio_channels.append(
101
+ Channel(
102
+ label=label,
103
+ wavelength_id=wavelength_id,
104
+ channel_visualisation=channel_visualisation,
105
+ )
106
+ )
107
+
108
+ v05_omero_extra = v05_omero.model_extra if v05_omero.model_extra is not None else {}
109
+ return ChannelsMeta(channels=ngio_channels, **v05_omero_extra)
110
+
111
+
112
+ def _compute_scale_translation(
113
+ v05_transforms: ValidTransformV05,
114
+ scale: list[float],
115
+ translation: list[float],
116
+ ) -> tuple[list[float], list[float]]:
117
+ for v05_transform in v05_transforms:
118
+ if isinstance(v05_transform, VectorScaleV05):
119
+ scale = [t1 * t2 for t1, t2 in zip(scale, v05_transform.scale, strict=True)]
120
+
121
+ elif isinstance(v05_transform, VectorTranslationV05):
122
+ translation = [
123
+ t1 + t2
124
+ for t1, t2 in zip(translation, v05_transform.translation, strict=True)
125
+ ]
126
+ else:
127
+ raise NotImplementedError(
128
+ f"Coordinate transformation {v05_transform} is not supported."
129
+ )
130
+ return scale, translation
131
+
132
+
133
+ def _v05_to_ngio_datasets(
134
+ v05_multiscale: MultiscaleV05,
135
+ axes_setup: AxesSetup,
136
+ ) -> list[Dataset]:
137
+ """Convert a v05 multiscale to a list of ngio datasets."""
138
+ datasets = []
139
+
140
+ global_scale = [1.0] * len(v05_multiscale.axes)
141
+ global_translation = [0.0] * len(v05_multiscale.axes)
142
+
143
+ if v05_multiscale.coordinateTransformations is not None:
144
+ global_scale, global_translation = _compute_scale_translation(
145
+ v05_multiscale.coordinateTransformations, global_scale, global_translation
146
+ )
147
+
148
+ # Prepare axes handler
149
+ axes = []
150
+ for v05_axis in v05_multiscale.axes:
151
+ unit = v05_axis.unit
152
+ if unit is not None and not isinstance(unit, str):
153
+ unit = str(unit)
154
+ axes.append(
155
+ Axis(
156
+ name=str(v05_axis.name),
157
+ axis_type=AxisType(v05_axis.type),
158
+ # (for some reason the type is a generic JsonValue,
159
+ # but it should be a string or None)
160
+ unit=v05_axis.unit, # type: ignore
161
+ )
162
+ )
163
+ axes_handler = AxesHandler(
164
+ axes=axes,
165
+ axes_setup=axes_setup,
166
+ )
167
+
168
+ for v05_dataset in v05_multiscale.datasets:
169
+ _scale, _translation = _compute_scale_translation(
170
+ v05_dataset.coordinateTransformations, global_scale, global_translation
171
+ )
172
+ datasets.append(
173
+ Dataset(
174
+ path=v05_dataset.path,
175
+ axes_handler=axes_handler,
176
+ scale=_scale,
177
+ translation=_translation,
178
+ )
179
+ )
180
+ return datasets
181
+
182
+
183
+ def v05_to_ngio_image_meta(
184
+ metadata: dict,
185
+ axes_setup: AxesSetup,
186
+ ) -> NgioImageMeta:
187
+ """Convert a v05 image metadata to a ngio image metadata.
188
+
189
+ Args:
190
+ metadata (dict): The v05 image metadata.
191
+ axes_setup (AxesSetup): The axes setup. This is
192
+ required to convert image with non-canonical axes names.
193
+
194
+ Returns:
195
+ NgioImageMeta: The ngio image metadata.
196
+ """
197
+ v05_image = ImageV05WithOmero(**metadata)
198
+ v05_image = v05_image.ome
199
+ if len(v05_image.multiscales) > 1:
200
+ raise NotImplementedError(
201
+ "Multiple multiscales in a single image are not supported in ngio."
202
+ )
203
+
204
+ v05_multiscale = v05_image.multiscales[0]
205
+
206
+ channels_meta = _v05_omero_to_channels(v05_image.omero)
207
+ datasets = _v05_to_ngio_datasets(
208
+ v05_multiscale,
209
+ axes_setup=axes_setup,
210
+ )
211
+
212
+ name = v05_multiscale.name
213
+ if name is not None and not isinstance(name, str):
214
+ name = str(name)
215
+ return NgioImageMeta(
216
+ version="0.5",
217
+ name=name,
218
+ datasets=datasets,
219
+ channels=channels_meta,
220
+ )
221
+
222
+
223
+ def v05_to_ngio_label_meta(
224
+ metadata: dict,
225
+ axes_setup: AxesSetup,
226
+ ) -> NgioLabelMeta:
227
+ """Convert a v05 image metadata to a ngio image metadata.
228
+
229
+ Args:
230
+ metadata (dict): The v05 image metadata.
231
+ axes_setup (AxesSetup): The axes setup. This is
232
+ required to convert image with non-canonical axes names.
233
+
234
+ Returns:
235
+ NgioLabelMeta: The ngio label metadata.
236
+ """
237
+ v05_label = ImageLabelV05(**metadata)
238
+ v05_label = v05_label.ome
239
+
240
+ if len(v05_label.multiscales) > 1:
241
+ raise NotImplementedError(
242
+ "Multiple multiscales in a single image are not supported in ngio."
243
+ )
244
+
245
+ v05_multiscale = v05_label.multiscales[0]
246
+
247
+ datasets = _v05_to_ngio_datasets(
248
+ v05_multiscale,
249
+ axes_setup=axes_setup,
250
+ )
251
+
252
+ if v05_label.image_label is not None:
253
+ source = v05_label.image_label.source
254
+ if source is None:
255
+ image_label_source = None
256
+ else:
257
+ source = v05_label.image_label.source
258
+ if source is None:
259
+ image_label_source = None
260
+ else:
261
+ image_label_source = source.image
262
+ image_label_source = ImageLabelSource(
263
+ version="0.5",
264
+ source={"image": image_label_source},
265
+ )
266
+ else:
267
+ image_label_source = None
268
+ name = v05_multiscale.name
269
+ if name is not None and not isinstance(name, str):
270
+ name = str(name)
271
+
272
+ return NgioLabelMeta(
273
+ version="0.5",
274
+ name=name,
275
+ datasets=datasets,
276
+ image_label=image_label_source,
277
+ )
278
+
279
+
280
+ def _ngio_to_v05_multiscale(name: str | None, datasets: list[Dataset]) -> MultiscaleV05:
281
+ """Convert a ngio multiscale to a v05 multiscale.
282
+
283
+ Args:
284
+ name (str | None): The name of the multiscale.
285
+ datasets (list[Dataset]): The ngio datasets.
286
+
287
+ Returns:
288
+ MultiscaleV05: The v05 multiscale.
289
+ """
290
+ ax_mapper = datasets[0].axes_handler
291
+ v05_axes = []
292
+ for axis in ax_mapper.axes:
293
+ v05_axes.append(
294
+ AxisV05(
295
+ name=axis.name,
296
+ type=axis.axis_type.value if axis.axis_type is not None else None,
297
+ unit=axis.unit if axis.unit is not None else None,
298
+ )
299
+ )
300
+
301
+ v05_datasets = []
302
+ for dataset in datasets:
303
+ transform = [VectorScaleV05(type="scale", scale=list(dataset._scale))]
304
+ if sum(dataset._translation) > 0:
305
+ transform = (
306
+ VectorScaleV05(type="scale", scale=list(dataset._scale)),
307
+ VectorTranslationV05(
308
+ type="translation", translation=list(dataset._translation)
309
+ ),
310
+ )
311
+ else:
312
+ transform = (VectorScaleV05(type="scale", scale=list(dataset._scale)),)
313
+
314
+ v05_datasets.append(
315
+ DatasetV05(path=dataset.path, coordinateTransformations=transform)
316
+ )
317
+ return MultiscaleV05(axes=v05_axes, datasets=tuple(v05_datasets), name=name)
318
+
319
+
320
+ def _ngio_to_v05_omero(channels: ChannelsMeta | None) -> OmeroV05 | None:
321
+ """Convert a ngio channels to a v05 omero."""
322
+ if channels is None:
323
+ return None
324
+
325
+ v05_channels = []
326
+ for channel in channels.channels:
327
+ _model_extra = {
328
+ "label": channel.label,
329
+ "wavelength_id": channel.wavelength_id,
330
+ "active": channel.channel_visualisation.active,
331
+ }
332
+ if channel.channel_visualisation.model_extra is not None:
333
+ _model_extra.update(channel.channel_visualisation.model_extra)
334
+
335
+ v05_channels.append(
336
+ ChannelV05(
337
+ color=channel.channel_visualisation.valid_color,
338
+ window=WindowV05(
339
+ start=channel.channel_visualisation.start,
340
+ end=channel.channel_visualisation.end,
341
+ min=channel.channel_visualisation.min,
342
+ max=channel.channel_visualisation.max,
343
+ ),
344
+ **_model_extra,
345
+ )
346
+ )
347
+
348
+ _model_extra = channels.model_extra if channels.model_extra is not None else {}
349
+ return OmeroV05(channels=v05_channels, **_model_extra)
350
+
351
+
352
+ def ngio_to_v05_image_meta(metadata: NgioImageMeta) -> dict:
353
+ """Convert a ngio image metadata to a v05 image metadata.
354
+
355
+ Args:
356
+ metadata (NgioImageMeta): The ngio image metadata.
357
+
358
+ Returns:
359
+ dict: The v05 image metadata.
360
+ """
361
+ v05_muliscale = _ngio_to_v05_multiscale(
362
+ name=metadata.name, datasets=metadata.datasets
363
+ )
364
+ v05_omero = _ngio_to_v05_omero(metadata._channels_meta)
365
+
366
+ v05_image_attrs = ImageV05AttrsWithOmero(
367
+ multiscales=[v05_muliscale], omero=v05_omero, version="0.5"
368
+ )
369
+ v05_image = ImageV05WithOmero(
370
+ ome=v05_image_attrs,
371
+ )
372
+ return v05_image.model_dump(exclude_none=True, by_alias=True)
373
+
374
+
375
+ def ngio_to_v05_label_meta(metadata: NgioLabelMeta) -> dict:
376
+ """Convert a ngio image metadata to a v05 image metadata.
377
+
378
+ Args:
379
+ metadata (NgioImageMeta): The ngio image metadata.
380
+
381
+ Returns:
382
+ dict: The v05 image metadata.
383
+ """
384
+ v05_muliscale = _ngio_to_v05_multiscale(
385
+ name=metadata.name, datasets=metadata.datasets
386
+ )
387
+ labels_meta = {
388
+ "multiscales": [v05_muliscale],
389
+ "image-label": metadata.image_label.model_dump(),
390
+ }
391
+ v05_label = ImageLabelAttrsV05(**labels_meta, version="0.5")
392
+ v05_label = ImageLabelV05(
393
+ ome=v05_label,
394
+ )
395
+ return v05_label.model_dump(exclude_none=True, by_alias=True)
396
+
397
+
398
+ class WellV05(BaseModel):
399
+ ome: WellAttrsV05
400
+
401
+
402
+ class HCSV05(BaseModel):
403
+ ome: HCSAttrsV05
404
+
405
+
406
+ def v05_to_ngio_well_meta(
407
+ metadata: dict,
408
+ ) -> NgioWellMeta:
409
+ """Convert a v05 well metadata to a ngio well metadata.
410
+
411
+ Args:
412
+ metadata (dict): The v05 well metadata.
413
+
414
+ Returns:
415
+ NgioWellMeta: The ngio well metadata.
416
+ """
417
+ v05_well = WellV05(**metadata).ome.well.model_dump()
418
+ images = v05_well.get("images", [])
419
+ return NgioWellMeta(images=images, version="0.5")
420
+
421
+
422
+ def v05_to_ngio_plate_meta(
423
+ metadata: dict,
424
+ ) -> NgioPlateMeta:
425
+ """Convert a v05 plate metadata to a ngio plate metadata.
426
+
427
+ Args:
428
+ metadata (dict): The v05 plate metadata.
429
+
430
+ Returns:
431
+ NgioPlateMeta: The ngio plate metadata.
432
+ """
433
+ v05_plate = HCSV05(**metadata).ome.plate.model_dump()
434
+ return NgioPlateMeta(plate=v05_plate, version="0.5") # type: ignore
435
+
436
+
437
+ def ngio_to_v05_well_meta(metadata: NgioWellMeta) -> dict:
438
+ """Convert a ngio well metadata to a v05 well metadata.
439
+
440
+ Args:
441
+ metadata (NgioWellMeta): The ngio well metadata.
442
+
443
+ Returns:
444
+ dict: The v05 well metadata.
445
+ """
446
+ v05_well = WellAttrsV05(well=metadata.model_dump()) # type: ignore
447
+ v05_well = WellV05(ome=v05_well)
448
+ return v05_well.model_dump(exclude_none=True, by_alias=True)
449
+
450
+
451
+ def ngio_to_v05_plate_meta(metadata: NgioPlateMeta) -> dict:
452
+ """Convert a ngio plate metadata to a v05 plate metadata.
453
+
454
+ Args:
455
+ metadata (NgioPlateMeta): The ngio plate metadata.
456
+
457
+ Returns:
458
+ dict: The v05 plate metadata.
459
+ """
460
+ v05_plate = HCSAttrsV05(**metadata.model_dump())
461
+ v05_plate = HCSV05(ome=v05_plate)
462
+ return v05_plate.model_dump(exclude_none=True, by_alias=True)
463
+
464
+
465
+ class LabelsV05(BaseModel):
466
+ ome: LabelsAttrsV05
467
+
468
+
469
+ def v05_to_ngio_labels_group_meta(
470
+ metadata: dict,
471
+ ) -> NgioLabelsGroupMeta:
472
+ """Convert a v04 label group metadata to a ngio label group metadata.
473
+
474
+ Args:
475
+ metadata (dict): The v04 label group metadata.
476
+
477
+ Returns:
478
+ NgioLabelGroupMeta: The ngio label group metadata.
479
+ """
480
+ v05_label_group = LabelsV05(**metadata)
481
+ return NgioLabelsGroupMeta(labels=v05_label_group.ome.labels, version="0.5")
482
+
483
+
484
+ def ngio_to_v05_labels_group_meta(metadata: NgioLabelsGroupMeta) -> dict:
485
+ """Convert a ngio label group metadata to a v05 label group metadata.
486
+
487
+ Args:
488
+ metadata (NgioLabelsGroupMeta): The ngio label group metadata.
489
+
490
+ Returns:
491
+ dict: The v05 label group metadata.
492
+ """
493
+ v05_labels_attrs = LabelsAttrsV05(labels=metadata.labels, version="0.5")
494
+ v05_labels_group = LabelsV05(ome=v05_labels_attrs)
495
+ return v05_labels_group.model_dump(exclude_none=True, by_alias=True)
@@ -31,7 +31,7 @@ _resources = {
31
31
  dtype="uint8",
32
32
  ),
33
33
  ],
34
- xy_pixelsize=0.325,
34
+ pixelsize=0.325,
35
35
  z_spacing=1.0,
36
36
  time_spacing=1.0,
37
37
  name="Cardiomyocyte Differentiation",
@@ -27,7 +27,7 @@ class SampleInfo(BaseModel):
27
27
 
28
28
  img_path: Path
29
29
  labels: list[LabelsInfo] = Field(default_factory=list)
30
- xy_pixelsize: float
30
+ pixelsize: float
31
31
  z_spacing: float = 1.0
32
32
  time_spacing: float = 1.0
33
33
  space_unit: SpaceUnits = DefaultSpaceUnit