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
@@ -403,7 +403,7 @@ def convert_anndata_to_pandas(
403
403
  DataFrame: Converted and normalized pandas DataFrame.
404
404
  """
405
405
  pandas_df = anndata.to_df()
406
- pandas_df[anndata.obs_keys()] = anndata.obs
406
+ pandas_df[anndata.obs.columns.to_list()] = anndata.obs
407
407
  pandas_df = normalize_pandas_df(
408
408
  pandas_df,
409
409
  index_key=index_key,
@@ -4,6 +4,7 @@ This class follows the roi_table specification at:
4
4
  https://fractal-analytics-platform.github.io/fractal-tasks-core/tables/
5
5
  """
6
6
 
7
+ import warnings
7
8
  from collections.abc import Iterable
8
9
  from typing import Literal
9
10
  from uuid import uuid4
@@ -26,7 +27,6 @@ from ngio.utils import (
26
27
  NgioTableValidationError,
27
28
  NgioValueError,
28
29
  ZarrGroupHandler,
29
- ngio_warn,
30
30
  )
31
31
 
32
32
  REQUIRED_COLUMNS = [
@@ -77,7 +77,9 @@ OPTIONAL_COLUMNS = ORIGIN_COLUMNS + TRANSLATION_COLUMNS + PLATE_COLUMNS + INDEX_
77
77
  def _check_optional_columns(col_name: str) -> None:
78
78
  """Check if the column name is in the optional columns."""
79
79
  if col_name not in OPTIONAL_COLUMNS + TIME_COLUMNS:
80
- ngio_warn(f"Column {col_name} is not in the optional columns.")
80
+ warnings.warn(
81
+ f"Column {col_name} is not in the optional columns.", stacklevel=2
82
+ )
81
83
 
82
84
 
83
85
  def _dataframe_to_rois(
@@ -120,17 +122,17 @@ def _dataframe_to_rois(
120
122
  else:
121
123
  label = getattr(row, "label", None)
122
124
 
123
- roi = Roi(
125
+ slices = {
126
+ "x": (row.x_micrometer, row.len_x_micrometer),
127
+ "y": (row.y_micrometer, row.len_y_micrometer),
128
+ "z": (z_micrometer, z_length_micrometer),
129
+ }
130
+ if t_second is not None or t_length_second is not None:
131
+ slices["t"] = (t_second, t_length_second)
132
+ roi = Roi.from_values(
124
133
  name=str(row.Index),
125
- x=row.x_micrometer, # type: ignore (type can not be known here)
126
- y=row.y_micrometer, # type: ignore (type can not be known here)
127
- z=z_micrometer,
128
- t=t_second,
129
- x_length=row.len_x_micrometer, # type: ignore (type can not be known here)
130
- y_length=row.len_y_micrometer, # type: ignore (type can not be known here)
131
- z_length=z_length_micrometer,
132
- t_length=t_length_second,
133
- unit="micrometer",
134
+ slices=slices,
135
+ space="world",
134
136
  label=label,
135
137
  **extras,
136
138
  )
@@ -143,24 +145,39 @@ def _rois_to_dataframe(rois: dict[str, Roi], index_key: str | None) -> pd.DataFr
143
145
  data = []
144
146
  for roi in rois.values():
145
147
  # This normalization is necessary for backward compatibility
146
- z_micrometer = roi.z if roi.z is not None else 0.0
147
- len_z_micrometer = roi.z_length if roi.z_length is not None else 1.0
148
+ if roi.space != "world":
149
+ raise NotImplementedError(
150
+ "Only ROIs in world coordinates can be serialized."
151
+ )
148
152
 
153
+ z_slice = roi.get("z")
154
+ if z_slice is None:
155
+ z_micrometer = 0.0
156
+ len_z_micrometer = 1.0
157
+ else:
158
+ z_micrometer = z_slice.start if z_slice.start is not None else 0.0
159
+ len_z_micrometer = z_slice.length if z_slice.length is not None else 1.0
160
+
161
+ x_slice = roi.get("x")
162
+ if x_slice is None:
163
+ raise NgioValueError("ROI is missing 'x' slice.")
164
+ y_slice = roi.get("y")
165
+ if y_slice is None:
166
+ raise NgioValueError("ROI is missing 'y' slice.")
149
167
  row = {
150
168
  index_key: roi.get_name(),
151
- "x_micrometer": roi.x,
152
- "y_micrometer": roi.y,
169
+ "x_micrometer": x_slice.start if x_slice.start is not None else 0.0,
170
+ "y_micrometer": y_slice.start if y_slice.start is not None else 0.0,
153
171
  "z_micrometer": z_micrometer,
154
- "len_x_micrometer": roi.x_length,
155
- "len_y_micrometer": roi.y_length,
172
+ "len_x_micrometer": x_slice.length if x_slice.length is not None else 1.0,
173
+ "len_y_micrometer": y_slice.length if y_slice.length is not None else 1.0,
156
174
  "len_z_micrometer": len_z_micrometer,
157
175
  }
158
176
 
159
- if roi.t is not None:
160
- row["t_second"] = roi.t
161
-
162
- if roi.t_length is not None:
163
- row["len_t_second"] = roi.t_length
177
+ t_slice = roi.get("t")
178
+ if t_slice is not None:
179
+ row["t_second"] = t_slice.start if t_slice.start is not None else 0.0
180
+ row["len_t_second"] = t_slice.length if t_slice.length is not None else 1.0
164
181
 
165
182
  if roi.label is not None and index_key != "label":
166
183
  row["label"] = roi.label
@@ -183,7 +200,7 @@ class RoiDictWrapper:
183
200
  self._rois_by_name = {}
184
201
  self._rois_by_label = {}
185
202
  for roi in rois:
186
- name = roi.get_name()
203
+ name = roi.name
187
204
  if name in self._rois_by_name:
188
205
  name = f"{name}_{uuid4().hex[:8]}"
189
206
  self._rois_by_name[name] = roi
ngio/utils/__init__.py CHANGED
@@ -1,13 +1,12 @@
1
1
  """Various utilities for the ngio package."""
2
2
 
3
- import os
4
-
5
3
  from ngio.utils._datasets import (
6
4
  download_ome_zarr_dataset,
7
5
  list_ome_zarr_datasets,
8
6
  print_datasets_infos,
9
7
  )
10
8
  from ngio.utils._errors import (
9
+ NgioError,
11
10
  NgioFileExistsError,
12
11
  NgioFileNotFoundError,
13
12
  NgioTableValidationError,
@@ -15,35 +14,32 @@ from ngio.utils._errors import (
15
14
  NgioValueError,
16
15
  )
17
16
  from ngio.utils._fractal_fsspec_store import fractal_fsspec_store
18
- from ngio.utils._logger import ngio_logger, ngio_warn, set_logger_level
19
17
  from ngio.utils._zarr_utils import (
20
18
  AccessModeLiteral,
19
+ NgioCache,
20
+ NgioSupportedStore,
21
21
  StoreOrGroup,
22
22
  ZarrGroupHandler,
23
+ copy_group,
23
24
  open_group_wrapper,
24
25
  )
25
26
 
26
- set_logger_level(os.getenv("NGIO_LOGGER_LEVEL", "WARNING"))
27
-
28
27
  __all__ = [
29
- # Zarr
30
28
  "AccessModeLiteral",
31
- # Errors
29
+ "NgioCache",
30
+ "NgioError",
32
31
  "NgioFileExistsError",
33
32
  "NgioFileNotFoundError",
33
+ "NgioSupportedStore",
34
34
  "NgioTableValidationError",
35
35
  "NgioValidationError",
36
36
  "NgioValueError",
37
37
  "StoreOrGroup",
38
38
  "ZarrGroupHandler",
39
- # Other
39
+ "copy_group",
40
40
  "download_ome_zarr_dataset",
41
41
  "fractal_fsspec_store",
42
42
  "list_ome_zarr_datasets",
43
- # Logger
44
- "ngio_logger",
45
- "ngio_warn",
46
43
  "open_group_wrapper",
47
44
  "print_datasets_infos",
48
- "set_logger_level",
49
45
  ]
ngio/utils/_cache.py ADDED
@@ -0,0 +1,48 @@
1
+ from typing import Generic, TypeVar
2
+
3
+ T = TypeVar("T")
4
+
5
+
6
+ class NgioCache(Generic[T]):
7
+ """A simple cache for NGIO objects."""
8
+
9
+ def __init__(self, use_cache: bool = True):
10
+ self._cache: dict[str, T] = {}
11
+ self._use_cache = use_cache
12
+
13
+ def _cache_sanity_check(self) -> None:
14
+ if len(self._cache) > 0:
15
+ raise RuntimeError(
16
+ "Cache is disabled, but cache contains items. "
17
+ "This indicates a logic error."
18
+ )
19
+
20
+ @property
21
+ def use_cache(self) -> bool:
22
+ return self._use_cache
23
+
24
+ @property
25
+ def cache(self) -> dict[str, T]:
26
+ return self._cache
27
+
28
+ @property
29
+ def is_empty(self) -> bool:
30
+ return len(self._cache) == 0
31
+
32
+ def get(self, key: str, default: T | None = None) -> T | None:
33
+ if not self._use_cache:
34
+ self._cache_sanity_check()
35
+ return default
36
+ return self._cache.get(key, default)
37
+
38
+ def set(self, key: str, value: T, overwrite: bool = True) -> None:
39
+ if not self._use_cache:
40
+ self._cache_sanity_check()
41
+ return
42
+ self._cache[key] = value
43
+
44
+ def clear(self) -> None:
45
+ if not self._use_cache:
46
+ self._cache_sanity_check()
47
+ return
48
+ self._cache.clear()