ngio 0.5.0__py3-none-any.whl → 0.5.0a1__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 (54) hide show
  1. ngio/__init__.py +2 -5
  2. ngio/common/__init__.py +6 -11
  3. ngio/common/_masking_roi.py +54 -34
  4. ngio/common/_pyramid.py +87 -321
  5. ngio/common/_roi.py +330 -258
  6. ngio/experimental/iterators/_feature.py +3 -3
  7. ngio/experimental/iterators/_rois_utils.py +11 -10
  8. ngio/hcs/_plate.py +136 -192
  9. ngio/images/_abstract_image.py +35 -539
  10. ngio/images/_create.py +283 -0
  11. ngio/images/_create_synt_container.py +43 -40
  12. ngio/images/_image.py +251 -517
  13. ngio/images/_label.py +172 -249
  14. ngio/images/_masked_image.py +2 -2
  15. ngio/images/_ome_zarr_container.py +241 -644
  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 +8 -6
  21. ngio/io_pipes/_ops_slices_utils.py +5 -8
  22. ngio/ome_zarr_meta/__init__.py +18 -29
  23. ngio/ome_zarr_meta/_meta_handlers.py +708 -392
  24. ngio/ome_zarr_meta/ngio_specs/__init__.py +0 -4
  25. ngio/ome_zarr_meta/ngio_specs/_axes.py +51 -152
  26. ngio/ome_zarr_meta/ngio_specs/_dataset.py +22 -13
  27. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +91 -129
  28. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +68 -57
  29. ngio/ome_zarr_meta/v04/__init__.py +1 -5
  30. ngio/ome_zarr_meta/v04/{_v04_spec.py → _v04_spec_utils.py} +85 -54
  31. ngio/ome_zarr_meta/v05/__init__.py +1 -5
  32. ngio/ome_zarr_meta/v05/{_v05_spec.py → _v05_spec_utils.py} +87 -64
  33. ngio/resources/__init__.py +1 -1
  34. ngio/resources/resource_model.py +1 -1
  35. ngio/tables/_tables_container.py +27 -85
  36. ngio/tables/backends/_anndata.py +8 -58
  37. ngio/tables/backends/_anndata_utils.py +6 -1
  38. ngio/tables/backends/_csv.py +19 -3
  39. ngio/tables/backends/_json.py +13 -10
  40. ngio/tables/backends/_non_zarr_backends.py +196 -0
  41. ngio/tables/backends/_parquet.py +31 -3
  42. ngio/tables/v1/_roi_table.py +27 -44
  43. ngio/utils/__init__.py +12 -8
  44. ngio/utils/_datasets.py +0 -6
  45. ngio/utils/_logger.py +50 -0
  46. ngio/utils/_zarr_utils.py +250 -292
  47. {ngio-0.5.0.dist-info → ngio-0.5.0a1.dist-info}/METADATA +6 -13
  48. ngio-0.5.0a1.dist-info/RECORD +88 -0
  49. {ngio-0.5.0.dist-info → ngio-0.5.0a1.dist-info}/WHEEL +1 -1
  50. ngio/images/_create_utils.py +0 -406
  51. ngio/tables/backends/_py_arrow_backends.py +0 -222
  52. ngio/utils/_cache.py +0 -48
  53. ngio-0.5.0.dist-info/RECORD +0 -88
  54. {ngio-0.5.0.dist-info → ngio-0.5.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -4,7 +4,6 @@ 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
8
7
  from collections.abc import Iterable
9
8
  from typing import Literal
10
9
  from uuid import uuid4
@@ -27,6 +26,7 @@ from ngio.utils import (
27
26
  NgioTableValidationError,
28
27
  NgioValueError,
29
28
  ZarrGroupHandler,
29
+ ngio_warn,
30
30
  )
31
31
 
32
32
  REQUIRED_COLUMNS = [
@@ -77,9 +77,7 @@ 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
- warnings.warn(
81
- f"Column {col_name} is not in the optional columns.", stacklevel=2
82
- )
80
+ ngio_warn(f"Column {col_name} is not in the optional columns.")
83
81
 
84
82
 
85
83
  def _dataframe_to_rois(
@@ -88,10 +86,10 @@ def _dataframe_to_rois(
88
86
  ) -> dict[str, Roi]:
89
87
  """Convert a DataFrame to a WorldCooROI object."""
90
88
  # Validate the columns of the DataFrame
91
- _missing_columns = set(required_columns).difference(set(dataframe.columns))
92
- if len(_missing_columns) != 0:
89
+ _required_columns = set(dataframe.columns).intersection(set(required_columns))
90
+ if len(_required_columns) != len(required_columns):
93
91
  raise NgioTableValidationError(
94
- f"Could not find required columns: {_missing_columns} in the table."
92
+ f"Could not find required columns: {_required_columns} in the table."
95
93
  )
96
94
 
97
95
  extra_columns = set(dataframe.columns).difference(
@@ -122,17 +120,17 @@ def _dataframe_to_rois(
122
120
  else:
123
121
  label = getattr(row, "label", None)
124
122
 
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(
123
+ roi = Roi(
133
124
  name=str(row.Index),
134
- slices=slices,
135
- space="world",
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",
136
134
  label=label,
137
135
  **extras,
138
136
  )
@@ -145,39 +143,24 @@ def _rois_to_dataframe(rois: dict[str, Roi], index_key: str | None) -> pd.DataFr
145
143
  data = []
146
144
  for roi in rois.values():
147
145
  # This normalization is necessary for backward compatibility
148
- if roi.space != "world":
149
- raise NotImplementedError(
150
- "Only ROIs in world coordinates can be serialized."
151
- )
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
152
148
 
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.")
167
149
  row = {
168
150
  index_key: roi.get_name(),
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,
151
+ "x_micrometer": roi.x,
152
+ "y_micrometer": roi.y,
171
153
  "z_micrometer": z_micrometer,
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,
154
+ "len_x_micrometer": roi.x_length,
155
+ "len_y_micrometer": roi.y_length,
174
156
  "len_z_micrometer": len_z_micrometer,
175
157
  }
176
158
 
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
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
181
164
 
182
165
  if roi.label is not None and index_key != "label":
183
166
  row["label"] = roi.label
@@ -200,7 +183,7 @@ class RoiDictWrapper:
200
183
  self._rois_by_name = {}
201
184
  self._rois_by_label = {}
202
185
  for roi in rois:
203
- name = roi.name
186
+ name = roi.get_name()
204
187
  if name in self._rois_by_name:
205
188
  name = f"{name}_{uuid4().hex[:8]}"
206
189
  self._rois_by_name[name] = roi
ngio/utils/__init__.py CHANGED
@@ -1,12 +1,13 @@
1
1
  """Various utilities for the ngio package."""
2
2
 
3
+ import os
4
+
3
5
  from ngio.utils._datasets import (
4
6
  download_ome_zarr_dataset,
5
7
  list_ome_zarr_datasets,
6
8
  print_datasets_infos,
7
9
  )
8
10
  from ngio.utils._errors import (
9
- NgioError,
10
11
  NgioFileExistsError,
11
12
  NgioFileNotFoundError,
12
13
  NgioTableValidationError,
@@ -14,32 +15,35 @@ from ngio.utils._errors import (
14
15
  NgioValueError,
15
16
  )
16
17
  from ngio.utils._fractal_fsspec_store import fractal_fsspec_store
18
+ from ngio.utils._logger import ngio_logger, ngio_warn, set_logger_level
17
19
  from ngio.utils._zarr_utils import (
18
20
  AccessModeLiteral,
19
- NgioCache,
20
- NgioSupportedStore,
21
21
  StoreOrGroup,
22
22
  ZarrGroupHandler,
23
- copy_group,
24
23
  open_group_wrapper,
25
24
  )
26
25
 
26
+ set_logger_level(os.getenv("NGIO_LOGGER_LEVEL", "WARNING"))
27
+
27
28
  __all__ = [
29
+ # Zarr
28
30
  "AccessModeLiteral",
29
- "NgioCache",
30
- "NgioError",
31
+ # Errors
31
32
  "NgioFileExistsError",
32
33
  "NgioFileNotFoundError",
33
- "NgioSupportedStore",
34
34
  "NgioTableValidationError",
35
35
  "NgioValidationError",
36
36
  "NgioValueError",
37
37
  "StoreOrGroup",
38
38
  "ZarrGroupHandler",
39
- "copy_group",
39
+ # Other
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",
43
46
  "open_group_wrapper",
44
47
  "print_datasets_infos",
48
+ "set_logger_level",
45
49
  ]
ngio/utils/_datasets.py CHANGED
@@ -155,11 +155,5 @@ def download_ome_zarr_dataset(
155
155
  path=download_dir,
156
156
  processor=processor,
157
157
  progressbar=progressbar,
158
- # Add User-Agent to avoid 403 errors from Zenodo
159
- downloader=pooch.HTTPDownloader(
160
- headers={
161
- "User-Agent": f"pooch/{pooch.__version__} (https://github.com/BioVisionCenter/ngio)"
162
- }
163
- ),
164
158
  )
165
159
  return processor.output_file()
ngio/utils/_logger.py ADDED
@@ -0,0 +1,50 @@
1
+ import logging
2
+ import time
3
+ from functools import cache
4
+
5
+ from ngio.utils._errors import NgioValueError
6
+
7
+ # Configure the logger
8
+ ngio_logger = logging.getLogger("NgioLogger")
9
+ ngio_logger.setLevel(logging.ERROR)
10
+
11
+ # Set up a console handler with a custom format
12
+ console_handler = logging.StreamHandler()
13
+ formatter = logging.Formatter(
14
+ "%(asctime)s - %(levelname)s - %(name)s - "
15
+ "[%(module)s.%(funcName)s:%(lineno)d]: %(message)s"
16
+ )
17
+ console_handler.setFormatter(formatter)
18
+
19
+ # Add the handler to the logger
20
+ ngio_logger.addHandler(console_handler)
21
+
22
+
23
+ def set_logger_level(level: str) -> None:
24
+ """Set the logger level.
25
+
26
+ Args:
27
+ level: The level to set the logger to.
28
+ Must be one of "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL".
29
+ """
30
+ if level not in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]:
31
+ raise NgioValueError(f"Invalid log level: {level}")
32
+
33
+ ngio_logger.setLevel(level)
34
+
35
+
36
+ @cache
37
+ def _warn(message: str, ttl_hash: int) -> None:
38
+ """Log a warning message with a time-to-live (TTL) hash."""
39
+ ngio_logger.warning(message, stacklevel=3)
40
+
41
+
42
+ def ngio_warn(message: str, cooldown: int = 2) -> None:
43
+ """Log a warning message.
44
+
45
+ Args:
46
+ message: The warning message to log.
47
+ cooldown: The cooldown period in seconds to avoid repeated logging.
48
+ """
49
+ ttl_hash = time.time() // cooldown
50
+ _warn(message, ttl_hash)