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.
- ngio/__init__.py +5 -2
- ngio/common/__init__.py +11 -6
- ngio/common/_masking_roi.py +34 -54
- ngio/common/_pyramid.py +322 -75
- ngio/common/_roi.py +258 -330
- ngio/experimental/iterators/_feature.py +3 -3
- ngio/experimental/iterators/_rois_utils.py +10 -11
- ngio/hcs/_plate.py +192 -136
- ngio/images/_abstract_image.py +539 -35
- ngio/images/_create_synt_container.py +45 -47
- ngio/images/_create_utils.py +406 -0
- ngio/images/_image.py +524 -248
- ngio/images/_label.py +257 -180
- ngio/images/_masked_image.py +2 -2
- ngio/images/_ome_zarr_container.py +658 -255
- 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 +6 -8
- ngio/io_pipes/_ops_slices_utils.py +8 -5
- ngio/ome_zarr_meta/__init__.py +29 -18
- ngio/ome_zarr_meta/_meta_handlers.py +402 -689
- ngio/ome_zarr_meta/ngio_specs/__init__.py +4 -0
- ngio/ome_zarr_meta/ngio_specs/_axes.py +152 -51
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +13 -22
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +129 -91
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +69 -69
- ngio/ome_zarr_meta/v04/__init__.py +5 -1
- ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +55 -86
- ngio/ome_zarr_meta/v05/__init__.py +27 -0
- ngio/ome_zarr_meta/v05/_custom_models.py +18 -0
- ngio/ome_zarr_meta/v05/_v05_spec.py +495 -0
- ngio/resources/__init__.py +1 -1
- ngio/resources/resource_model.py +1 -1
- ngio/tables/_tables_container.py +82 -24
- ngio/tables/backends/_abstract_backend.py +7 -0
- ngio/tables/backends/_anndata.py +60 -7
- ngio/tables/backends/_anndata_utils.py +2 -4
- ngio/tables/backends/_csv.py +3 -19
- ngio/tables/backends/_json.py +10 -13
- ngio/tables/backends/_parquet.py +3 -31
- ngio/tables/backends/_py_arrow_backends.py +222 -0
- ngio/tables/backends/_utils.py +1 -1
- ngio/tables/v1/_roi_table.py +41 -24
- ngio/utils/__init__.py +8 -12
- ngio/utils/_cache.py +48 -0
- ngio/utils/_zarr_utils.py +354 -236
- {ngio-0.4.8.dist-info → ngio-0.5.0.dist-info}/METADATA +12 -5
- ngio-0.5.0.dist-info/RECORD +88 -0
- ngio/images/_create.py +0 -276
- ngio/tables/backends/_non_zarr_backends.py +0 -196
- ngio/utils/_logger.py +0 -50
- ngio-0.4.8.dist-info/RECORD +0 -85
- {ngio-0.4.8.dist-info → ngio-0.5.0.dist-info}/WHEEL +0 -0
- {ngio-0.4.8.dist-info → ngio-0.5.0.dist-info}/licenses/LICENSE +0 -0
ngio/tables/backends/_utils.py
CHANGED
|
@@ -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.
|
|
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,
|
ngio/tables/v1/_roi_table.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
|
|
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
|
-
|
|
147
|
-
|
|
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":
|
|
152
|
-
"y_micrometer":
|
|
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":
|
|
155
|
-
"len_y_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,
|
|
156
174
|
"len_z_micrometer": len_z_micrometer,
|
|
157
175
|
}
|
|
158
176
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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()
|