ngio 0.5.0a2__py3-none-any.whl → 0.5.0b1__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 +2 -2
  2. ngio/common/__init__.py +11 -6
  3. ngio/common/_masking_roi.py +12 -41
  4. ngio/common/_pyramid.py +206 -76
  5. ngio/common/_roi.py +257 -329
  6. ngio/experimental/iterators/_feature.py +3 -3
  7. ngio/experimental/iterators/_rois_utils.py +10 -11
  8. ngio/hcs/_plate.py +50 -43
  9. ngio/images/_abstract_image.py +418 -35
  10. ngio/images/_create_synt_container.py +35 -42
  11. ngio/images/_create_utils.py +423 -0
  12. ngio/images/_image.py +162 -176
  13. ngio/images/_label.py +182 -137
  14. ngio/images/_ome_zarr_container.py +372 -197
  15. ngio/io_pipes/_io_pipes.py +9 -9
  16. ngio/io_pipes/_io_pipes_masked.py +7 -7
  17. ngio/io_pipes/_io_pipes_roi.py +6 -6
  18. ngio/io_pipes/_io_pipes_types.py +3 -3
  19. ngio/io_pipes/_match_shape.py +5 -4
  20. ngio/io_pipes/_ops_slices_utils.py +8 -5
  21. ngio/ome_zarr_meta/__init__.py +21 -18
  22. ngio/ome_zarr_meta/_meta_handlers.py +409 -701
  23. ngio/ome_zarr_meta/ngio_specs/__init__.py +2 -0
  24. ngio/ome_zarr_meta/ngio_specs/_axes.py +1 -0
  25. ngio/ome_zarr_meta/ngio_specs/_dataset.py +13 -22
  26. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +54 -61
  27. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +21 -68
  28. ngio/ome_zarr_meta/v04/__init__.py +5 -1
  29. ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +49 -63
  30. ngio/ome_zarr_meta/v05/__init__.py +5 -1
  31. ngio/ome_zarr_meta/v05/{_v05_spec_utils.py → _v05_spec.py} +57 -64
  32. ngio/tables/_tables_container.py +2 -4
  33. ngio/tables/backends/_anndata.py +58 -8
  34. ngio/tables/backends/_anndata_utils.py +1 -6
  35. ngio/tables/backends/_csv.py +3 -19
  36. ngio/tables/backends/_json.py +10 -13
  37. ngio/tables/backends/_parquet.py +3 -31
  38. ngio/tables/backends/_py_arrow_backends.py +222 -0
  39. ngio/tables/v1/_roi_table.py +41 -24
  40. ngio/utils/__init__.py +4 -12
  41. ngio/utils/_zarr_utils.py +163 -53
  42. {ngio-0.5.0a2.dist-info → ngio-0.5.0b1.dist-info}/METADATA +6 -2
  43. ngio-0.5.0b1.dist-info/RECORD +88 -0
  44. {ngio-0.5.0a2.dist-info → ngio-0.5.0b1.dist-info}/WHEEL +1 -1
  45. ngio/images/_create.py +0 -287
  46. ngio/tables/backends/_non_zarr_backends.py +0 -196
  47. ngio/utils/_logger.py +0 -50
  48. ngio-0.5.0a2.dist-info/RECORD +0 -89
  49. {ngio-0.5.0a2.dist-info → ngio-0.5.0b1.dist-info}/licenses/LICENSE +0 -0
ngio/images/_label.py CHANGED
@@ -1,23 +1,26 @@
1
1
  """A module for handling label images in OME-NGFF files."""
2
2
 
3
- from collections.abc import Sequence
4
- from typing import Literal
3
+ from collections.abc import Mapping, Sequence
4
+ from typing import Any, Literal
5
5
 
6
6
  from zarr.core.array import CompressorLike
7
7
 
8
8
  from ngio.common import compute_masking_roi
9
- from ngio.images._abstract_image import AbstractImage
10
- from ngio.images._create import create_empty_label_container
9
+ from ngio.common._pyramid import ChunksLike, ShardsLike
10
+ from ngio.images._abstract_image import AbstractImage, abstract_derive
11
11
  from ngio.images._image import Image
12
12
  from ngio.ome_zarr_meta import (
13
13
  LabelMetaHandler,
14
+ LabelsGroupMetaHandler,
14
15
  NgioLabelMeta,
16
+ NgioLabelsGroupMeta,
15
17
  PixelSize,
16
- find_label_meta_handler,
18
+ update_ngio_labels_group_meta,
17
19
  )
18
20
  from ngio.ome_zarr_meta.ngio_specs import (
19
21
  DefaultSpaceUnit,
20
22
  DefaultTimeUnit,
23
+ NgffVersions,
21
24
  SpaceUnits,
22
25
  TimeUnits,
23
26
  )
@@ -28,10 +31,9 @@ from ngio.utils import (
28
31
  StoreOrGroup,
29
32
  ZarrGroupHandler,
30
33
  )
31
- from ngio.utils._zarr_utils import find_dimension_separator
32
34
 
33
35
 
34
- class Label(AbstractImage[LabelMetaHandler]):
36
+ class Label(AbstractImage):
35
37
  """Placeholder class for a label."""
36
38
 
37
39
  get_as_numpy = AbstractImage._get_as_numpy
@@ -58,7 +60,7 @@ class Label(AbstractImage[LabelMetaHandler]):
58
60
 
59
61
  """
60
62
  if meta_handler is None:
61
- meta_handler = find_label_meta_handler(group_handler)
63
+ meta_handler = LabelMetaHandler(group_handler)
62
64
  super().__init__(
63
65
  group_handler=group_handler, path=path, meta_handler=meta_handler
64
66
  )
@@ -67,10 +69,18 @@ class Label(AbstractImage[LabelMetaHandler]):
67
69
  """Return the string representation of the label."""
68
70
  return f"Label(path={self.path}, {self.dimensions})"
69
71
 
72
+ @property
73
+ def meta_handler(self) -> LabelMetaHandler:
74
+ """Return the metadata handler."""
75
+ assert isinstance(self._meta_handler, LabelMetaHandler)
76
+ return self._meta_handler
77
+
70
78
  @property
71
79
  def meta(self) -> NgioLabelMeta:
72
80
  """Return the metadata."""
73
- return self._meta_handler.meta
81
+ meta = self.meta_handler.get_meta()
82
+ assert isinstance(meta, NgioLabelMeta)
83
+ return meta
74
84
 
75
85
  def set_axes_unit(
76
86
  self,
@@ -85,7 +95,7 @@ class Label(AbstractImage[LabelMetaHandler]):
85
95
  """
86
96
  meta = self.meta
87
97
  meta = meta.to_units(space_unit=space_unit, time_unit=time_unit)
88
- self._meta_handler.write_meta(meta)
98
+ self.meta_handler.update_meta(meta)
89
99
 
90
100
  def build_masking_roi_table(self) -> MaskingRoiTable:
91
101
  """Compute the masking ROI table."""
@@ -105,29 +115,39 @@ class Label(AbstractImage[LabelMetaHandler]):
105
115
  class LabelsContainer:
106
116
  """A class to handle the /labels group in an OME-NGFF file."""
107
117
 
108
- def __init__(self, group_handler: ZarrGroupHandler) -> None:
118
+ def __init__(
119
+ self,
120
+ group_handler: ZarrGroupHandler,
121
+ ngff_version: NgffVersions | None = None,
122
+ ) -> None:
109
123
  """Initialize the LabelGroupHandler."""
110
124
  self._group_handler = group_handler
111
- # Validate the group
112
- # Either contains a labels attribute or is empty
113
- attrs = self._group_handler.load_attrs()
114
- if len(attrs) == 0:
115
- # It's an empty group
116
- pass
117
- elif "labels" in attrs and isinstance(attrs["labels"], list):
118
- # It's a valid group
119
- pass
120
- else:
121
- raise NgioValidationError(
122
- f"Invalid /labels group. "
123
- f"Expected a single labels attribute with a list of label names. "
124
- f"Found: {attrs}"
125
+ # If the group is empty, initialize the metadata
126
+ try:
127
+ self._meta_handler = LabelsGroupMetaHandler(group_handler)
128
+ except NgioValidationError:
129
+ if ngff_version is None:
130
+ raise NgioValueError(
131
+ "The /labels group is missing metadata. "
132
+ "Please provide the ngff_version to initialize it."
133
+ ) from None
134
+ meta = NgioLabelsGroupMeta(labels=[], version=ngff_version)
135
+ update_ngio_labels_group_meta(
136
+ group_handler=group_handler,
137
+ ngio_meta=meta,
125
138
  )
139
+ self._group_handler = self._group_handler.reopen_handler()
140
+ self._meta_handler = LabelsGroupMetaHandler(group_handler)
141
+
142
+ @property
143
+ def meta(self) -> NgioLabelsGroupMeta:
144
+ """Return the metadata."""
145
+ meta = self._meta_handler.get_meta()
146
+ return meta
126
147
 
127
148
  def list(self) -> list[str]:
128
149
  """Create the /labels group if it doesn't exist."""
129
- attrs = self._group_handler.load_attrs()
130
- return attrs.get("labels", [])
150
+ return self.meta.labels
131
151
 
132
152
  def get(
133
153
  self,
@@ -153,49 +173,81 @@ class LabelsContainer:
153
173
  f"Available labels: {self.list()}"
154
174
  )
155
175
 
156
- group_handler = self._group_handler.derive_handler(name)
157
- label_meta_handler = find_label_meta_handler(group_handler)
158
- path = label_meta_handler.meta.get_dataset(
159
- path=path, pixel_size=pixel_size, strict=strict
160
- ).path
176
+ group_handler = self._group_handler.get_handler(name)
177
+ label_meta_handler = LabelMetaHandler(group_handler)
178
+ path = (
179
+ label_meta_handler.get_meta()
180
+ .get_dataset(path=path, pixel_size=pixel_size, strict=strict)
181
+ .path
182
+ )
161
183
  return Label(group_handler, path, label_meta_handler)
162
184
 
163
185
  def derive(
164
186
  self,
165
187
  name: str,
166
188
  ref_image: Image | Label,
189
+ # Metadata parameters
167
190
  shape: Sequence[int] | None = None,
168
- pixel_size: PixelSize | None = None,
169
- axes_names: Sequence[str] | None = None,
170
- chunks: Sequence[int] | None = None,
171
- dtype: str = "uint32",
191
+ pixelsize: float | tuple[float, float] | None = None,
192
+ z_spacing: float | None = None,
193
+ time_spacing: float | None = None,
194
+ channels_policy: Literal["same", "squeeze", "singleton"] | int = "squeeze",
195
+ ngff_version: NgffVersions | None = None,
196
+ # Zarr Array parameters
197
+ chunks: ChunksLike = "auto",
198
+ shards: ShardsLike | None = None,
199
+ dtype: str | None = None,
172
200
  dimension_separator: Literal[".", "/"] | None = None,
173
201
  compressors: CompressorLike | None = None,
202
+ extra_array_kwargs: Mapping[str, Any] | None = None,
174
203
  overwrite: bool = False,
204
+ # Deprecated arguments
205
+ labels: Sequence[str] | None = None,
206
+ pixel_size: PixelSize | None = None,
175
207
  ) -> "Label":
176
- """Create an empty OME-Zarr label from a reference image.
208
+ """Create an empty OME-Zarr image from an existing image.
177
209
 
178
- And add the label to the /labels group.
210
+ If a kwarg is not provided, the value from the reference image will be used.
179
211
 
180
212
  Args:
181
213
  store (StoreOrGroup): The Zarr store or group to create the image in.
182
- ref_image (Image | Label): A reference image that will be used to create
183
- the new image.
184
- name (str): The name of the new image.
214
+ ref_image (Image | Label): The reference image to derive the new image from.
185
215
  shape (Sequence[int] | None): The shape of the new image.
186
- pixel_size (PixelSize | None): The pixel size of the new image.
216
+ pixelsize (float | tuple[float, float] | None): The pixel size of the new
217
+ image.
218
+ z_spacing (float | None): The z spacing of the new image.
219
+ time_spacing (float | None): The time spacing of the new image.
220
+ scaling_factors (Sequence[float] | Literal["auto"] | None): The scaling
221
+ factors of the new image.
187
222
  axes_names (Sequence[str] | None): The axes names of the new image.
188
- For labels, the channel axis is not allowed.
223
+ name (str | None): The name of the new image.
224
+ channels_meta (Sequence[str | Channel] | None): The channels metadata
225
+ of the new image.
226
+ channels_policy (Literal["squeeze", "same", "singleton"] | int):
227
+ Possible policies:
228
+ - If "squeeze", the channels axis will be removed (no matter its size).
229
+ - If "same", the channels axis will be kept as is (if it exists).
230
+ - If "singleton", the channels axis will be set to size 1.
231
+ - If an integer is provided, the channels axis will be changed to have
232
+ that size.
233
+ ngff_version (NgffVersions | None): The NGFF version to use.
189
234
  chunks (Sequence[int] | None): The chunk shape of the new image.
190
- dtype (str): The data type of the new label.
235
+ shards (ShardsLike | None): The shard shape of the new image.
236
+ dtype (str | None): The data type of the new image.
191
237
  dimension_separator (DIMENSION_SEPARATOR | None): The separator to use for
192
- dimensions. If None it will use the same as the reference image.
193
- compressors (CompressorLike | None): The compressors to use. If None it will
194
- use the same as the reference image.
238
+ dimensions.
239
+ compressors (CompressorLike | None): The compressors to use.
240
+ extra_array_kwargs (Mapping[str, Any] | None): Extra arguments to pass to
241
+ the zarr array creation.
195
242
  overwrite (bool): Whether to overwrite an existing image.
243
+ labels (Sequence[str] | None): The labels of the new image.
244
+ This argument is deprecated please use channels_meta instead.
245
+ pixel_size (PixelSize | None): The pixel size of the new image.
246
+ This argument is deprecated please use pixelsize, z_spacing,
247
+ and time_spacing instead.
196
248
 
197
249
  Returns:
198
- Label: The new label.
250
+ Label: The new derived label.
199
251
 
200
252
  """
201
253
  existing_labels = self.list()
@@ -208,131 +260,124 @@ class LabelsContainer:
208
260
  label_group = self._group_handler.get_group(name, create_mode=True)
209
261
 
210
262
  derive_label(
211
- store=label_group,
212
263
  ref_image=ref_image,
213
- name=name,
264
+ store=label_group,
214
265
  shape=shape,
215
- pixel_size=pixel_size,
216
- axes_names=axes_names,
266
+ pixelsize=pixelsize,
267
+ z_spacing=z_spacing,
268
+ time_spacing=time_spacing,
269
+ name=name,
270
+ channels_policy=channels_policy,
271
+ ngff_version=ngff_version,
217
272
  chunks=chunks,
273
+ shards=shards,
218
274
  dtype=dtype,
219
275
  dimension_separator=dimension_separator,
220
276
  compressors=compressors,
277
+ extra_array_kwargs=extra_array_kwargs,
221
278
  overwrite=overwrite,
279
+ labels=labels,
280
+ pixel_size=pixel_size,
222
281
  )
223
282
 
224
283
  if name not in existing_labels:
225
284
  existing_labels.append(name)
226
- self._group_handler.write_attrs({"labels": existing_labels})
227
285
 
286
+ update_meta = NgioLabelsGroupMeta(
287
+ labels=existing_labels, version=self.meta.version
288
+ )
289
+ self._meta_handler.update_meta(update_meta)
228
290
  return self.get(name)
229
291
 
230
292
 
231
293
  def derive_label(
294
+ *,
232
295
  store: StoreOrGroup,
233
296
  ref_image: Image | Label,
234
- name: str,
297
+ # Metadata parameters
235
298
  shape: Sequence[int] | None = None,
236
- pixel_size: PixelSize | None = None,
237
- axes_names: Sequence[str] | None = None,
238
- chunks: Sequence[int] | None = None,
299
+ pixelsize: float | tuple[float, float] | None = None,
300
+ z_spacing: float | None = None,
301
+ time_spacing: float | None = None,
302
+ name: str | None = None,
303
+ channels_policy: Literal["same", "squeeze", "singleton"] | int = "squeeze",
304
+ ngff_version: NgffVersions | None = None,
305
+ # Zarr Array parameters
306
+ chunks: ChunksLike = "auto",
307
+ shards: ShardsLike | None = None,
308
+ dtype: str | None = None,
239
309
  dimension_separator: Literal[".", "/"] | None = None,
240
310
  compressors: CompressorLike | None = None,
241
- dtype: str = "uint32",
311
+ extra_array_kwargs: Mapping[str, Any] | None = None,
242
312
  overwrite: bool = False,
243
- ) -> None:
244
- """Create an empty OME-Zarr label from a reference image.
313
+ # Deprecated arguments
314
+ labels: Sequence[str] | None = None,
315
+ pixel_size: PixelSize | None = None,
316
+ ) -> ZarrGroupHandler:
317
+ """Derive a new OME-Zarr label from an existing image or label.
318
+
319
+ If a kwarg is not provided, the value from the reference image will be used.
245
320
 
246
321
  Args:
247
- store (StoreOrGroup): The Zarr store or group to create the image in.
248
- ref_image (Image | Label): A reference image that will be used to
249
- create the new image.
250
- name (str): The name of the new image.
251
- shape (Sequence[int] | None): The shape of the new image.
252
- pixel_size (PixelSize | None): The pixel size of the new image.
253
- axes_names (Sequence[str] | None): The axes names of the new image.
254
- For labels, the channel axis is not allowed.
255
- chunks (Sequence[int] | None): The chunk shape of the new image.
256
- dtype (str): The data type of the new label.
257
- dimension_separator (DIMENSION_SEPARATOR | None): The separator to use for
258
- dimensions. If None it will use the same as the reference image.
259
- compressors (CompressorLike | None): The compressor to use. If None it will use
260
- the same as the reference image.
261
- overwrite (bool): Whether to overwrite an existing image.
322
+ store (StoreOrGroup): The Zarr store or group to create the label in.
323
+ ref_image (Image | Label): The reference image to derive the new label from.
324
+ shape (Sequence[int] | None): The shape of the new label.
325
+ pixelsize (float | tuple[float, float] | None): The pixel size of the new label.
326
+ z_spacing (float | None): The z spacing of the new label.
327
+ time_spacing (float | None): The time spacing of the new label.
328
+ name (str | None): The name of the new label.
329
+ channels_policy (Literal["squeeze", "same", "singleton"] | int): Possible
330
+ policies:
331
+ - If "squeeze", the channels axis will be removed (no matter its size).
332
+ - If "same", the channels axis will be kept as is (if it exists).
333
+ - If "singleton", the channels axis will be set to size 1.
334
+ - If an integer is provided, the channels axis will be changed to have that
335
+ size.
336
+ ngff_version (NgffVersions | None): The NGFF version to use.
337
+ chunks (ChunksLike): The chunk shape of the new label. Defaults to "auto".
338
+ shards (ShardsLike | None): The shard shape of the new label.
339
+ dtype (str | None): The data type of the new label.
340
+ dimension_separator (Literal[".", "/"] | None): The separator to use for
341
+ dimensions.
342
+ compressors (CompressorLike | None): The compressors to use.
343
+ extra_array_kwargs (Mapping[str, Any] | None): Extra arguments to pass to
344
+ the zarr array creation.
345
+ overwrite (bool): Whether to overwrite an existing label. Defaults to False.
346
+ labels (Sequence[str] | None): Deprecated. This argument is deprecated,
347
+ please use channels_meta instead.
348
+ pixel_size (PixelSize | None): Deprecated. The pixel size of the new label.
349
+ This argument is deprecated, please use pixelsize, z_spacing,
350
+ and time_spacing instead.
262
351
 
263
352
  Returns:
264
- None
353
+ ZarrGroupHandler: The group handler of the new label.
265
354
 
266
355
  """
267
- ref_meta = ref_image.meta
268
-
269
- if shape is None:
270
- shape = ref_image.shape
271
-
272
- if pixel_size is None:
273
- pixel_size = ref_image.pixel_size
274
-
275
- if axes_names is None:
276
- axes_names = ref_meta.axes_handler.axes_names
277
- c_axis = ref_meta.axes_handler.get_index("c")
278
- else:
279
- if "c" in axes_names:
280
- raise NgioValidationError(
281
- "Labels cannot have a channel axis. "
282
- "Please remove the channel axis from the axes names."
283
- )
284
- c_axis = None
285
-
286
- if len(axes_names) != len(shape):
287
- raise NgioValidationError(
288
- "The axes names of the new image does not match the reference image."
289
- f"Got {axes_names} for shape {shape}."
290
- )
291
-
292
- if chunks is None:
293
- chunks = ref_image.chunks
294
-
295
- if len(chunks) != len(shape):
296
- raise NgioValidationError(
297
- "The chunks of the new image does not match the reference image."
298
- f"Got {chunks} for shape {shape}."
299
- )
300
-
301
- if c_axis is not None:
302
- # remove channel if present
303
- shape = list(shape)
304
- shape = shape[:c_axis] + shape[c_axis + 1 :]
305
- chunks = list(chunks)
306
- chunks = chunks[:c_axis] + chunks[c_axis + 1 :]
307
- axes_names = list(axes_names)
308
- axes_names = axes_names[:c_axis] + axes_names[c_axis + 1 :]
309
-
310
- if dimension_separator is None:
311
- dimension_separator = find_dimension_separator(ref_image.zarr_array)
312
- if compressors is None:
313
- compressors = ref_image.zarr_array.compressors # type: ignore
314
-
315
- _ = create_empty_label_container(
356
+ if dtype is None and isinstance(ref_image, Image):
357
+ dtype = "uint32"
358
+ group_handler = abstract_derive(
359
+ ref_image=ref_image,
360
+ meta_type=NgioLabelMeta,
316
361
  store=store,
317
362
  shape=shape,
318
- pixelsize=ref_image.pixel_size.x,
319
- z_spacing=ref_image.pixel_size.z,
320
- time_spacing=ref_image.pixel_size.t,
321
- levels=ref_meta.paths,
322
- yx_scaling_factor=ref_meta.yx_scaling(),
323
- z_scaling_factor=ref_meta.z_scaling(),
324
- time_unit=ref_image.pixel_size.time_unit,
325
- space_unit=ref_image.pixel_size.space_unit,
326
- axes_names=axes_names,
363
+ pixelsize=pixelsize,
364
+ z_spacing=z_spacing,
365
+ time_spacing=time_spacing,
366
+ name=name,
367
+ channels_meta=None,
368
+ channels_policy=channels_policy,
369
+ ngff_version=ngff_version,
327
370
  chunks=chunks,
371
+ shards=shards,
328
372
  dtype=dtype,
329
373
  dimension_separator=dimension_separator,
330
374
  compressors=compressors,
375
+ extra_array_kwargs=extra_array_kwargs,
331
376
  overwrite=overwrite,
332
- ngff_version=ref_meta.version,
333
- name=name,
377
+ labels=labels,
378
+ pixel_size=pixel_size,
334
379
  )
335
- return None
380
+ return group_handler
336
381
 
337
382
 
338
383
  def build_masking_roi_table(label: Label) -> MaskingRoiTable: