ngio 0.5.0b7__py3-none-any.whl → 0.5.1__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.
@@ -99,7 +99,14 @@ def compute_slices(segmentation: np.ndarray) -> dict[int, tuple[slice, ...]]:
99
99
 
100
100
 
101
101
  def lazy_compute_slices(segmentation: da.Array) -> dict[int, tuple[slice, ...]]:
102
- """Compute slices for each label in a segmentation."""
102
+ """Compute slices for each label in a segmentation using lazy evaluation.
103
+
104
+ Args:
105
+ segmentation: The dask segmentation array.
106
+
107
+ Returns:
108
+ A dictionary mapping label IDs to their bounding box slices.
109
+ """
103
110
  global_offsets = _compute_offsets(segmentation.chunks)
104
111
  delayed_chunks = segmentation.to_delayed() # type: ignore
105
112
 
@@ -120,12 +127,18 @@ def compute_masking_roi(
120
127
  pixel_size: PixelSize,
121
128
  axes_order: Sequence[str],
122
129
  ) -> list[Roi]:
123
- """Compute a ROIs for each label in a segmentation.
130
+ """Compute ROIs for each label in a segmentation.
131
+
132
+ This function expects a 2D, 3D, or 4D segmentation array.
133
+ The axes order should match the segmentation dimensions.
124
134
 
125
- This function expects a 2D or 3D segmentation array.
126
- And this function expects the axes order to be 'zyx' or 'yx'.
127
- Other axes orders are not supported.
135
+ Args:
136
+ segmentation: The segmentation array (2D, 3D, or 4D).
137
+ pixel_size: The pixel size metadata for coordinate conversion.
138
+ axes_order: The order of axes in the segmentation (e.g., 'zyx' or 'yx').
128
139
 
140
+ Returns:
141
+ A list of Roi objects, one for each unique label in the segmentation.
129
142
  """
130
143
  if segmentation.ndim not in [2, 3, 4]:
131
144
  raise NgioValueError("Only 2D, 3D, and 4D segmentations are supported.")
ngio/hcs/_plate.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """A module for handling the Plate Sequence in an OME-Zarr file."""
2
2
 
3
3
  import asyncio
4
- import warnings
4
+ import logging
5
5
  from collections.abc import Sequence
6
6
  from typing import Literal
7
7
 
@@ -48,6 +48,8 @@ from ngio.utils import (
48
48
  ZarrGroupHandler,
49
49
  )
50
50
 
51
+ logger = logging.getLogger(f"ngio:{__name__}")
52
+
51
53
 
52
54
  def _try_get_table_container(
53
55
  handler: ZarrGroupHandler, create_mode: bool = True
@@ -918,12 +920,10 @@ class OmeZarrPlate:
918
920
 
919
921
  """
920
922
  if check_type is not None:
921
- warnings.warn(
922
- "The 'check_type' argument is deprecated, and will be removed in "
923
- "ngio=0.3. Use 'get_table_as' instead or one of the "
924
- "type specific get_*table() methods.",
925
- DeprecationWarning,
926
- stacklevel=2,
923
+ logger.warning(
924
+ "The 'check_type' argument is deprecated and will be removed in "
925
+ "ngio=0.6. Please use 'get_table_as' instead or one of the "
926
+ "type specific get_*table() methods."
927
927
  )
928
928
  return self.tables_container.get(name=name, strict=False)
929
929
 
@@ -1182,9 +1182,21 @@ def _create_empty_plate_from_meta(
1182
1182
  meta: NgioPlateMeta,
1183
1183
  overwrite: bool = False,
1184
1184
  ) -> ZarrGroupHandler:
1185
- """Create an empty OME-Zarr plate from metadata."""
1185
+ """Create an empty OME-Zarr plate from metadata.
1186
+
1187
+ Args:
1188
+ store: The Zarr store or group to create the plate in.
1189
+ meta: The plate metadata to use.
1190
+ overwrite: Whether to overwrite an existing plate.
1191
+
1192
+ Returns:
1193
+ The ZarrGroupHandler for the created plate.
1194
+ """
1186
1195
  mode = "w" if overwrite else "w-"
1187
- group_handler = ZarrGroupHandler(store=store, cache=True, mode=mode)
1196
+ zarr_format = 2 if meta.plate.version == "0.4" else 3
1197
+ group_handler = ZarrGroupHandler(
1198
+ store=store, cache=True, mode=mode, zarr_format=zarr_format
1199
+ )
1188
1200
  update_ngio_plate_meta(group_handler, meta)
1189
1201
  return group_handler
1190
1202
 
@@ -1211,11 +1223,9 @@ def create_empty_plate(
1211
1223
  overwrite (bool): Whether to overwrite the existing plate.
1212
1224
  """
1213
1225
  if version is not None:
1214
- warnings.warn(
1215
- "The 'version' argument is deprecated, and will be removed in ngio=0.3. "
1216
- "Please use 'ngff_version' instead.",
1217
- DeprecationWarning,
1218
- stacklevel=2,
1226
+ logger.warning(
1227
+ "The 'version' argument is deprecated and will be removed in ngio=0.6. "
1228
+ "Please use 'ngff_version' instead."
1219
1229
  )
1220
1230
  ngff_version = version
1221
1231
  plate_meta = NgioPlateMeta.default_init(
@@ -1268,11 +1278,9 @@ def derive_ome_zarr_plate(
1268
1278
  overwrite (bool): Whether to overwrite the existing plate.
1269
1279
  """
1270
1280
  if version is not None:
1271
- warnings.warn(
1272
- "The 'version' argument is deprecated, and will be removed in ngio=0.3. "
1273
- "Please use 'ngff_version' instead.",
1274
- DeprecationWarning,
1275
- stacklevel=2,
1281
+ logger.warning(
1282
+ "The 'version' argument is deprecated and will be removed in ngio=0.6. "
1283
+ "Please use 'ngff_version' instead."
1276
1284
  )
1277
1285
  ngff_version = version
1278
1286
 
@@ -1333,11 +1341,9 @@ def create_empty_well(
1333
1341
  overwrite (bool): Whether to overwrite the existing well.
1334
1342
  """
1335
1343
  if version is not None:
1336
- warnings.warn(
1337
- "The 'version' argument is deprecated, and will be removed in ngio=0.3. "
1338
- "Please use 'ngff_version' instead.",
1339
- DeprecationWarning,
1340
- stacklevel=2,
1344
+ logger.warning(
1345
+ "The 'version' argument is deprecated and will be removed in ngio=0.6. "
1346
+ "Please use 'ngff_version' instead."
1341
1347
  )
1342
1348
  ngff_version = version
1343
1349
  group_handler = ZarrGroupHandler(
@@ -1,6 +1,6 @@
1
1
  """Generic class to handle Image-like data in a OME-NGFF file."""
2
2
 
3
- import warnings
3
+ import logging
4
4
  from abc import ABC, abstractmethod
5
5
  from collections.abc import Mapping, Sequence
6
6
  from typing import Any, Literal
@@ -52,6 +52,13 @@ from ngio.ome_zarr_meta.ngio_specs import (
52
52
  NgffVersions,
53
53
  NgioLabelMeta,
54
54
  )
55
+ from ngio.ome_zarr_meta.ngio_specs._axes import (
56
+ AxesSetup,
57
+ DefaultSpaceUnit,
58
+ DefaultTimeUnit,
59
+ SpaceUnits,
60
+ TimeUnits,
61
+ )
55
62
  from ngio.tables import RoiTable
56
63
  from ngio.utils import (
57
64
  NgioFileExistsError,
@@ -61,6 +68,8 @@ from ngio.utils import (
61
68
  )
62
69
  from ngio.utils._zarr_utils import find_dimension_separator
63
70
 
71
+ logger = logging.getLogger(f"ngio:{__name__}")
72
+
64
73
 
65
74
  class AbstractImage(ABC):
66
75
  """A class to handle a single image (or level) in an OME-Zarr image.
@@ -136,6 +145,11 @@ class AbstractImage(ABC):
136
145
  """Return the axes handler of the image."""
137
146
  return self.dataset.axes_handler
138
147
 
148
+ @property
149
+ def axes_setup(self) -> AxesSetup:
150
+ """Return the axes setup of the image."""
151
+ return self.axes_handler.axes_setup
152
+
139
153
  @property
140
154
  def axes(self) -> tuple[str, ...]:
141
155
  """Return the axes of the image."""
@@ -205,6 +219,47 @@ class AbstractImage(ABC):
205
219
  """Return True if the image has the given axis."""
206
220
  return self.axes_handler.has_axis(axis)
207
221
 
222
+ def set_axes_unit(
223
+ self,
224
+ space_unit: SpaceUnits = DefaultSpaceUnit,
225
+ time_unit: TimeUnits = DefaultTimeUnit,
226
+ ) -> None:
227
+ """Set the axes unit of the image.
228
+
229
+ Args:
230
+ space_unit (SpaceUnits): The space unit of the image.
231
+ time_unit (TimeUnits): The time unit of the image.
232
+ """
233
+ meta = self._meta_handler.get_meta()
234
+ meta = meta.to_units(space_unit=space_unit, time_unit=time_unit)
235
+ self._meta_handler.update_meta(meta) # type: ignore
236
+
237
+ def set_axes_names(self, axes_names: Sequence[str]) -> None:
238
+ """Set the axes names of the label.
239
+
240
+ Args:
241
+ axes_names (Sequence[str]): The axes names to set.
242
+ """
243
+ meta = self._meta_handler.get_meta()
244
+ meta = meta.rename_axes(axes_names=axes_names)
245
+ self._meta_handler._axes_setup = meta.axes_handler.axes_setup
246
+ self._meta_handler.update_meta(meta) # type: ignore
247
+
248
+ def set_name(
249
+ self,
250
+ name: str,
251
+ ) -> None:
252
+ """Set the name of the image in the metadata.
253
+
254
+ This does not change the group name or any paths.
255
+
256
+ Args:
257
+ name (str): The name of the image.
258
+ """
259
+ meta = self._meta_handler.get_meta()
260
+ meta = meta.rename_image(name=name)
261
+ self._meta_handler.update_meta(meta) # type: ignore
262
+
208
263
  def _get_as_numpy(
209
264
  self,
210
265
  axes_order: Sequence[str] | None = None,
@@ -887,7 +942,7 @@ def abstract_derive(
887
942
  # Deprecated arguments
888
943
  labels: Sequence[str] | None = None,
889
944
  pixel_size: PixelSize | None = None,
890
- ) -> ZarrGroupHandler:
945
+ ) -> tuple[ZarrGroupHandler, AxesSetup]:
891
946
  """Create an empty OME-Zarr image from an existing image.
892
947
 
893
948
  If a kwarg is not provided, the value from the reference image will be used.
@@ -934,19 +989,16 @@ def abstract_derive(
934
989
  """
935
990
  # TODO: remove in ngio 0.6
936
991
  if labels is not None:
937
- warnings.warn(
992
+ logger.warning(
938
993
  "The 'labels' argument is deprecated and will be removed in "
939
- "a future release.",
940
- DeprecationWarning,
941
- stacklevel=2,
994
+ "ngio=0.6. Please use 'channels_meta' instead."
942
995
  )
943
996
  channels_meta = list(labels)
944
997
  if pixel_size is not None:
945
- warnings.warn(
998
+ logger.warning(
946
999
  "The 'pixel_size' argument is deprecated and will be removed in "
947
- "a future release.",
948
- DeprecationWarning,
949
- stacklevel=2,
1000
+ "ngio=0.6. Please use 'pixelsize', 'z_spacing', and 'time_spacing'"
1001
+ "instead."
950
1002
  )
951
1003
  pixelsize = (pixel_size.y, pixel_size.x)
952
1004
  # End of deprecated arguments handling
@@ -1012,7 +1064,7 @@ def abstract_derive(
1012
1064
  channels_meta=channels_meta,
1013
1065
  )
1014
1066
 
1015
- handler = init_image_like_from_shapes(
1067
+ handler, axes_setup = init_image_like_from_shapes(
1016
1068
  store=store,
1017
1069
  meta_type=meta_type,
1018
1070
  shapes=shapes,
@@ -1023,6 +1075,7 @@ def abstract_derive(
1023
1075
  space_unit=ref_image.space_unit,
1024
1076
  axes_names=axes,
1025
1077
  name=name,
1078
+ axes_setup=ref_image.axes_setup,
1026
1079
  channels_meta=channels_meta_,
1027
1080
  chunks=chunks,
1028
1081
  shards=shards,
@@ -1033,4 +1086,4 @@ def abstract_derive(
1033
1086
  ngff_version=ngff_version,
1034
1087
  extra_array_kwargs=extra_array_kwargs,
1035
1088
  )
1036
- return handler
1089
+ return handler, axes_setup
@@ -85,7 +85,7 @@ def create_synthetic_ome_zarr(
85
85
  ome_zarr = create_ome_zarr_from_array(
86
86
  store=store,
87
87
  array=raw,
88
- pixelsize=sample_info.xy_pixelsize,
88
+ pixelsize=sample_info.pixelsize,
89
89
  z_spacing=sample_info.z_spacing,
90
90
  time_spacing=sample_info.time_spacing,
91
91
  levels=levels,
@@ -1,6 +1,6 @@
1
1
  """Utility functions for working with OME-Zarr images."""
2
2
 
3
- import warnings
3
+ import logging
4
4
  from collections.abc import Mapping, Sequence
5
5
  from typing import Any, Literal, TypeVar
6
6
 
@@ -22,6 +22,7 @@ from ngio.ome_zarr_meta.ngio_specs import (
22
22
  NgffVersions,
23
23
  SpaceUnits,
24
24
  TimeUnits,
25
+ build_axes_handler,
25
26
  build_canonical_axes_handler,
26
27
  canonical_axes_order,
27
28
  canonical_label_axes_order,
@@ -29,37 +30,9 @@ from ngio.ome_zarr_meta.ngio_specs import (
29
30
  from ngio.ome_zarr_meta.ngio_specs._axes import AxesSetup
30
31
  from ngio.utils import NgioValueError, StoreOrGroup, ZarrGroupHandler
31
32
 
32
- _image_or_label_meta = TypeVar("_image_or_label_meta", NgioImageMeta, NgioLabelMeta)
33
-
33
+ logger = logging.getLogger(f"ngio:{__name__}")
34
34
 
35
- def _build_axes_handler(
36
- *,
37
- shape: tuple[int, ...],
38
- axes_names: Sequence[str] | None,
39
- default_channel_order: tuple[str, ...],
40
- space_units: SpaceUnits | str | None = DefaultSpaceUnit,
41
- time_units: TimeUnits | str | None = DefaultTimeUnit,
42
- axes_setup: AxesSetup | None = None,
43
- allow_non_canonical_axes: bool = False,
44
- strict_canonical_order: bool = False,
45
- ) -> AxesHandler:
46
- """Compute axes names for given shape."""
47
- if axes_names is None:
48
- axes_names = default_channel_order[-len(shape) :]
49
- # Validate length
50
- if len(axes_names) != len(shape):
51
- raise NgioValueError(
52
- f"Number of axes names {axes_names} does not match the number of "
53
- f"dimensions {shape}."
54
- )
55
- return build_canonical_axes_handler(
56
- axes_names=axes_names,
57
- space_units=space_units,
58
- time_units=time_units,
59
- axes_setup=axes_setup,
60
- allow_non_canonical_axes=allow_non_canonical_axes,
61
- strict_canonical_order=strict_canonical_order,
62
- )
35
+ _image_or_label_meta = TypeVar("_image_or_label_meta", NgioImageMeta, NgioLabelMeta)
63
36
 
64
37
 
65
38
  def _align_to_axes(
@@ -85,9 +58,9 @@ def _check_deprecated_scaling_factors(
85
58
  shape: tuple[int, ...],
86
59
  ) -> Sequence[float] | Literal["auto"]:
87
60
  if yx_scaling_factor is not None or z_scaling_factor is not None:
88
- warnings.warn(
61
+ logger.warning(
89
62
  "The 'yx_scaling_factor' and 'z_scaling_factor' arguments are deprecated "
90
- "and will be removed in future versions. Please use the 'scaling_factors' "
63
+ "and will be removed in ngio=0.6. Please use the 'scaling_factors' "
91
64
  "argument instead.",
92
65
  DeprecationWarning,
93
66
  stacklevel=2,
@@ -231,6 +204,38 @@ def _add_channels_meta(
231
204
  return meta
232
205
 
233
206
 
207
+ def _build_axes_handler(
208
+ *,
209
+ shape: tuple[int, ...],
210
+ meta_type: type[_image_or_label_meta],
211
+ axes_names: Sequence[str] | None = None,
212
+ axes_setup: AxesSetup | None = None,
213
+ space_unit: SpaceUnits | str | None = None,
214
+ time_unit: TimeUnits | str | None = None,
215
+ ) -> AxesHandler:
216
+ """Build axes handler for given shape and axes names."""
217
+ if meta_type is NgioImageMeta:
218
+ canonical_axes_order_ = canonical_axes_order()
219
+ else:
220
+ canonical_axes_order_ = canonical_label_axes_order()
221
+ if axes_names is None:
222
+ axes_names = canonical_axes_order_[-len(shape) :]
223
+
224
+ if axes_setup is None:
225
+ return build_canonical_axes_handler(
226
+ axes_names=axes_names,
227
+ canonical_channel_order=canonical_axes_order_,
228
+ space_unit=space_unit,
229
+ time_unit=time_unit,
230
+ )
231
+ return build_axes_handler(
232
+ axes_names=axes_names,
233
+ axes_setup=axes_setup,
234
+ space_unit=space_unit,
235
+ time_unit=time_unit,
236
+ )
237
+
238
+
234
239
  def init_image_like(
235
240
  *,
236
241
  # Where to create the image
@@ -259,31 +264,27 @@ def init_image_like(
259
264
  extra_array_kwargs: Mapping[str, Any] | None = None,
260
265
  # internal axes configuration for advanced use cases
261
266
  axes_setup: AxesSetup | None = None,
262
- allow_non_canonical_axes: bool = False,
263
- strict_canonical_order: bool = False,
264
267
  # Whether to overwrite existing image
265
268
  overwrite: bool = False,
266
269
  # Deprecated arguments
267
270
  yx_scaling_factor: float | tuple[float, float] | None = None,
268
271
  z_scaling_factor: float | None = None,
269
- ) -> ZarrGroupHandler:
272
+ ) -> tuple[ZarrGroupHandler, AxesSetup]:
270
273
  """Create an empty OME-Zarr image with the given shape and metadata."""
271
274
  shape = tuple(shape)
272
- if meta_type is NgioImageMeta:
273
- default_axes_order = canonical_axes_order()
274
- else:
275
- default_axes_order = canonical_label_axes_order()
276
-
277
275
  axes_handler = _build_axes_handler(
278
276
  shape=shape,
277
+ meta_type=meta_type,
279
278
  axes_names=axes_names,
280
- default_channel_order=default_axes_order,
281
- space_units=space_unit,
282
- time_units=time_unit,
283
279
  axes_setup=axes_setup,
284
- allow_non_canonical_axes=allow_non_canonical_axes,
285
- strict_canonical_order=strict_canonical_order,
280
+ space_unit=space_unit,
281
+ time_unit=time_unit,
286
282
  )
283
+ if len(shape) != len(axes_handler.axes_names):
284
+ raise NgioValueError(
285
+ f"Mismatch between shape {shape} "
286
+ f"and number of axes {len(axes_handler.axes_names)}."
287
+ )
287
288
  base_scale = compute_base_scale(
288
289
  pixelsize=pixelsize,
289
290
  z_spacing=z_spacing,
@@ -327,12 +328,13 @@ def init_image_like(
327
328
  )
328
329
  meta = _add_channels_meta(meta=meta, channels_meta=channels_meta)
329
330
  # Keep this creation at the end to avoid partial creations on errors
330
- return _create_image_like_group(
331
+ image_handler = _create_image_like_group(
331
332
  store=store,
332
333
  pyramid_builder=pyramid_builder,
333
334
  meta=meta,
334
335
  overwrite=overwrite,
335
336
  )
337
+ return image_handler, axes_handler.axes_setup
336
338
 
337
339
 
338
340
  def init_image_like_from_shapes(
@@ -360,28 +362,24 @@ def init_image_like_from_shapes(
360
362
  extra_array_kwargs: Mapping[str, Any] | None = None,
361
363
  # internal axes configuration for advanced use cases
362
364
  axes_setup: AxesSetup | None = None,
363
- allow_non_canonical_axes: bool = False,
364
- strict_canonical_order: bool = False,
365
365
  # Whether to overwrite existing image
366
366
  overwrite: bool = False,
367
- ) -> ZarrGroupHandler:
367
+ ) -> tuple[ZarrGroupHandler, AxesSetup]:
368
368
  """Create an empty OME-Zarr image with the given shape and metadata."""
369
369
  base_shape = shapes[0]
370
- if meta_type is NgioImageMeta:
371
- default_axes_order = canonical_axes_order()
372
- else:
373
- default_axes_order = canonical_label_axes_order()
374
-
375
370
  axes_handler = _build_axes_handler(
376
371
  shape=base_shape,
372
+ meta_type=meta_type,
377
373
  axes_names=axes_names,
378
- default_channel_order=default_axes_order,
379
- space_units=space_unit,
380
- time_units=time_unit,
381
374
  axes_setup=axes_setup,
382
- allow_non_canonical_axes=allow_non_canonical_axes,
383
- strict_canonical_order=strict_canonical_order,
375
+ space_unit=space_unit,
376
+ time_unit=time_unit,
384
377
  )
378
+ if len(base_shape) != len(axes_handler.axes_names):
379
+ raise NgioValueError(
380
+ f"Mismatch between shape {base_shape} "
381
+ f"and number of axes {len(axes_handler.axes_names)}."
382
+ )
385
383
  if levels is None:
386
384
  levels_paths = tuple(str(i) for i in range(len(shapes)))
387
385
  else:
@@ -411,9 +409,10 @@ def init_image_like_from_shapes(
411
409
  )
412
410
  meta = _add_channels_meta(meta=meta, channels_meta=channels_meta)
413
411
  # Keep this creation at the end to avoid partial creations on errors
414
- return _create_image_like_group(
412
+ image_handler = _create_image_like_group(
415
413
  store=store,
416
414
  pyramid_builder=pyramid_builder,
417
415
  meta=meta,
418
416
  overwrite=overwrite,
419
417
  )
418
+ return image_handler, axes_handler.axes_setup