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.
- ngio/__init__.py +2 -5
- ngio/common/__init__.py +6 -11
- ngio/common/_masking_roi.py +54 -34
- ngio/common/_pyramid.py +87 -321
- ngio/common/_roi.py +330 -258
- ngio/experimental/iterators/_feature.py +3 -3
- ngio/experimental/iterators/_rois_utils.py +11 -10
- ngio/hcs/_plate.py +136 -192
- ngio/images/_abstract_image.py +35 -539
- ngio/images/_create.py +283 -0
- ngio/images/_create_synt_container.py +43 -40
- ngio/images/_image.py +251 -517
- ngio/images/_label.py +172 -249
- ngio/images/_masked_image.py +2 -2
- ngio/images/_ome_zarr_container.py +241 -644
- ngio/io_pipes/_io_pipes.py +9 -9
- ngio/io_pipes/_io_pipes_masked.py +7 -7
- ngio/io_pipes/_io_pipes_roi.py +6 -6
- ngio/io_pipes/_io_pipes_types.py +3 -3
- ngio/io_pipes/_match_shape.py +8 -6
- ngio/io_pipes/_ops_slices_utils.py +5 -8
- ngio/ome_zarr_meta/__init__.py +18 -29
- ngio/ome_zarr_meta/_meta_handlers.py +708 -392
- ngio/ome_zarr_meta/ngio_specs/__init__.py +0 -4
- ngio/ome_zarr_meta/ngio_specs/_axes.py +51 -152
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +22 -13
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +91 -129
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +68 -57
- ngio/ome_zarr_meta/v04/__init__.py +1 -5
- ngio/ome_zarr_meta/v04/{_v04_spec.py → _v04_spec_utils.py} +85 -54
- ngio/ome_zarr_meta/v05/__init__.py +1 -5
- ngio/ome_zarr_meta/v05/{_v05_spec.py → _v05_spec_utils.py} +87 -64
- ngio/resources/__init__.py +1 -1
- ngio/resources/resource_model.py +1 -1
- ngio/tables/_tables_container.py +27 -85
- ngio/tables/backends/_anndata.py +8 -58
- ngio/tables/backends/_anndata_utils.py +6 -1
- ngio/tables/backends/_csv.py +19 -3
- ngio/tables/backends/_json.py +13 -10
- ngio/tables/backends/_non_zarr_backends.py +196 -0
- ngio/tables/backends/_parquet.py +31 -3
- ngio/tables/v1/_roi_table.py +27 -44
- ngio/utils/__init__.py +12 -8
- ngio/utils/_datasets.py +0 -6
- ngio/utils/_logger.py +50 -0
- ngio/utils/_zarr_utils.py +250 -292
- {ngio-0.5.0.dist-info → ngio-0.5.0a1.dist-info}/METADATA +6 -13
- ngio-0.5.0a1.dist-info/RECORD +88 -0
- {ngio-0.5.0.dist-info → ngio-0.5.0a1.dist-info}/WHEEL +1 -1
- ngio/images/_create_utils.py +0 -406
- ngio/tables/backends/_py_arrow_backends.py +0 -222
- ngio/utils/_cache.py +0 -48
- ngio-0.5.0.dist-info/RECORD +0 -88
- {ngio-0.5.0.dist-info → ngio-0.5.0a1.dist-info}/licenses/LICENSE +0 -0
ngio/tables/v1/_roi_table.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
92
|
-
if len(
|
|
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: {
|
|
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
|
-
|
|
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
|
-
|
|
135
|
-
|
|
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.
|
|
149
|
-
|
|
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":
|
|
170
|
-
"y_micrometer":
|
|
151
|
+
"x_micrometer": roi.x,
|
|
152
|
+
"y_micrometer": roi.y,
|
|
171
153
|
"z_micrometer": z_micrometer,
|
|
172
|
-
"len_x_micrometer":
|
|
173
|
-
"len_y_micrometer":
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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)
|