ngio 0.1.6__py3-none-any.whl → 0.2.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 +31 -5
- ngio/common/__init__.py +44 -0
- ngio/common/_array_pipe.py +160 -0
- ngio/common/_axes_transforms.py +63 -0
- ngio/common/_common_types.py +5 -0
- ngio/common/_dimensions.py +113 -0
- ngio/common/_pyramid.py +222 -0
- ngio/{core/roi.py → common/_roi.py} +22 -23
- ngio/common/_slicer.py +97 -0
- ngio/{pipes/_zoom_utils.py → common/_zoom.py} +2 -78
- ngio/hcs/__init__.py +60 -0
- ngio/images/__init__.py +23 -0
- ngio/images/abstract_image.py +240 -0
- ngio/images/create.py +251 -0
- ngio/images/image.py +383 -0
- ngio/images/label.py +96 -0
- ngio/images/omezarr_container.py +512 -0
- ngio/ome_zarr_meta/__init__.py +35 -0
- ngio/ome_zarr_meta/_generic_handlers.py +320 -0
- ngio/ome_zarr_meta/_meta_handlers.py +142 -0
- ngio/ome_zarr_meta/ngio_specs/__init__.py +63 -0
- ngio/ome_zarr_meta/ngio_specs/_axes.py +481 -0
- ngio/ome_zarr_meta/ngio_specs/_channels.py +378 -0
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +134 -0
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +5 -0
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +434 -0
- ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +84 -0
- ngio/ome_zarr_meta/v04/__init__.py +11 -0
- ngio/ome_zarr_meta/v04/_meta_handlers.py +54 -0
- ngio/ome_zarr_meta/v04/_v04_spec_utils.py +412 -0
- ngio/tables/__init__.py +21 -5
- ngio/tables/_validators.py +192 -0
- ngio/tables/backends/__init__.py +8 -0
- ngio/tables/backends/_abstract_backend.py +71 -0
- ngio/tables/backends/_anndata_utils.py +194 -0
- ngio/tables/backends/_anndata_v1.py +75 -0
- ngio/tables/backends/_json_v1.py +56 -0
- ngio/tables/backends/_table_backends.py +102 -0
- ngio/tables/tables_container.py +300 -0
- ngio/tables/v1/__init__.py +6 -5
- ngio/tables/v1/_feature_table.py +161 -0
- ngio/tables/v1/_generic_table.py +99 -182
- ngio/tables/v1/_masking_roi_table.py +175 -0
- ngio/tables/v1/_roi_table.py +226 -0
- ngio/utils/__init__.py +23 -10
- ngio/utils/_datasets.py +51 -0
- ngio/utils/_errors.py +10 -4
- ngio/utils/_zarr_utils.py +378 -0
- {ngio-0.1.6.dist-info → ngio-0.2.0a1.dist-info}/METADATA +18 -39
- ngio-0.2.0a1.dist-info/RECORD +53 -0
- ngio/core/__init__.py +0 -7
- ngio/core/dimensions.py +0 -122
- ngio/core/image_handler.py +0 -228
- ngio/core/image_like_handler.py +0 -549
- ngio/core/label_handler.py +0 -410
- ngio/core/ngff_image.py +0 -387
- ngio/core/utils.py +0 -287
- ngio/io/__init__.py +0 -19
- ngio/io/_zarr.py +0 -88
- ngio/io/_zarr_array_utils.py +0 -0
- ngio/io/_zarr_group_utils.py +0 -60
- ngio/iterators/__init__.py +0 -1
- ngio/ngff_meta/__init__.py +0 -27
- ngio/ngff_meta/fractal_image_meta.py +0 -1267
- ngio/ngff_meta/meta_handler.py +0 -92
- ngio/ngff_meta/utils.py +0 -235
- ngio/ngff_meta/v04/__init__.py +0 -6
- ngio/ngff_meta/v04/specs.py +0 -158
- ngio/ngff_meta/v04/zarr_utils.py +0 -376
- ngio/pipes/__init__.py +0 -7
- ngio/pipes/_slicer_transforms.py +0 -176
- ngio/pipes/_transforms.py +0 -33
- ngio/pipes/data_pipe.py +0 -52
- ngio/tables/_ad_reader.py +0 -80
- ngio/tables/_utils.py +0 -301
- ngio/tables/tables_group.py +0 -252
- ngio/tables/v1/feature_tables.py +0 -182
- ngio/tables/v1/masking_roi_tables.py +0 -243
- ngio/tables/v1/roi_tables.py +0 -285
- ngio/utils/_common_types.py +0 -5
- ngio/utils/_pydantic_utils.py +0 -52
- ngio-0.1.6.dist-info/RECORD +0 -44
- {ngio-0.1.6.dist-info → ngio-0.2.0a1.dist-info}/WHEEL +0 -0
- {ngio-0.1.6.dist-info → ngio-0.2.0a1.dist-info}/licenses/LICENSE +0 -0
ngio/utils/__init__.py
CHANGED
|
@@ -2,29 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
-
from ngio.
|
|
5
|
+
from ngio.common._common_types import ArrayLike
|
|
6
|
+
from ngio.utils._datasets import download_ome_zarr_dataset, list_ome_zarr_datasets
|
|
6
7
|
from ngio.utils._errors import (
|
|
7
8
|
NgioFileExistsError,
|
|
8
9
|
NgioFileNotFoundError,
|
|
9
|
-
NgioNGFFValidationError,
|
|
10
10
|
NgioTableValidationError,
|
|
11
|
+
NgioValidationError,
|
|
12
|
+
NgioValueError,
|
|
11
13
|
)
|
|
12
14
|
from ngio.utils._logger import ngio_logger, set_logger_level
|
|
13
|
-
from ngio.utils.
|
|
15
|
+
from ngio.utils._zarr_utils import (
|
|
16
|
+
AccessModeLiteral,
|
|
17
|
+
StoreOrGroup,
|
|
18
|
+
ZarrGroupHandler,
|
|
19
|
+
open_group_wrapper,
|
|
20
|
+
)
|
|
14
21
|
|
|
15
22
|
set_logger_level(os.getenv("NGIO_LOGGER_LEVEL", "WARNING"))
|
|
16
23
|
|
|
17
24
|
__all__ = [
|
|
25
|
+
# Zarr
|
|
26
|
+
"AccessModeLiteral",
|
|
18
27
|
"ArrayLike",
|
|
19
|
-
# Pydantic
|
|
20
|
-
"BaseWithExtraFields",
|
|
21
|
-
"unique_items_validator",
|
|
22
|
-
# Logger
|
|
23
|
-
"ngio_logger",
|
|
24
|
-
"set_logger_level",
|
|
25
28
|
# Errors
|
|
26
29
|
"NgioFileExistsError",
|
|
27
30
|
"NgioFileNotFoundError",
|
|
28
|
-
"NgioNGFFValidationError",
|
|
29
31
|
"NgioTableValidationError",
|
|
32
|
+
"NgioValidationError",
|
|
33
|
+
"NgioValueError",
|
|
34
|
+
"StoreOrGroup",
|
|
35
|
+
"ZarrGroupHandler",
|
|
36
|
+
# Datasets
|
|
37
|
+
"download_ome_zarr_dataset",
|
|
38
|
+
"list_ome_zarr_datasets",
|
|
39
|
+
# Logger
|
|
40
|
+
"ngio_logger",
|
|
41
|
+
"open_group_wrapper",
|
|
42
|
+
"set_logger_level",
|
|
30
43
|
]
|
ngio/utils/_datasets.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Download testing OME-Zarr datasets."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pooch
|
|
6
|
+
|
|
7
|
+
_ome_zarr_zoo = {
|
|
8
|
+
"CardiomyocyteTiny": {
|
|
9
|
+
"url": "https://zenodo.org/records/13305156/files/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr.zip",
|
|
10
|
+
"known_hash": "md5:efc21fe8d4ea3abab76226d8c166452c",
|
|
11
|
+
"fname": "20200812-CardiomyocyteDifferentiation14-Cycle1.zarr.zip",
|
|
12
|
+
"processor": pooch.Unzip(extract_dir=""),
|
|
13
|
+
},
|
|
14
|
+
"CardiomyocyteSmallMip": {
|
|
15
|
+
"url": "https://zenodo.org/records/13305316/files/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr.zip",
|
|
16
|
+
"known_hash": "md5:3ed3ea898e0ed42d397da2e1dbe40750",
|
|
17
|
+
"fname": "20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr.zip",
|
|
18
|
+
"processor": pooch.Unzip(extract_dir=""),
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def list_ome_zarr_datasets() -> list[str]:
|
|
24
|
+
"""List available OME-Zarr datasets."""
|
|
25
|
+
return list(_ome_zarr_zoo.keys())
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def download_ome_zarr_dataset(
|
|
29
|
+
dataset_name: str,
|
|
30
|
+
download_dir: str = "data",
|
|
31
|
+
) -> str:
|
|
32
|
+
"""Download an OME-Zarr dataset.
|
|
33
|
+
|
|
34
|
+
To list available datasets, use `list_ome_zarr_datasets`.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
dataset_name (str): The dataset name.
|
|
38
|
+
download_dir (str): The download directory. Defaults to "data".
|
|
39
|
+
"""
|
|
40
|
+
if dataset_name not in _ome_zarr_zoo:
|
|
41
|
+
raise ValueError(f"Dataset {dataset_name} not found in the OME-Zarr zoo.")
|
|
42
|
+
ome_zarr_url = _ome_zarr_zoo[dataset_name]
|
|
43
|
+
pooch.retrieve(
|
|
44
|
+
path=download_dir,
|
|
45
|
+
**ome_zarr_url,
|
|
46
|
+
)
|
|
47
|
+
path = Path(download_dir) / ome_zarr_url["fname"]
|
|
48
|
+
|
|
49
|
+
if isinstance(ome_zarr_url["processor"], pooch.Unzip):
|
|
50
|
+
path = path.with_suffix("")
|
|
51
|
+
return path
|
ngio/utils/_errors.py
CHANGED
|
@@ -7,20 +7,20 @@ class NgioError(Exception):
|
|
|
7
7
|
pass
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class NgioFileNotFoundError(NgioError):
|
|
10
|
+
class NgioFileNotFoundError(NgioError, FileNotFoundError):
|
|
11
11
|
"""Error raised when a file is not found."""
|
|
12
12
|
|
|
13
13
|
pass
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
class NgioFileExistsError(NgioError):
|
|
16
|
+
class NgioFileExistsError(NgioError, FileExistsError):
|
|
17
17
|
"""Error raised when a file already exists."""
|
|
18
18
|
|
|
19
19
|
pass
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
class
|
|
23
|
-
"""
|
|
22
|
+
class NgioValidationError(NgioError, ValueError):
|
|
23
|
+
"""Generic error raised when a file does not pass validation."""
|
|
24
24
|
|
|
25
25
|
pass
|
|
26
26
|
|
|
@@ -29,3 +29,9 @@ class NgioTableValidationError(NgioError):
|
|
|
29
29
|
"""Error raised when a table does not pass validation."""
|
|
30
30
|
|
|
31
31
|
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NgioValueError(NgioError, ValueError):
|
|
35
|
+
"""Error raised when a value does not pass a run time test."""
|
|
36
|
+
|
|
37
|
+
pass
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
"""Common utilities for working with Zarr groups in consistent ways."""
|
|
2
|
+
|
|
3
|
+
# %%
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
import fsspec
|
|
8
|
+
import zarr
|
|
9
|
+
from filelock import BaseFileLock, FileLock
|
|
10
|
+
from zarr.errors import ContainsGroupError, GroupNotFoundError
|
|
11
|
+
from zarr.storage import DirectoryStore, FSStore, Store
|
|
12
|
+
|
|
13
|
+
from ngio.utils import NgioFileExistsError, NgioFileNotFoundError, NgioValueError
|
|
14
|
+
from ngio.utils._errors import NgioError
|
|
15
|
+
|
|
16
|
+
AccessModeLiteral = Literal["r", "r+", "w", "w-", "a"]
|
|
17
|
+
# StoreLike is more restrictive than it could be
|
|
18
|
+
# but to make sure we can handle the store correctly
|
|
19
|
+
# we need to be more restrictive
|
|
20
|
+
NgioSupportedStore = str | Path | fsspec.mapping.FSMap | FSStore | DirectoryStore
|
|
21
|
+
GenericStore = Store | NgioSupportedStore
|
|
22
|
+
StoreOrGroup = GenericStore | zarr.Group
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _check_store(store) -> NgioSupportedStore:
|
|
26
|
+
"""Check the store and return a valid store."""
|
|
27
|
+
if isinstance(store, NgioSupportedStore):
|
|
28
|
+
return store
|
|
29
|
+
|
|
30
|
+
raise NotImplementedError(
|
|
31
|
+
f"Store type {type(store)} is not supported. "
|
|
32
|
+
f"Supported types are: {NgioSupportedStore}"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _check_group(group: zarr.Group, mode: AccessModeLiteral) -> zarr.Group:
|
|
37
|
+
"""Check the group and return a valid group."""
|
|
38
|
+
is_read_only = getattr(group, "_read_only", False)
|
|
39
|
+
if is_read_only and mode in ["w", "w-"]:
|
|
40
|
+
raise NgioValueError(
|
|
41
|
+
"The group is read only. Cannot open in write mode ['w', 'w-']"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if mode == "r" and not is_read_only:
|
|
45
|
+
# let's make sure we don't accidentally write to the group
|
|
46
|
+
group = zarr.open_group(store=group.store, path=group.path, mode="r")
|
|
47
|
+
|
|
48
|
+
return group
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def open_group_wrapper(
|
|
52
|
+
store: StoreOrGroup, mode: AccessModeLiteral
|
|
53
|
+
) -> tuple[zarr.Group, NgioSupportedStore]:
|
|
54
|
+
"""Wrapper around zarr.open_group with some additional checks.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
store (StoreOrGroup): The store or group to open.
|
|
58
|
+
mode (ReadOrEdirLiteral): The mode to open the group in.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
zarr.Group: The opened Zarr group.
|
|
62
|
+
"""
|
|
63
|
+
if isinstance(store, zarr.Group):
|
|
64
|
+
group = _check_group(store, mode)
|
|
65
|
+
if hasattr(group, "store_path"):
|
|
66
|
+
_store = group.store_path
|
|
67
|
+
if isinstance(group.store, DirectoryStore):
|
|
68
|
+
_store = group.store.path
|
|
69
|
+
else:
|
|
70
|
+
_store = group.store
|
|
71
|
+
|
|
72
|
+
_store = _check_store(_store)
|
|
73
|
+
return group, _store
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
store = _check_store(store)
|
|
77
|
+
group = zarr.open_group(store=store, mode=mode)
|
|
78
|
+
|
|
79
|
+
except ContainsGroupError as e:
|
|
80
|
+
raise NgioFileExistsError(
|
|
81
|
+
f"A Zarr group already exists at {store}, consider setting overwrite=True."
|
|
82
|
+
) from e
|
|
83
|
+
|
|
84
|
+
except GroupNotFoundError as e:
|
|
85
|
+
raise NgioFileNotFoundError(f"No Zarr group found at {store}") from e
|
|
86
|
+
|
|
87
|
+
return group, store
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ZarrGroupHandler:
|
|
91
|
+
"""A simple wrapper around a Zarr group to handle metadata."""
|
|
92
|
+
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
store: StoreOrGroup,
|
|
96
|
+
cache: bool = False,
|
|
97
|
+
mode: AccessModeLiteral = "a",
|
|
98
|
+
parallel_safe: bool = False,
|
|
99
|
+
parent: "ZarrGroupHandler | None" = None,
|
|
100
|
+
):
|
|
101
|
+
"""Initialize the handler.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
store (StoreOrGroup): The Zarr store or group containing the image data.
|
|
105
|
+
meta_mode (str): The mode of the metadata handler.
|
|
106
|
+
cache (bool): Whether to cache the metadata.
|
|
107
|
+
mode (str): The mode of the store.
|
|
108
|
+
parallel_safe (bool): If True, the handler will create a lock file to make
|
|
109
|
+
that can be used to make the handler parallel safe.
|
|
110
|
+
Be aware that the lock needs to be used manually.
|
|
111
|
+
parent (ZarrGroupHandler | None): The parent handler.
|
|
112
|
+
"""
|
|
113
|
+
if mode not in ["r", "r+", "w", "w-", "a"]:
|
|
114
|
+
raise NgioValueError(f"Mode {mode} is not supported.")
|
|
115
|
+
|
|
116
|
+
if parallel_safe and cache:
|
|
117
|
+
raise NgioValueError(
|
|
118
|
+
"The cache and parallel_safe options are mutually exclusive."
|
|
119
|
+
"If you want to use the lock mechanism, you should not use the cache."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
_group, _store = open_group_wrapper(store, mode)
|
|
123
|
+
|
|
124
|
+
# Make sure the cache is set in the attrs
|
|
125
|
+
# in the same way as the cache in the handler
|
|
126
|
+
_group.attrs.cache = cache
|
|
127
|
+
|
|
128
|
+
if parallel_safe:
|
|
129
|
+
if not isinstance(_store, str | Path):
|
|
130
|
+
raise NgioValueError(
|
|
131
|
+
"The store needs to be a path to use the lock mechanism."
|
|
132
|
+
)
|
|
133
|
+
self._lock_path = f"{_store}.lock"
|
|
134
|
+
self._lock = FileLock(self._lock_path)
|
|
135
|
+
|
|
136
|
+
else:
|
|
137
|
+
self._lock_path = None
|
|
138
|
+
self._lock = None
|
|
139
|
+
|
|
140
|
+
self._group = _group
|
|
141
|
+
self._mode = mode
|
|
142
|
+
self._store = _store
|
|
143
|
+
self.use_cache = cache
|
|
144
|
+
self._parallel_safe = parallel_safe
|
|
145
|
+
self._cache = {}
|
|
146
|
+
self._parent = parent
|
|
147
|
+
|
|
148
|
+
def __repr__(self) -> str:
|
|
149
|
+
"""Return a string representation of the handler."""
|
|
150
|
+
return (
|
|
151
|
+
f"ZarrGroupHandler(full_path={self.full_path}, mode={self.mode}, "
|
|
152
|
+
f"cache={self.use_cache}"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def store(self) -> NgioSupportedStore:
|
|
157
|
+
"""Return the store of the group."""
|
|
158
|
+
return self._store
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def full_path(self) -> str:
|
|
162
|
+
"""Return the store path."""
|
|
163
|
+
return f"{self._store}/{self._group.path}"
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def mode(self) -> AccessModeLiteral:
|
|
167
|
+
"""Return the mode of the group."""
|
|
168
|
+
return self._mode # type: ignore
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def lock(self) -> BaseFileLock | None:
|
|
172
|
+
"""Return the lock."""
|
|
173
|
+
return self._lock
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def parent(self) -> "ZarrGroupHandler | None":
|
|
177
|
+
"""Return the parent handler."""
|
|
178
|
+
return self._parent
|
|
179
|
+
|
|
180
|
+
def remove_lock(self) -> None:
|
|
181
|
+
"""Return the lock."""
|
|
182
|
+
if self._lock is None or self._lock_path is None:
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
lock_path = Path(self._lock_path)
|
|
186
|
+
if lock_path.exists() and self._lock.lock_counter == 0:
|
|
187
|
+
lock_path.unlink()
|
|
188
|
+
self._lock = None
|
|
189
|
+
self._lock_path = None
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
raise NgioValueError("The lock is still in use. Cannot remove it.")
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def group(self) -> zarr.Group:
|
|
196
|
+
"""Return the group."""
|
|
197
|
+
return self._group
|
|
198
|
+
|
|
199
|
+
def add_to_cache(self, key: str, value: object) -> None:
|
|
200
|
+
"""Add an object to the cache."""
|
|
201
|
+
if not self.use_cache:
|
|
202
|
+
return None
|
|
203
|
+
self._cache[key] = value
|
|
204
|
+
|
|
205
|
+
def get_from_cache(self, key: str) -> object | None:
|
|
206
|
+
"""Get an object from the cache."""
|
|
207
|
+
if not self.use_cache:
|
|
208
|
+
return None
|
|
209
|
+
return self._cache.get(key, None)
|
|
210
|
+
|
|
211
|
+
def clean_cache(self) -> None:
|
|
212
|
+
"""Clear the cached metadata."""
|
|
213
|
+
self._cache = {}
|
|
214
|
+
|
|
215
|
+
def load_attrs(self) -> dict:
|
|
216
|
+
"""Load the attributes of the group."""
|
|
217
|
+
attrs = self.get_from_cache("attrs")
|
|
218
|
+
if attrs is not None and isinstance(attrs, dict):
|
|
219
|
+
return attrs
|
|
220
|
+
|
|
221
|
+
attrs = dict(self.group.attrs)
|
|
222
|
+
|
|
223
|
+
self.add_to_cache("attrs", attrs)
|
|
224
|
+
return attrs
|
|
225
|
+
|
|
226
|
+
def _write_attrs(self, attrs: dict, overwrite: bool = False) -> None:
|
|
227
|
+
"""Write the metadata to the store."""
|
|
228
|
+
is_read_only = getattr(self._group, "_read_only", False)
|
|
229
|
+
if is_read_only:
|
|
230
|
+
raise NgioValueError("The group is read only. Cannot write metadata.")
|
|
231
|
+
|
|
232
|
+
# we need to invalidate the current attrs cache
|
|
233
|
+
self.add_to_cache("attrs", None)
|
|
234
|
+
if overwrite:
|
|
235
|
+
self.group.attrs.clear()
|
|
236
|
+
|
|
237
|
+
self.group.attrs.update(attrs)
|
|
238
|
+
|
|
239
|
+
def write_attrs(self, attrs: dict, overwrite: bool = False) -> None:
|
|
240
|
+
"""Write the metadata to the store."""
|
|
241
|
+
# Maybe we should use the lock here
|
|
242
|
+
self._write_attrs(attrs, overwrite)
|
|
243
|
+
|
|
244
|
+
def _obj_get(self, path: str):
|
|
245
|
+
"""Get a group from the group."""
|
|
246
|
+
group_or_array = self.get_from_cache(path)
|
|
247
|
+
if group_or_array is not None:
|
|
248
|
+
return group_or_array
|
|
249
|
+
|
|
250
|
+
group_or_array = self.group.get(path, None)
|
|
251
|
+
self.add_to_cache(path, group_or_array)
|
|
252
|
+
return group_or_array
|
|
253
|
+
|
|
254
|
+
def create_group(self, path: str, overwrite: bool = False) -> zarr.Group:
|
|
255
|
+
"""Create a group in the group."""
|
|
256
|
+
if self.mode == "r":
|
|
257
|
+
raise NgioValueError("Cannot create a group in read only mode.")
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
group = self.group.create_group(path, overwrite=overwrite)
|
|
261
|
+
except ContainsGroupError as e:
|
|
262
|
+
raise NgioFileExistsError(
|
|
263
|
+
f"A Zarr group already exists at {path}, "
|
|
264
|
+
"consider setting overwrite=True."
|
|
265
|
+
) from e
|
|
266
|
+
self.add_to_cache(path, group)
|
|
267
|
+
return group
|
|
268
|
+
|
|
269
|
+
def get_group(
|
|
270
|
+
self,
|
|
271
|
+
path: str,
|
|
272
|
+
create_mode: bool = False,
|
|
273
|
+
) -> zarr.Group:
|
|
274
|
+
"""Get a group from the group.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
path (str): The path to the group.
|
|
278
|
+
create_mode (bool): If True, create the group if it does not exist.
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
zarr.Group: The Zarr group.
|
|
282
|
+
|
|
283
|
+
"""
|
|
284
|
+
group = self._obj_get(path)
|
|
285
|
+
if isinstance(group, zarr.Group):
|
|
286
|
+
return group
|
|
287
|
+
|
|
288
|
+
if group is not None:
|
|
289
|
+
raise NgioValueError(
|
|
290
|
+
f"The object at {path} is not a group, but a {type(group)}"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
if not create_mode:
|
|
294
|
+
raise NgioFileNotFoundError(f"No group found at {path}")
|
|
295
|
+
group = self.create_group(path)
|
|
296
|
+
return group
|
|
297
|
+
|
|
298
|
+
def safe_get_group(
|
|
299
|
+
self, path: str, create_mode: bool = False
|
|
300
|
+
) -> tuple[bool, zarr.Group | NgioError]:
|
|
301
|
+
"""Get a group from the group.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
path (str): The path to the group.
|
|
305
|
+
create_mode (bool): If True, create the group if it does not exist.
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
zarr.Group | None: The Zarr group or None if it does not exist
|
|
309
|
+
or an error occurs.
|
|
310
|
+
|
|
311
|
+
"""
|
|
312
|
+
try:
|
|
313
|
+
return True, self.get_group(path, create_mode)
|
|
314
|
+
except NgioError as e:
|
|
315
|
+
return False, e
|
|
316
|
+
|
|
317
|
+
def get_array(self, path: str) -> zarr.Array:
|
|
318
|
+
"""Get an array from the group."""
|
|
319
|
+
array = self._obj_get(path)
|
|
320
|
+
if array is None:
|
|
321
|
+
raise NgioFileNotFoundError(f"No array found at {path}")
|
|
322
|
+
if not isinstance(array, zarr.Array):
|
|
323
|
+
raise NgioValueError(
|
|
324
|
+
f"The object at {path} is not an array, but a {type(array)}"
|
|
325
|
+
)
|
|
326
|
+
return array
|
|
327
|
+
|
|
328
|
+
def create_array(
|
|
329
|
+
self,
|
|
330
|
+
path: str,
|
|
331
|
+
shape: tuple[int, ...],
|
|
332
|
+
dtype: str,
|
|
333
|
+
chunks: tuple[int, ...] | None = None,
|
|
334
|
+
overwrite: bool = False,
|
|
335
|
+
) -> zarr.Array:
|
|
336
|
+
if self.mode == "r":
|
|
337
|
+
raise NgioValueError("Cannot create an array in read only mode.")
|
|
338
|
+
|
|
339
|
+
try:
|
|
340
|
+
return self.group.zeros(
|
|
341
|
+
name=path,
|
|
342
|
+
shape=shape,
|
|
343
|
+
dtype=dtype,
|
|
344
|
+
chunks=chunks,
|
|
345
|
+
dimension_separator="/",
|
|
346
|
+
overwrite=overwrite,
|
|
347
|
+
)
|
|
348
|
+
except ContainsGroupError as e:
|
|
349
|
+
raise NgioFileExistsError(
|
|
350
|
+
f"A Zarr array already exists at {path}, "
|
|
351
|
+
"consider setting overwrite=True."
|
|
352
|
+
) from e
|
|
353
|
+
except Exception as e:
|
|
354
|
+
raise NgioValueError(f"Error creating array at {path}") from e
|
|
355
|
+
|
|
356
|
+
def derive_handler(
|
|
357
|
+
self,
|
|
358
|
+
path: str,
|
|
359
|
+
) -> "ZarrGroupHandler":
|
|
360
|
+
"""Derive a new handler from the current handler."""
|
|
361
|
+
group = self.get_group(path, create_mode=True)
|
|
362
|
+
return ZarrGroupHandler(
|
|
363
|
+
store=group,
|
|
364
|
+
cache=self.use_cache,
|
|
365
|
+
mode=self.mode,
|
|
366
|
+
parallel_safe=self._parallel_safe,
|
|
367
|
+
parent=self,
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
def safe_derive_handler(
|
|
371
|
+
self,
|
|
372
|
+
path: str,
|
|
373
|
+
) -> tuple[bool, "ZarrGroupHandler | NgioError"]:
|
|
374
|
+
"""Derive a new handler from the current handler."""
|
|
375
|
+
try:
|
|
376
|
+
return True, self.derive_handler(path)
|
|
377
|
+
except NgioError as e:
|
|
378
|
+
return False, e
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ngio
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0a1
|
|
4
4
|
Summary: Next Generation file format IO
|
|
5
5
|
Project-URL: homepage, https://github.com/lorenzocerrone/ngio
|
|
6
6
|
Project-URL: repository, https://github.com/lorenzocerrone/ngio
|
|
@@ -10,54 +10,34 @@ License-File: LICENSE
|
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: License :: OSI Approved :: BSD License
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
16
|
Classifier: Typing :: Typed
|
|
18
|
-
Requires-Python:
|
|
17
|
+
Requires-Python: <3.14,>=3.11
|
|
19
18
|
Requires-Dist: aiohttp
|
|
20
19
|
Requires-Dist: anndata>=0.8.0
|
|
21
20
|
Requires-Dist: dask[array]
|
|
21
|
+
Requires-Dist: dask[distributed]
|
|
22
|
+
Requires-Dist: filelock
|
|
22
23
|
Requires-Dist: numpy
|
|
24
|
+
Requires-Dist: ome-zarr-models
|
|
23
25
|
Requires-Dist: pandas>=1.2.0
|
|
26
|
+
Requires-Dist: pooch
|
|
24
27
|
Requires-Dist: pydantic
|
|
25
28
|
Requires-Dist: requests
|
|
26
|
-
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
Requires-Dist:
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
Requires-Dist:
|
|
38
|
-
Requires-Dist: pdbpp; extra == 'dev2'
|
|
39
|
-
Requires-Dist: plotly; extra == 'dev2'
|
|
40
|
-
Requires-Dist: pre-commit; extra == 'dev2'
|
|
41
|
-
Requires-Dist: pyqt5; extra == 'dev2'
|
|
42
|
-
Requires-Dist: pytest; extra == 'dev2'
|
|
43
|
-
Requires-Dist: pytest-cov; extra == 'dev2'
|
|
44
|
-
Requires-Dist: rich; extra == 'dev2'
|
|
45
|
-
Requires-Dist: ruff; extra == 'dev2'
|
|
46
|
-
Requires-Dist: scikit-image; extra == 'dev2'
|
|
47
|
-
Requires-Dist: zarr<3; extra == 'dev2'
|
|
48
|
-
Provides-Extra: dev3
|
|
49
|
-
Requires-Dist: dask-image; extra == 'dev3'
|
|
50
|
-
Requires-Dist: dask[distributed]; extra == 'dev3'
|
|
51
|
-
Requires-Dist: ipython; extra == 'dev3'
|
|
52
|
-
Requires-Dist: mypy; extra == 'dev3'
|
|
53
|
-
Requires-Dist: notebook; extra == 'dev3'
|
|
54
|
-
Requires-Dist: pdbpp; extra == 'dev3'
|
|
55
|
-
Requires-Dist: pre-commit; extra == 'dev3'
|
|
56
|
-
Requires-Dist: pytest; extra == 'dev3'
|
|
57
|
-
Requires-Dist: pytest-cov; extra == 'dev3'
|
|
58
|
-
Requires-Dist: rich; extra == 'dev3'
|
|
59
|
-
Requires-Dist: ruff; extra == 'dev3'
|
|
60
|
-
Requires-Dist: zarr==v3.0.0-alpha.4; extra == 'dev3'
|
|
29
|
+
Requires-Dist: xarray
|
|
30
|
+
Requires-Dist: zarr<3
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: matplotlib; extra == 'dev'
|
|
33
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
34
|
+
Requires-Dist: napari; extra == 'dev'
|
|
35
|
+
Requires-Dist: notebook; extra == 'dev'
|
|
36
|
+
Requires-Dist: pdbpp; extra == 'dev'
|
|
37
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
38
|
+
Requires-Dist: pyqt5; extra == 'dev'
|
|
39
|
+
Requires-Dist: rich; extra == 'dev'
|
|
40
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
61
41
|
Provides-Extra: docs
|
|
62
42
|
Requires-Dist: mkdocs; extra == 'docs'
|
|
63
43
|
Requires-Dist: mkdocs-autorefs; extra == 'docs'
|
|
@@ -70,7 +50,6 @@ Requires-Dist: scikit-image; extra == 'docs'
|
|
|
70
50
|
Provides-Extra: test
|
|
71
51
|
Requires-Dist: pytest; extra == 'test'
|
|
72
52
|
Requires-Dist: pytest-cov; extra == 'test'
|
|
73
|
-
Requires-Dist: zarr<3; extra == 'test'
|
|
74
53
|
Description-Content-Type: text/markdown
|
|
75
54
|
|
|
76
55
|
# NGIO - Next Generation file format IO
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
ngio/__init__.py,sha256=8FKJLwwoat9eykBHWa-U93PkzIlkQcde-sRZvtOdDYg,991
|
|
2
|
+
ngio/common/__init__.py,sha256=1vyK1i1ueu2BKKI-gaUhEyERVtHOtfKjQuPJOKDbrLA,1140
|
|
3
|
+
ngio/common/_array_pipe.py,sha256=DyYjGTy2L74zBtD6S2jGPsFG4zj7KaY39TwPGWsSf8g,4877
|
|
4
|
+
ngio/common/_axes_transforms.py,sha256=Dmrta3ZT1IgZAWomdUeTZVje8TBF_oU3RMKBv1r4EvM,1989
|
|
5
|
+
ngio/common/_common_types.py,sha256=OkAYNSNjZkixL1MI-HPBVuXamheFBr862uJ4PvTxmhk,129
|
|
6
|
+
ngio/common/_dimensions.py,sha256=t3X2wzqCl3-UeocqIpQgKRdcWYagijtKsaHTQz5bfT4,3680
|
|
7
|
+
ngio/common/_pyramid.py,sha256=rFFIrOtgcDLsUvUX5WMQkQx9ce2MBqyk-hcXHXxxTqY,7203
|
|
8
|
+
ngio/common/_roi.py,sha256=q7HZg2lPbnJ6flKRgK9e4nfOIgkGpDjDcdz-tE8F0Ys,2869
|
|
9
|
+
ngio/common/_slicer.py,sha256=Qk7XXmd4W9T3w55LvFTjAyloyIcS9tt9NTXiDZNwOx8,3076
|
|
10
|
+
ngio/common/_zoom.py,sha256=z2UjgotoDw7pvpeZVrxiGx4PXx65nk1vwQH3osTFACI,5422
|
|
11
|
+
ngio/hcs/__init__.py,sha256=ugnRl22hM9dic5XRsqQr-HCyyyY1qXTqYOAOzyOZm4M,1624
|
|
12
|
+
ngio/images/__init__.py,sha256=aijqG14eyqVgHtnlcKVNx37FRfT6QvKt8V6bwBpX_r8,526
|
|
13
|
+
ngio/images/abstract_image.py,sha256=n4akBHWQul7vmo4OlYtKWZwpsYl8IYg82AThJrvDrxc,7113
|
|
14
|
+
ngio/images/create.py,sha256=DMyiNVmj3uaJmJQqsL9ftzZNvXxeqgZ0-Pjo63WqWoM,8991
|
|
15
|
+
ngio/images/image.py,sha256=23rrwR2rUN9WA_YFoPrlcsSdETQlW6dwNsOeGxu1qRo,12363
|
|
16
|
+
ngio/images/label.py,sha256=fhxMC_pL_BbyE5IyS_86mMuzRWzKckSwUX8_oXHQ3Qk,2871
|
|
17
|
+
ngio/images/omezarr_container.py,sha256=QEcPPX4-dgAY4oEWJAq3KhtMN5yo4R78_ulc9Zvjeis,18439
|
|
18
|
+
ngio/ome_zarr_meta/__init__.py,sha256=9kYdyuCc3ouQCLDyUCB_lCVXUJ69Ls0fYwtodk3GtYI,798
|
|
19
|
+
ngio/ome_zarr_meta/_generic_handlers.py,sha256=4zRm3P1CRKgUQgD5Z2C9j8VVv659DZIifrmzkQ7o31I,10987
|
|
20
|
+
ngio/ome_zarr_meta/_meta_handlers.py,sha256=b6r1zX3SBBTKoQdKfEqvJaSnTOen5t1EPO3rh0I9VH4,4925
|
|
21
|
+
ngio/ome_zarr_meta/ngio_specs/__init__.py,sha256=S2bNEhqrUhp2muDCCiVik3E6HrbaG91Q9mfT9EXnWNs,1370
|
|
22
|
+
ngio/ome_zarr_meta/ngio_specs/_axes.py,sha256=zgAE0-2DHzJfqGqBhyjY7y_6qAgDLdV9iuWz-YsHe9Y,16682
|
|
23
|
+
ngio/ome_zarr_meta/ngio_specs/_channels.py,sha256=KLQAo7eerBXa5NN3-QWSFxeAfM_bvXXaxFjFNM6OMoA,13762
|
|
24
|
+
ngio/ome_zarr_meta/ngio_specs/_dataset.py,sha256=xT4GY2mdIsm6nAP8bXRj5E9-P6rS-iwzcXT_o3pZajo,4696
|
|
25
|
+
ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py,sha256=Gk29MJ4Vn4TLFK4M7X_33BumCLiZWRuXtSeLX6H56s4,115
|
|
26
|
+
ngio/ome_zarr_meta/ngio_specs/_ngio_image.py,sha256=FnVSupNHHsUxlQOfDXQP2fBZqL8BOX7bB_c1gHP0iWk,14703
|
|
27
|
+
ngio/ome_zarr_meta/ngio_specs/_pixel_size.py,sha256=Ny4F0Wa7uVCKdDQhvFJPCQFUEtKP_DpwId8vdVxkVyQ,2777
|
|
28
|
+
ngio/ome_zarr_meta/v04/__init__.py,sha256=0sb_CaJNgC1AOYeR6LDB91oUmSk6edJyQTTo3Lnh5Y4,226
|
|
29
|
+
ngio/ome_zarr_meta/v04/_meta_handlers.py,sha256=aRvuq9ofzQQCB-CJehD3e2T1eKBtZwKg6r2OkpTG1Oo,1768
|
|
30
|
+
ngio/ome_zarr_meta/v04/_v04_spec_utils.py,sha256=_vszk3ORSLObwW_0xbR61ptekvwgFiXsBLZ-5wgWli8,13775
|
|
31
|
+
ngio/tables/__init__.py,sha256=7Mz0kzssaM4k39az__6F9L8s46RaN0Vbw1JkfkysCPU,565
|
|
32
|
+
ngio/tables/_validators.py,sha256=Cg2sFb__G60RwSbNv4n9g_941R4Dzbp_LnaQBdVyaWw,6457
|
|
33
|
+
ngio/tables/tables_container.py,sha256=d7PVerDkAtrhWRmv8BVVE0tbrdugPSxZEa8w9tR5KrA,9508
|
|
34
|
+
ngio/tables/backends/__init__.py,sha256=NlOXmZXDA1kOCVONUyo_aqSjdMHmfqk-3xuKBLXpaUM,217
|
|
35
|
+
ngio/tables/backends/_abstract_backend.py,sha256=JlaHqLjDm2uVnXFWxsHMojNeKo3leg6uauzoFMSF5O4,2256
|
|
36
|
+
ngio/tables/backends/_anndata_utils.py,sha256=xqkm52XMMWC-jJ126oK7x1w7s4Y2jDjtgP8dmHSB3uU,6792
|
|
37
|
+
ngio/tables/backends/_anndata_v1.py,sha256=rzr471-Ae76KFJ0vjgzbGyW2m2Gb0El7JcmnT6mwGJY,2573
|
|
38
|
+
ngio/tables/backends/_json_v1.py,sha256=mT7qJY2U4vhwromZnT9foz_vVtNh_fwmsASGnLZbIac,1897
|
|
39
|
+
ngio/tables/backends/_table_backends.py,sha256=XXtnZqVDbMqFkbUkHbf9IBNz4teCouLpa5fWgEgVtfg,3269
|
|
40
|
+
ngio/tables/v1/__init__.py,sha256=C35WD3vSqB-AcATi7MRhGbCw5Z5VK7gGb8E8cMgL_L0,359
|
|
41
|
+
ngio/tables/v1/_feature_table.py,sha256=WOkFOb0UDxwM-MPZGdv-nLIiqfiGL8etVkMett9iQic,5099
|
|
42
|
+
ngio/tables/v1/_generic_table.py,sha256=KOVzbeUs8AwVvI83Os5gBZky948ucEKjxXl2CGzSQqQ,3483
|
|
43
|
+
ngio/tables/v1/_masking_roi_table.py,sha256=vPlUWGQalxDJ7G4NcSmzsOFqSEIJFXbuCLD_ucBj7ew,5492
|
|
44
|
+
ngio/tables/v1/_roi_table.py,sha256=UMwJEMkOAgDf-80z0qPfIiB6fsQInjzjDlR4OkBYH4o,7147
|
|
45
|
+
ngio/utils/__init__.py,sha256=fhT4CUTk3obQHzjfTcZYOwjZrSuuI1uXQ8qJmyH17iY,1021
|
|
46
|
+
ngio/utils/_datasets.py,sha256=o6-CGwK7tEUtlwrTOayrg08Yc_nYBuKxWCu1K3dAgm4,1668
|
|
47
|
+
ngio/utils/_errors.py,sha256=pKQ12LUjQLYE1nUawemA5h7HsgznjaSvV1n2PQU33N0,759
|
|
48
|
+
ngio/utils/_logger.py,sha256=zvFG-Ta3ZIJxTyY93zYoPGp2A6TTUf7mSO0zr_uFy4A,837
|
|
49
|
+
ngio/utils/_zarr_utils.py,sha256=5Mo8Nfb7oww7rGVqEWUH29VvOkhD3-Despb3c1SlENM,12172
|
|
50
|
+
ngio-0.2.0a1.dist-info/METADATA,sha256=7IwTK3iOC_hzeIa2Q7n7TXvROIboYJjjUMGkW9oFf5c,4804
|
|
51
|
+
ngio-0.2.0a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
52
|
+
ngio-0.2.0a1.dist-info/licenses/LICENSE,sha256=UgN_a1QCeNh9rZWfz-wORQFxE3elQzLWPQaoK6N6fxQ,1502
|
|
53
|
+
ngio-0.2.0a1.dist-info/RECORD,,
|