ngio 0.1.6__py3-none-any.whl → 0.2.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 +33 -5
- ngio/common/__init__.py +54 -0
- ngio/common/_array_pipe.py +265 -0
- ngio/common/_axes_transforms.py +64 -0
- ngio/common/_common_types.py +5 -0
- ngio/common/_dimensions.py +120 -0
- ngio/common/_masking_roi.py +158 -0
- ngio/common/_pyramid.py +228 -0
- ngio/common/_roi.py +165 -0
- ngio/common/_slicer.py +96 -0
- ngio/{pipes/_zoom_utils.py → common/_zoom.py} +7 -81
- ngio/hcs/__init__.py +5 -0
- ngio/hcs/plate.py +448 -0
- ngio/images/__init__.py +23 -0
- ngio/images/abstract_image.py +349 -0
- ngio/images/create.py +270 -0
- ngio/images/image.py +453 -0
- ngio/images/label.py +285 -0
- ngio/images/masked_image.py +273 -0
- ngio/images/ome_zarr_container.py +738 -0
- ngio/ome_zarr_meta/__init__.py +47 -0
- ngio/ome_zarr_meta/_meta_handlers.py +791 -0
- ngio/ome_zarr_meta/ngio_specs/__init__.py +71 -0
- ngio/ome_zarr_meta/ngio_specs/_axes.py +481 -0
- ngio/ome_zarr_meta/ngio_specs/_channels.py +389 -0
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +134 -0
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +377 -0
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +489 -0
- ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +116 -0
- ngio/ome_zarr_meta/v04/__init__.py +23 -0
- ngio/ome_zarr_meta/v04/_v04_spec_utils.py +485 -0
- ngio/tables/__init__.py +24 -6
- ngio/tables/_validators.py +190 -0
- ngio/tables/backends/__init__.py +8 -0
- ngio/tables/backends/_abstract_backend.py +71 -0
- ngio/tables/backends/_anndata_utils.py +198 -0
- ngio/tables/backends/_anndata_v1.py +76 -0
- ngio/tables/backends/_json_v1.py +56 -0
- ngio/tables/backends/_table_backends.py +102 -0
- ngio/tables/tables_container.py +310 -0
- ngio/tables/v1/__init__.py +5 -5
- ngio/tables/v1/_feature_table.py +182 -0
- ngio/tables/v1/_generic_table.py +160 -179
- ngio/tables/v1/_roi_table.py +366 -0
- ngio/utils/__init__.py +26 -10
- ngio/utils/_datasets.py +53 -0
- ngio/utils/_errors.py +10 -4
- ngio/utils/_fractal_fsspec_store.py +13 -0
- ngio/utils/_logger.py +3 -1
- ngio/utils/_zarr_utils.py +401 -0
- {ngio-0.1.6.dist-info → ngio-0.2.0.dist-info}/METADATA +30 -43
- ngio-0.2.0.dist-info/RECORD +54 -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/roi.py +0 -92
- 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.0.dist-info}/WHEEL +0 -0
- {ngio-0.1.6.dist-info → ngio-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,9 +3,10 @@ from typing import Literal
|
|
|
3
3
|
|
|
4
4
|
import dask.array as da
|
|
5
5
|
import numpy as np
|
|
6
|
-
import zarr
|
|
7
6
|
from scipy.ndimage import zoom as scipy_zoom
|
|
8
7
|
|
|
8
|
+
from ngio.utils import NgioValueError
|
|
9
|
+
|
|
9
10
|
|
|
10
11
|
def _stacked_zoom(x, zoom_y, zoom_x, order=1, mode="grid-constant", grid_mode=True):
|
|
11
12
|
*rest, yshape, xshape = x.shape
|
|
@@ -54,15 +55,15 @@ def _zoom_inputs_check(
|
|
|
54
55
|
target_shape: tuple[int, ...] | None = None,
|
|
55
56
|
) -> tuple[np.ndarray, tuple[int, ...]]:
|
|
56
57
|
if scale is None and target_shape is None:
|
|
57
|
-
raise
|
|
58
|
+
raise NgioValueError("Either scale or target_shape must be provided")
|
|
58
59
|
|
|
59
60
|
if scale is not None and target_shape is not None:
|
|
60
|
-
raise
|
|
61
|
+
raise NgioValueError("Only one of scale or target_shape must be provided")
|
|
61
62
|
|
|
62
63
|
if scale is None:
|
|
63
64
|
assert target_shape is not None, "Target shape must be provided"
|
|
64
65
|
if len(target_shape) != source_array.ndim:
|
|
65
|
-
raise
|
|
66
|
+
raise NgioValueError(
|
|
66
67
|
"Target shape must have the "
|
|
67
68
|
"same number of dimensions as "
|
|
68
69
|
"the source array"
|
|
@@ -76,7 +77,7 @@ def _zoom_inputs_check(
|
|
|
76
77
|
return _scale, _target_shape
|
|
77
78
|
|
|
78
79
|
|
|
79
|
-
def
|
|
80
|
+
def dask_zoom(
|
|
80
81
|
source_array: da.Array,
|
|
81
82
|
scale: tuple[int, ...] | None = None,
|
|
82
83
|
target_shape: tuple[int, ...] | None = None,
|
|
@@ -127,7 +128,7 @@ def _dask_zoom(
|
|
|
127
128
|
return out_array
|
|
128
129
|
|
|
129
130
|
|
|
130
|
-
def
|
|
131
|
+
def numpy_zoom(
|
|
131
132
|
source_array: np.ndarray,
|
|
132
133
|
scale: tuple[int, ...] | None = None,
|
|
133
134
|
target_shape: tuple[int, ...] | None = None,
|
|
@@ -155,78 +156,3 @@ def _numpy_zoom(
|
|
|
155
156
|
)
|
|
156
157
|
assert isinstance(out_array, np.ndarray)
|
|
157
158
|
return out_array
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def on_disk_zoom(
|
|
161
|
-
source: zarr.Array,
|
|
162
|
-
target: zarr.Array,
|
|
163
|
-
order: Literal[0, 1, 2] = 1,
|
|
164
|
-
mode: Literal["dask", "numpy"] = "dask",
|
|
165
|
-
) -> None:
|
|
166
|
-
"""Apply a zoom operation from a source zarr array to a target zarr array.
|
|
167
|
-
|
|
168
|
-
Args:
|
|
169
|
-
source (zarr.Array): The source array to zoom.
|
|
170
|
-
target (zarr.Array): The target array to save the zoomed result to.
|
|
171
|
-
order (Literal[0, 1, 2]): The order of interpolation. Defaults to 1.
|
|
172
|
-
mode (Literal["dask", "numpy"]): The mode to use. Defaults to "dask".
|
|
173
|
-
"""
|
|
174
|
-
if not isinstance(source, zarr.Array):
|
|
175
|
-
raise ValueError("source must be a zarr array")
|
|
176
|
-
|
|
177
|
-
if not isinstance(target, zarr.Array):
|
|
178
|
-
raise ValueError("target must be a zarr array")
|
|
179
|
-
|
|
180
|
-
if source.dtype != target.dtype:
|
|
181
|
-
raise ValueError("source and target must have the same dtype")
|
|
182
|
-
|
|
183
|
-
assert mode in ["dask", "numpy"], "mode must be either 'dask' or 'numpy'"
|
|
184
|
-
|
|
185
|
-
if mode == "numpy":
|
|
186
|
-
target[...] = _numpy_zoom(source[...], target_shape=target.shape, order=order)
|
|
187
|
-
return None
|
|
188
|
-
|
|
189
|
-
source_array = da.from_zarr(source)
|
|
190
|
-
target_array = _dask_zoom(source_array, target_shape=target.shape, order=order)
|
|
191
|
-
|
|
192
|
-
target_array = target_array.rechunk(target.chunks)
|
|
193
|
-
target_array.compute_chunk_sizes()
|
|
194
|
-
target_array.to_zarr(target)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
def on_disk_coarsen(
|
|
198
|
-
source: zarr.Array,
|
|
199
|
-
target: zarr.Array,
|
|
200
|
-
aggregation_function: np.ufunc,
|
|
201
|
-
) -> None:
|
|
202
|
-
"""Apply a coarsening operation from a source zarr array to a target zarr array.
|
|
203
|
-
|
|
204
|
-
Args:
|
|
205
|
-
source (zarr.Array): The source array to coarsen.
|
|
206
|
-
target (zarr.Array): The target array to save the coarsened result to.
|
|
207
|
-
aggregation_function (np.ufunc): The aggregation function to use.
|
|
208
|
-
"""
|
|
209
|
-
source_array = da.from_zarr(source)
|
|
210
|
-
|
|
211
|
-
_scale, _target_shape = _zoom_inputs_check(
|
|
212
|
-
source_array=source_array, scale=None, target_shape=target.shape
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
assert (
|
|
216
|
-
_target_shape == target.shape
|
|
217
|
-
), "Target shape must match the target array shape"
|
|
218
|
-
coarsening_setup = {}
|
|
219
|
-
for i, s in enumerate(_scale):
|
|
220
|
-
factor = 1 / s
|
|
221
|
-
if factor.is_integer():
|
|
222
|
-
coarsening_setup[i] = int(factor)
|
|
223
|
-
else:
|
|
224
|
-
raise ValueError(
|
|
225
|
-
f"Coarsening factor must be an integer, got {factor} on axis {i}"
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
out_target = da.coarsen(
|
|
229
|
-
aggregation_function, source_array, coarsening_setup, trim_excess=True
|
|
230
|
-
)
|
|
231
|
-
out_target = out_target.rechunk(target.chunks)
|
|
232
|
-
out_target.to_zarr(target)
|
ngio/hcs/__init__.py
ADDED
ngio/hcs/plate.py
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
"""A module for handling the Plate Collection in an OME-Zarr file."""
|
|
2
|
+
|
|
3
|
+
from ngio.images import OmeZarrContainer
|
|
4
|
+
from ngio.ome_zarr_meta import (
|
|
5
|
+
ImageInWellPath,
|
|
6
|
+
NgioPlateMeta,
|
|
7
|
+
NgioWellMeta,
|
|
8
|
+
find_plate_meta_handler,
|
|
9
|
+
find_well_meta_handler,
|
|
10
|
+
get_plate_meta_handler,
|
|
11
|
+
get_well_meta_handler,
|
|
12
|
+
)
|
|
13
|
+
from ngio.utils import (
|
|
14
|
+
AccessModeLiteral,
|
|
15
|
+
StoreOrGroup,
|
|
16
|
+
ZarrGroupHandler,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Mock lock class that does nothing
|
|
21
|
+
class MockLock:
|
|
22
|
+
"""A mock lock class that does nothing."""
|
|
23
|
+
|
|
24
|
+
def __enter__(self):
|
|
25
|
+
"""Enter the lock."""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
29
|
+
"""Exit the lock."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class OmeZarrWell:
|
|
34
|
+
"""A class to handle the Well Collection in an OME-Zarr file."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, group_handler: ZarrGroupHandler) -> None:
|
|
37
|
+
"""Initialize the LabelGroupHandler.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
group_handler: The Zarr group handler that contains the Well.
|
|
41
|
+
"""
|
|
42
|
+
self._group_handler = group_handler
|
|
43
|
+
self._meta_handler = find_well_meta_handler(group_handler)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def meta_handler(self):
|
|
47
|
+
"""Return the metadata handler."""
|
|
48
|
+
return self._meta_handler
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def meta(self):
|
|
52
|
+
"""Return the metadata."""
|
|
53
|
+
return self._meta_handler.meta
|
|
54
|
+
|
|
55
|
+
def paths(self, acquisition: int | None = None) -> list[str]:
|
|
56
|
+
"""Return the images paths in the well.
|
|
57
|
+
|
|
58
|
+
If acquisition is None, return all images paths in the well.
|
|
59
|
+
Else, return the images paths in the well for the given acquisition.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
acquisition (int | None): The acquisition id to filter the images.
|
|
63
|
+
"""
|
|
64
|
+
return self.meta.paths(acquisition)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class OmeZarrPlate:
|
|
68
|
+
"""A class to handle the Plate Collection in an OME-Zarr file."""
|
|
69
|
+
|
|
70
|
+
def __init__(self, group_handler: ZarrGroupHandler) -> None:
|
|
71
|
+
"""Initialize the LabelGroupHandler.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
group_handler: The Zarr group handler that contains the Plate.
|
|
75
|
+
"""
|
|
76
|
+
self._group_handler = group_handler
|
|
77
|
+
self._meta_handler = find_plate_meta_handler(group_handler)
|
|
78
|
+
|
|
79
|
+
def __repr__(self) -> str:
|
|
80
|
+
"""Return a string representation of the plate."""
|
|
81
|
+
return f"Plate([rows x columns] ({len(self.rows)} x {len(self.columns)})"
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def meta_handler(self):
|
|
85
|
+
"""Return the metadata handler."""
|
|
86
|
+
return self._meta_handler
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def meta(self):
|
|
90
|
+
"""Return the metadata."""
|
|
91
|
+
return self._meta_handler.meta
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def columns(self) -> list[str]:
|
|
95
|
+
"""Return the number of columns in the plate."""
|
|
96
|
+
return self.meta.columns
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def rows(self) -> list[str]:
|
|
100
|
+
"""Return the number of rows in the plate."""
|
|
101
|
+
return self.meta.rows
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def acquisitions_names(self) -> list[str | None]:
|
|
105
|
+
"""Return the acquisitions in the plate."""
|
|
106
|
+
return self.meta.acquisitions_names
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def acquisitions_ids(self) -> list[int]:
|
|
110
|
+
"""Return the acquisitions ids in the plate."""
|
|
111
|
+
return self.meta.acquisitions_ids
|
|
112
|
+
|
|
113
|
+
def _well_path(self, row: str, column: int | str) -> str:
|
|
114
|
+
"""Return the well path in the plate."""
|
|
115
|
+
return self.meta.get_well_path(row=row, column=column)
|
|
116
|
+
|
|
117
|
+
def _image_path(self, row: str, column: int | str, path: str) -> str:
|
|
118
|
+
"""Return the image path in the plate."""
|
|
119
|
+
well = self.get_well(row, column)
|
|
120
|
+
if path not in well.paths():
|
|
121
|
+
raise ValueError(f"Image {path} does not exist in well {row}{column}")
|
|
122
|
+
return f"{self._well_path(row, column)}/{path}"
|
|
123
|
+
|
|
124
|
+
def wells_paths(self) -> list[str]:
|
|
125
|
+
"""Return the wells paths in the plate."""
|
|
126
|
+
return self.meta.wells_paths
|
|
127
|
+
|
|
128
|
+
def images_paths(self, acquisition: int | None = None) -> list[str]:
|
|
129
|
+
"""Return the images paths in the plate.
|
|
130
|
+
|
|
131
|
+
If acquisition is None, return all images paths in the plate.
|
|
132
|
+
Else, return the images paths in the plate for the given acquisition.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
acquisition (int | None): The acquisition id to filter the images.
|
|
136
|
+
"""
|
|
137
|
+
images = []
|
|
138
|
+
for well_path, wells in self.get_wells().items():
|
|
139
|
+
for img_path in wells.paths(acquisition):
|
|
140
|
+
images.append(f"{well_path}/{img_path}")
|
|
141
|
+
return images
|
|
142
|
+
|
|
143
|
+
def well_images_paths(
|
|
144
|
+
self, row: str, column: int | str, acquisition: int | None = None
|
|
145
|
+
) -> list[str]:
|
|
146
|
+
"""Return the images paths in a well.
|
|
147
|
+
|
|
148
|
+
If acquisition is None, return all images paths in the well.
|
|
149
|
+
Else, return the images paths in the well for the given acquisition.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
row (str): The row of the well.
|
|
153
|
+
column (int | str): The column of the well.
|
|
154
|
+
acquisition (int | None): The acquisition id to filter the images.
|
|
155
|
+
"""
|
|
156
|
+
images = []
|
|
157
|
+
well = self.get_well(row=row, column=column)
|
|
158
|
+
for path in well.paths(acquisition):
|
|
159
|
+
images.append(self._image_path(row=row, column=column, path=path))
|
|
160
|
+
return images
|
|
161
|
+
|
|
162
|
+
def get_well(self, row: str, column: int | str) -> OmeZarrWell:
|
|
163
|
+
"""Get a well from the plate.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
row (str): The row of the well.
|
|
167
|
+
column (int | str): The column of the well.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
OmeZarrWell: The well.
|
|
171
|
+
"""
|
|
172
|
+
well_path = self._well_path(row=row, column=column)
|
|
173
|
+
group_handler = self._group_handler.derive_handler(well_path)
|
|
174
|
+
return OmeZarrWell(group_handler)
|
|
175
|
+
|
|
176
|
+
def get_wells(self) -> dict[str, OmeZarrWell]:
|
|
177
|
+
"""Get all wells in the plate.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
dict[str, OmeZarrWell]: A dictionary of wells, where the key is the well
|
|
181
|
+
path and the value is the well object.
|
|
182
|
+
"""
|
|
183
|
+
wells = {}
|
|
184
|
+
for well_path in self.wells_paths():
|
|
185
|
+
group_handler = self._group_handler.derive_handler(well_path)
|
|
186
|
+
well = OmeZarrWell(group_handler)
|
|
187
|
+
wells[well_path] = well
|
|
188
|
+
return wells
|
|
189
|
+
|
|
190
|
+
def get_images(self, acquisition: int | None = None) -> dict[str, OmeZarrContainer]:
|
|
191
|
+
"""Get all images in the plate.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
acquisition: The acquisition id to filter the images.
|
|
195
|
+
"""
|
|
196
|
+
images = {}
|
|
197
|
+
for image_path in self.images_paths(acquisition):
|
|
198
|
+
img_group_handler = self._group_handler.derive_handler(image_path)
|
|
199
|
+
images[image_path] = OmeZarrContainer(img_group_handler)
|
|
200
|
+
return images
|
|
201
|
+
|
|
202
|
+
def get_image(
|
|
203
|
+
self, row: str, column: int | str, image_path: str
|
|
204
|
+
) -> OmeZarrContainer:
|
|
205
|
+
"""Get an image from the plate.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
row (str): The row of the well.
|
|
209
|
+
column (int | str): The column of the well.
|
|
210
|
+
image_path (str): The path of the image.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
OmeZarrContainer: The image.
|
|
214
|
+
"""
|
|
215
|
+
image_path = self._image_path(row=row, column=column, path=image_path)
|
|
216
|
+
group_handler = self._group_handler.derive_handler(image_path)
|
|
217
|
+
return OmeZarrContainer(group_handler)
|
|
218
|
+
|
|
219
|
+
def get_well_images(
|
|
220
|
+
self, row: str, column: str | int, acquisition: int | None = None
|
|
221
|
+
) -> dict[str, OmeZarrContainer]:
|
|
222
|
+
"""Get all images in a well.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
row: The row of the well.
|
|
226
|
+
column: The column of the well.
|
|
227
|
+
acquisition: The acquisition id to filter the images.
|
|
228
|
+
"""
|
|
229
|
+
images = {}
|
|
230
|
+
for image_paths in self.well_images_paths(
|
|
231
|
+
row=row, column=column, acquisition=acquisition
|
|
232
|
+
):
|
|
233
|
+
group_handler = self._group_handler.derive_handler(image_paths)
|
|
234
|
+
images[image_paths] = OmeZarrContainer(group_handler)
|
|
235
|
+
return images
|
|
236
|
+
|
|
237
|
+
def _add_image(
|
|
238
|
+
self,
|
|
239
|
+
row: str,
|
|
240
|
+
column: int | str,
|
|
241
|
+
image_path: str,
|
|
242
|
+
acquisition_id: int | None = None,
|
|
243
|
+
acquisition_name: str | None = None,
|
|
244
|
+
atomic: bool = False,
|
|
245
|
+
) -> StoreOrGroup:
|
|
246
|
+
"""Add an image to an ome-zarr plate."""
|
|
247
|
+
if atomic:
|
|
248
|
+
plate_lock = self._group_handler.lock
|
|
249
|
+
else:
|
|
250
|
+
plate_lock = MockLock()
|
|
251
|
+
|
|
252
|
+
with plate_lock:
|
|
253
|
+
meta = self.meta
|
|
254
|
+
meta = meta.add_well(row, column, acquisition_id, acquisition_name)
|
|
255
|
+
self.meta_handler.write_meta(meta)
|
|
256
|
+
self.meta_handler._group_handler.clean_cache()
|
|
257
|
+
|
|
258
|
+
well_path = self.meta.get_well_path(row=row, column=column)
|
|
259
|
+
group_handler = self._group_handler.derive_handler(well_path)
|
|
260
|
+
|
|
261
|
+
if atomic:
|
|
262
|
+
well_lock = group_handler.lock
|
|
263
|
+
else:
|
|
264
|
+
well_lock = MockLock()
|
|
265
|
+
|
|
266
|
+
with well_lock:
|
|
267
|
+
attrs = group_handler.load_attrs()
|
|
268
|
+
if len(attrs) == 0:
|
|
269
|
+
# Initialize the well metadata
|
|
270
|
+
# if the group is empty
|
|
271
|
+
well_meta = NgioWellMeta.default_init()
|
|
272
|
+
meta_handler = get_well_meta_handler(group_handler, version="0.4")
|
|
273
|
+
else:
|
|
274
|
+
meta_handler = find_well_meta_handler(group_handler)
|
|
275
|
+
well_meta = meta_handler.meta
|
|
276
|
+
|
|
277
|
+
group_handler = self._group_handler.derive_handler(well_path)
|
|
278
|
+
|
|
279
|
+
well_meta = well_meta.add_image(path=image_path, acquisition=acquisition_id)
|
|
280
|
+
meta_handler.write_meta(well_meta)
|
|
281
|
+
meta_handler._group_handler.clean_cache()
|
|
282
|
+
|
|
283
|
+
return group_handler.get_group(image_path, create_mode=True)
|
|
284
|
+
|
|
285
|
+
def atomic_add_image(
|
|
286
|
+
self,
|
|
287
|
+
row: str,
|
|
288
|
+
column: int | str,
|
|
289
|
+
image_path: str,
|
|
290
|
+
acquisition_id: int | None = None,
|
|
291
|
+
acquisition_name: str | None = None,
|
|
292
|
+
) -> StoreOrGroup:
|
|
293
|
+
"""Parallel safe version of add_image."""
|
|
294
|
+
return self._add_image(
|
|
295
|
+
row=row,
|
|
296
|
+
column=column,
|
|
297
|
+
image_path=image_path,
|
|
298
|
+
acquisition_id=acquisition_id,
|
|
299
|
+
acquisition_name=acquisition_name,
|
|
300
|
+
atomic=True,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
def add_image(
|
|
304
|
+
self,
|
|
305
|
+
row: str,
|
|
306
|
+
column: int | str,
|
|
307
|
+
image_path: str,
|
|
308
|
+
acquisition_id: int | None = None,
|
|
309
|
+
acquisition_name: str | None = None,
|
|
310
|
+
) -> StoreOrGroup:
|
|
311
|
+
"""Add an image to an ome-zarr plate."""
|
|
312
|
+
return self._add_image(
|
|
313
|
+
row=row,
|
|
314
|
+
column=column,
|
|
315
|
+
image_path=image_path,
|
|
316
|
+
acquisition_id=acquisition_id,
|
|
317
|
+
acquisition_name=acquisition_name,
|
|
318
|
+
atomic=False,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
def _remove_well(
|
|
322
|
+
self,
|
|
323
|
+
row: str,
|
|
324
|
+
column: int | str,
|
|
325
|
+
atomic: bool = False,
|
|
326
|
+
):
|
|
327
|
+
"""Remove a well from an ome-zarr plate."""
|
|
328
|
+
if atomic:
|
|
329
|
+
plate_lock = self._group_handler.lock
|
|
330
|
+
else:
|
|
331
|
+
plate_lock = MockLock()
|
|
332
|
+
|
|
333
|
+
with plate_lock:
|
|
334
|
+
meta = self.meta
|
|
335
|
+
meta = meta.remove_well(row, column)
|
|
336
|
+
self.meta_handler.write_meta(meta)
|
|
337
|
+
self.meta_handler._group_handler.clean_cache()
|
|
338
|
+
|
|
339
|
+
def _remove_image(
|
|
340
|
+
self,
|
|
341
|
+
row: str,
|
|
342
|
+
column: int | str,
|
|
343
|
+
image_path: str,
|
|
344
|
+
atomic: bool = False,
|
|
345
|
+
):
|
|
346
|
+
"""Remove an image from an ome-zarr plate."""
|
|
347
|
+
well = self.get_well(row, column)
|
|
348
|
+
|
|
349
|
+
if atomic:
|
|
350
|
+
well_lock = well.meta_handler._group_handler.lock
|
|
351
|
+
else:
|
|
352
|
+
well_lock = MockLock()
|
|
353
|
+
|
|
354
|
+
with well_lock:
|
|
355
|
+
well_meta = well.meta
|
|
356
|
+
well_meta = well_meta.remove_image(path=image_path)
|
|
357
|
+
well.meta_handler.write_meta(well_meta)
|
|
358
|
+
well.meta_handler._group_handler.clean_cache()
|
|
359
|
+
if len(well_meta.paths()) == 0:
|
|
360
|
+
self._remove_well(row, column, atomic=atomic)
|
|
361
|
+
|
|
362
|
+
def atomic_remove_image(
|
|
363
|
+
self,
|
|
364
|
+
row: str,
|
|
365
|
+
column: int | str,
|
|
366
|
+
image_path: str,
|
|
367
|
+
):
|
|
368
|
+
"""Parallel safe version of remove_image."""
|
|
369
|
+
return self._remove_image(
|
|
370
|
+
row=row,
|
|
371
|
+
column=column,
|
|
372
|
+
image_path=image_path,
|
|
373
|
+
atomic=True,
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
def remove_image(
|
|
377
|
+
self,
|
|
378
|
+
row: str,
|
|
379
|
+
column: int | str,
|
|
380
|
+
image_path: str,
|
|
381
|
+
):
|
|
382
|
+
"""Remove an image from an ome-zarr plate."""
|
|
383
|
+
return self._remove_image(
|
|
384
|
+
row=row,
|
|
385
|
+
column=column,
|
|
386
|
+
image_path=image_path,
|
|
387
|
+
atomic=False,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def open_ome_zarr_plate(
|
|
392
|
+
store: StoreOrGroup,
|
|
393
|
+
cache: bool = False,
|
|
394
|
+
mode: AccessModeLiteral = "r+",
|
|
395
|
+
parallel_safe: bool = True,
|
|
396
|
+
) -> OmeZarrPlate:
|
|
397
|
+
"""Open an OME-Zarr plate.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
store (StoreOrGroup): The Zarr store or group that stores the plate.
|
|
401
|
+
cache (bool): Whether to use a cache for the zarr group metadata.
|
|
402
|
+
mode (AccessModeLiteral): The
|
|
403
|
+
access mode for the image. Defaults to "r+".
|
|
404
|
+
parallel_safe (bool): Whether the group handler is parallel safe.
|
|
405
|
+
"""
|
|
406
|
+
group_handler = ZarrGroupHandler(
|
|
407
|
+
store=store, cache=cache, mode=mode, parallel_safe=parallel_safe
|
|
408
|
+
)
|
|
409
|
+
return OmeZarrPlate(group_handler)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def create_empty_plate(
|
|
413
|
+
store: StoreOrGroup,
|
|
414
|
+
name: str,
|
|
415
|
+
images: list[ImageInWellPath] | None = None,
|
|
416
|
+
version: str = "0.4",
|
|
417
|
+
cache: bool = False,
|
|
418
|
+
overwrite: bool = False,
|
|
419
|
+
parallel_safe: bool = True,
|
|
420
|
+
) -> OmeZarrPlate:
|
|
421
|
+
"""Initialize and create an empty OME-Zarr plate."""
|
|
422
|
+
mode = "w" if overwrite else "w-"
|
|
423
|
+
group_handler = ZarrGroupHandler(
|
|
424
|
+
store=store, cache=True, mode=mode, parallel_safe=False
|
|
425
|
+
)
|
|
426
|
+
meta_handler = get_plate_meta_handler(group_handler, version=version)
|
|
427
|
+
plate_meta = NgioPlateMeta.default_init(
|
|
428
|
+
name=name,
|
|
429
|
+
version=version,
|
|
430
|
+
)
|
|
431
|
+
meta_handler.write_meta(plate_meta)
|
|
432
|
+
|
|
433
|
+
if images is not None:
|
|
434
|
+
plate = OmeZarrPlate(group_handler)
|
|
435
|
+
for image in images:
|
|
436
|
+
plate.add_image(
|
|
437
|
+
row=image.row,
|
|
438
|
+
column=image.column,
|
|
439
|
+
image_path=image.path,
|
|
440
|
+
acquisition_id=image.acquisition_id,
|
|
441
|
+
acquisition_name=image.acquisition_name,
|
|
442
|
+
)
|
|
443
|
+
return open_ome_zarr_plate(
|
|
444
|
+
store=store,
|
|
445
|
+
cache=cache,
|
|
446
|
+
mode="r+",
|
|
447
|
+
parallel_safe=parallel_safe,
|
|
448
|
+
)
|
ngio/images/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""OME-Zarr object models."""
|
|
2
|
+
|
|
3
|
+
from ngio.images.image import Image, ImagesContainer
|
|
4
|
+
from ngio.images.label import Label, LabelsContainer
|
|
5
|
+
from ngio.images.ome_zarr_container import (
|
|
6
|
+
OmeZarrContainer,
|
|
7
|
+
create_empty_ome_zarr,
|
|
8
|
+
create_ome_zarr_from_array,
|
|
9
|
+
open_image,
|
|
10
|
+
open_ome_zarr_container,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Image",
|
|
15
|
+
"ImagesContainer",
|
|
16
|
+
"Label",
|
|
17
|
+
"LabelsContainer",
|
|
18
|
+
"OmeZarrContainer",
|
|
19
|
+
"create_empty_ome_zarr",
|
|
20
|
+
"create_ome_zarr_from_array",
|
|
21
|
+
"open_image",
|
|
22
|
+
"open_ome_zarr_container",
|
|
23
|
+
]
|