ngio 0.5.0a1__py3-none-any.whl → 0.5.0a3__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 -2
- ngio/common/__init__.py +11 -6
- ngio/common/_masking_roi.py +12 -41
- ngio/common/_pyramid.py +218 -78
- ngio/common/_roi.py +257 -329
- ngio/experimental/iterators/_feature.py +3 -3
- ngio/experimental/iterators/_rois_utils.py +10 -11
- ngio/hcs/_plate.py +114 -123
- ngio/images/_abstract_image.py +417 -35
- ngio/images/_create_synt_container.py +36 -43
- ngio/images/_create_utils.py +423 -0
- ngio/images/_image.py +155 -177
- ngio/images/_label.py +144 -119
- ngio/images/_ome_zarr_container.py +361 -196
- 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 +5 -4
- ngio/io_pipes/_ops_slices_utils.py +8 -5
- ngio/ome_zarr_meta/__init__.py +15 -18
- ngio/ome_zarr_meta/_meta_handlers.py +334 -713
- ngio/ome_zarr_meta/ngio_specs/_axes.py +1 -0
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +13 -22
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +54 -61
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +14 -68
- ngio/ome_zarr_meta/v04/__init__.py +1 -1
- ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +16 -61
- ngio/ome_zarr_meta/v05/__init__.py +1 -1
- ngio/ome_zarr_meta/v05/{_v05_spec_utils.py → _v05_spec.py} +18 -61
- ngio/tables/_tables_container.py +25 -20
- ngio/tables/backends/_anndata.py +57 -8
- ngio/tables/backends/_anndata_utils.py +1 -6
- 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/v1/_roi_table.py +44 -27
- ngio/utils/__init__.py +6 -12
- ngio/utils/_cache.py +48 -0
- ngio/utils/_zarr_utils.py +285 -245
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/METADATA +8 -4
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/RECORD +45 -45
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/WHEEL +1 -1
- ngio/images/_create.py +0 -283
- ngio/tables/backends/_non_zarr_backends.py +0 -196
- ngio/utils/_logger.py +0 -50
- {ngio-0.5.0a1.dist-info → ngio-0.5.0a3.dist-info}/licenses/LICENSE +0 -0
ngio/__init__.py
CHANGED
|
@@ -9,7 +9,7 @@ except PackageNotFoundError: # pragma: no cover
|
|
|
9
9
|
__author__ = "Lorenzo Cerrone"
|
|
10
10
|
__email__ = "lorenzo.cerrone@uzh.ch"
|
|
11
11
|
|
|
12
|
-
from ngio.common import Dimensions, Roi,
|
|
12
|
+
from ngio.common import Dimensions, Roi, RoiSlice
|
|
13
13
|
from ngio.hcs import (
|
|
14
14
|
OmeZarrPlate,
|
|
15
15
|
OmeZarrWell,
|
|
@@ -52,7 +52,7 @@ __all__ = [
|
|
|
52
52
|
"OmeZarrWell",
|
|
53
53
|
"PixelSize",
|
|
54
54
|
"Roi",
|
|
55
|
-
"
|
|
55
|
+
"RoiSlice",
|
|
56
56
|
"create_empty_ome_zarr",
|
|
57
57
|
"create_empty_plate",
|
|
58
58
|
"create_empty_well",
|
ngio/common/__init__.py
CHANGED
|
@@ -2,22 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
from ngio.common._dimensions import Dimensions
|
|
4
4
|
from ngio.common._masking_roi import compute_masking_roi
|
|
5
|
-
from ngio.common._pyramid import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
from ngio.common._pyramid import (
|
|
6
|
+
ChunksLike,
|
|
7
|
+
ImagePyramidBuilder,
|
|
8
|
+
ShardsLike,
|
|
9
|
+
consolidate_pyramid,
|
|
10
|
+
on_disk_zoom,
|
|
9
11
|
)
|
|
12
|
+
from ngio.common._roi import Roi, RoiSlice
|
|
10
13
|
from ngio.common._zoom import InterpolationOrder, dask_zoom, numpy_zoom
|
|
11
14
|
|
|
12
15
|
__all__ = [
|
|
16
|
+
"ChunksLike",
|
|
13
17
|
"Dimensions",
|
|
18
|
+
"ImagePyramidBuilder",
|
|
14
19
|
"InterpolationOrder",
|
|
15
20
|
"Roi",
|
|
16
|
-
"
|
|
21
|
+
"RoiSlice",
|
|
22
|
+
"ShardsLike",
|
|
17
23
|
"compute_masking_roi",
|
|
18
24
|
"consolidate_pyramid",
|
|
19
25
|
"dask_zoom",
|
|
20
|
-
"init_empty_pyramid",
|
|
21
26
|
"numpy_zoom",
|
|
22
27
|
"on_disk_zoom",
|
|
23
28
|
]
|
ngio/common/_masking_roi.py
CHANGED
|
@@ -7,7 +7,7 @@ import numpy as np
|
|
|
7
7
|
import scipy.ndimage as ndi
|
|
8
8
|
from dask.delayed import delayed
|
|
9
9
|
|
|
10
|
-
from ngio.common._roi import Roi
|
|
10
|
+
from ngio.common._roi import Roi
|
|
11
11
|
from ngio.ome_zarr_meta import PixelSize
|
|
12
12
|
from ngio.utils import NgioValueError
|
|
13
13
|
|
|
@@ -135,52 +135,23 @@ def compute_masking_roi(
|
|
|
135
135
|
rois = []
|
|
136
136
|
for label, slice_ in slices.items():
|
|
137
137
|
if len(slice_) == 2:
|
|
138
|
-
|
|
139
|
-
min_z, max_z = None, None
|
|
140
|
-
min_y, min_x = slice_[0].start, slice_[1].start
|
|
141
|
-
max_y, max_x = slice_[0].stop, slice_[1].stop
|
|
138
|
+
slices = {"y": slice_[0], "x": slice_[1]}
|
|
142
139
|
elif len(slice_) == 3:
|
|
143
|
-
|
|
144
|
-
min_z, min_y, min_x = slice_[0].start, slice_[1].start, slice_[2].start
|
|
145
|
-
max_z, max_y, max_x = slice_[0].stop, slice_[1].stop, slice_[2].stop
|
|
140
|
+
slices = {"z": slice_[0], "y": slice_[1], "x": slice_[2]}
|
|
146
141
|
elif len(slice_) == 4:
|
|
147
|
-
|
|
148
|
-
slice_[0]
|
|
149
|
-
slice_[1]
|
|
150
|
-
slice_[2]
|
|
151
|
-
slice_[3]
|
|
152
|
-
|
|
153
|
-
max_t, max_z, max_y, max_x = (
|
|
154
|
-
slice_[0].stop,
|
|
155
|
-
slice_[1].stop,
|
|
156
|
-
slice_[2].stop,
|
|
157
|
-
slice_[3].stop,
|
|
158
|
-
)
|
|
142
|
+
slices = {
|
|
143
|
+
"t": slice_[0],
|
|
144
|
+
"z": slice_[1],
|
|
145
|
+
"y": slice_[2],
|
|
146
|
+
"x": slice_[3],
|
|
147
|
+
}
|
|
159
148
|
else:
|
|
160
149
|
raise ValueError("Invalid slice length.")
|
|
161
150
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
else:
|
|
165
|
-
t_length = max_t - min_t
|
|
166
|
-
|
|
167
|
-
if max_z is None:
|
|
168
|
-
z_length = None
|
|
169
|
-
else:
|
|
170
|
-
z_length = max_z - min_z
|
|
171
|
-
|
|
172
|
-
roi = RoiPixels(
|
|
173
|
-
name=str(label),
|
|
174
|
-
x_length=max_x - min_x,
|
|
175
|
-
y_length=max_y - min_y,
|
|
176
|
-
z_length=z_length,
|
|
177
|
-
t_length=t_length,
|
|
178
|
-
x=min_x,
|
|
179
|
-
y=min_y,
|
|
180
|
-
z=min_z,
|
|
181
|
-
label=label,
|
|
151
|
+
roi = Roi.from_values(
|
|
152
|
+
name=str(label), slices=slices, label=label, space="pixel"
|
|
182
153
|
)
|
|
183
154
|
|
|
184
|
-
roi = roi.
|
|
155
|
+
roi = roi.to_world(pixel_size=pixel_size)
|
|
185
156
|
rois.append(roi)
|
|
186
157
|
return rois
|
ngio/common/_pyramid.py
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
from
|
|
3
|
-
from typing import Literal
|
|
1
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
2
|
+
from typing import Any, Literal
|
|
4
3
|
|
|
5
4
|
import dask.array as da
|
|
6
5
|
import numpy as np
|
|
7
6
|
import zarr
|
|
8
|
-
from
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, model_validator
|
|
9
8
|
|
|
10
|
-
# from zarr.types import DIMENSION_SEPARATOR
|
|
11
9
|
from ngio.common._zoom import (
|
|
12
10
|
InterpolationOrder,
|
|
13
11
|
_zoom_inputs_check,
|
|
@@ -15,10 +13,7 @@ from ngio.common._zoom import (
|
|
|
15
13
|
numpy_zoom,
|
|
16
14
|
)
|
|
17
15
|
from ngio.utils import (
|
|
18
|
-
AccessModeLiteral,
|
|
19
16
|
NgioValueError,
|
|
20
|
-
StoreOrGroup,
|
|
21
|
-
open_group_wrapper,
|
|
22
17
|
)
|
|
23
18
|
|
|
24
19
|
|
|
@@ -41,9 +36,19 @@ def _on_disk_dask_zoom(
|
|
|
41
36
|
source_array = da.from_zarr(source)
|
|
42
37
|
target_array = dask_zoom(source_array, target_shape=target.shape, order=order)
|
|
43
38
|
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
# This is a potential fix for Dask 2025.11
|
|
40
|
+
# import dask.config
|
|
41
|
+
# chunk_size_bytes = np.prod(target.chunks) * target_array.dtype.itemsize
|
|
42
|
+
# current_chunk_size = dask.config.get("array.chunk-size")
|
|
43
|
+
# Increase the chunk size to avoid dask potentially creating
|
|
44
|
+
# corrupted chunks when writing chunks that are not multiple of the
|
|
45
|
+
# target chunk size
|
|
46
|
+
# dask.config.set({"array.chunk-size": f"{chunk_size_bytes}B"})
|
|
47
|
+
target_array = target_array.rechunk(target.chunks)
|
|
48
|
+
target_array = target_array.compute_chunk_sizes()
|
|
46
49
|
target_array.to_zarr(target)
|
|
50
|
+
# Restore previous chunk size
|
|
51
|
+
# dask.config.set({"array.chunk-size": current_chunk_size})
|
|
47
52
|
|
|
48
53
|
|
|
49
54
|
def _on_disk_coarsen(
|
|
@@ -189,85 +194,220 @@ def consolidate_pyramid(
|
|
|
189
194
|
processed.append(target_image)
|
|
190
195
|
|
|
191
196
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def init_empty_pyramid(
|
|
202
|
-
store: StoreOrGroup,
|
|
203
|
-
paths: list[str],
|
|
204
|
-
ref_shape: Sequence[int],
|
|
205
|
-
scaling_factors: Sequence[float],
|
|
206
|
-
axes: Sequence[str],
|
|
207
|
-
chunks: Sequence[int] | Literal["auto"] = "auto",
|
|
208
|
-
dtype: str = "uint16",
|
|
209
|
-
mode: AccessModeLiteral = "a",
|
|
210
|
-
dimension_separator: Literal[".", "/"] = "/",
|
|
211
|
-
compressors: CompressorLike = "auto",
|
|
212
|
-
zarr_format: Literal[2, 3] = 2,
|
|
213
|
-
) -> None:
|
|
214
|
-
# Return the an Image object
|
|
215
|
-
if chunks != "auto" and len(chunks) != len(ref_shape):
|
|
216
|
-
raise NgioValueError(
|
|
217
|
-
"The shape and chunks must have the same number of dimensions."
|
|
218
|
-
)
|
|
197
|
+
################################################
|
|
198
|
+
#
|
|
199
|
+
# Builders for image pyramids
|
|
200
|
+
#
|
|
201
|
+
################################################
|
|
202
|
+
|
|
203
|
+
ChunksLike = tuple[int, ...] | Literal["auto"]
|
|
204
|
+
ShardsLike = tuple[int, ...] | Literal["auto"]
|
|
219
205
|
|
|
220
|
-
if chunks != "auto":
|
|
221
|
-
chunks = tuple(min(c, s) for c, s in zip(chunks, ref_shape, strict=True))
|
|
222
|
-
else:
|
|
223
|
-
chunks = "auto"
|
|
224
206
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
207
|
+
def shapes_from_scaling_factors(
|
|
208
|
+
base_shape: tuple[int, ...],
|
|
209
|
+
scaling_factors: tuple[float, ...],
|
|
210
|
+
num_levels: int,
|
|
211
|
+
) -> list[tuple[int, ...]]:
|
|
212
|
+
"""Compute the shapes of each level in the pyramid from scaling factors.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
base_shape (tuple[int, ...]): The shape of the base level.
|
|
216
|
+
scaling_factors (tuple[float, ...]): The scaling factors between levels.
|
|
217
|
+
num_levels (int): The number of levels in the pyramid.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
list[tuple[int, ...]]: The shapes of each level in the pyramid.
|
|
221
|
+
"""
|
|
222
|
+
shapes = []
|
|
223
|
+
current_shape = base_shape
|
|
224
|
+
for _ in range(num_levels):
|
|
225
|
+
shapes.append(current_shape)
|
|
226
|
+
current_shape = tuple(
|
|
227
|
+
max(1, int(s / f))
|
|
228
|
+
for s, f in zip(current_shape, scaling_factors, strict=True)
|
|
228
229
|
)
|
|
230
|
+
return shapes
|
|
229
231
|
|
|
230
|
-
# Ensure scaling factors are int if possible
|
|
231
|
-
# To reduce the risk of floating point issues
|
|
232
|
-
scaling_factors = [_maybe_int(s) for s in scaling_factors]
|
|
233
232
|
|
|
234
|
-
|
|
233
|
+
def _check_order(shapes: Sequence[tuple[int, ...]]):
|
|
234
|
+
"""Check if the shapes are in decreasing order."""
|
|
235
|
+
num_pixels = [np.prod(shape) for shape in shapes]
|
|
236
|
+
for i in range(1, len(num_pixels)):
|
|
237
|
+
if num_pixels[i] >= num_pixels[i - 1]:
|
|
238
|
+
raise NgioValueError("Shapes are not in decreasing order.")
|
|
235
239
|
|
|
236
|
-
array_static_kwargs = {
|
|
237
|
-
"dtype": dtype,
|
|
238
|
-
"overwrite": True,
|
|
239
|
-
"compressors": compressors,
|
|
240
|
-
}
|
|
241
240
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
array_static_kwargs["chunk_key_encoding"] = {
|
|
249
|
-
"name": "default",
|
|
250
|
-
"separator": dimension_separator,
|
|
251
|
-
}
|
|
252
|
-
array_static_kwargs["dimension_names"] = axes
|
|
241
|
+
class PyramidLevel(BaseModel):
|
|
242
|
+
path: str
|
|
243
|
+
shape: tuple[int, ...]
|
|
244
|
+
scale: tuple[float, ...]
|
|
245
|
+
chunks: ChunksLike = "auto"
|
|
246
|
+
shards: ShardsLike | None = None
|
|
253
247
|
|
|
254
|
-
|
|
255
|
-
|
|
248
|
+
@model_validator(mode="after")
|
|
249
|
+
def _model_validation(self) -> "PyramidLevel":
|
|
250
|
+
# Same length as shape
|
|
251
|
+
if len(self.scale) != len(self.shape):
|
|
256
252
|
raise NgioValueError(
|
|
257
|
-
"
|
|
258
|
-
f"
|
|
253
|
+
"Scale must have the same length as shape "
|
|
254
|
+
f"({len(self.shape)}), got {len(self.scale)}"
|
|
259
255
|
)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
256
|
+
if any(isinstance(s, float) and s < 0 for s in self.scale):
|
|
257
|
+
raise NgioValueError("Scale values must be positive.")
|
|
258
|
+
|
|
259
|
+
if isinstance(self.chunks, tuple):
|
|
260
|
+
if len(self.chunks) != len(self.shape):
|
|
261
|
+
raise NgioValueError(
|
|
262
|
+
"Chunks must have the same length as shape "
|
|
263
|
+
f"({len(self.shape)}), got {len(self.chunks)}"
|
|
264
|
+
)
|
|
265
|
+
normalized_chunks = []
|
|
266
|
+
for dim_size, chunk_size in zip(self.shape, self.chunks, strict=True):
|
|
267
|
+
normalized_chunks.append(min(dim_size, chunk_size))
|
|
268
|
+
self.chunks = tuple(normalized_chunks)
|
|
269
|
+
|
|
270
|
+
if isinstance(self.shards, tuple):
|
|
271
|
+
if len(self.shards) != len(self.shape):
|
|
272
|
+
raise NgioValueError(
|
|
273
|
+
"Shards must have the same length as shape "
|
|
274
|
+
f"({len(self.shape)}), got {len(self.shards)}"
|
|
275
|
+
)
|
|
276
|
+
return self
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class ImagePyramidBuilder(BaseModel):
|
|
280
|
+
levels: list[PyramidLevel]
|
|
281
|
+
axes: tuple[str, ...]
|
|
282
|
+
data_type: str = "uint16"
|
|
283
|
+
dimension_separator: Literal[".", "/"] = "/"
|
|
284
|
+
compressors: Any = "auto"
|
|
285
|
+
zarr_format: Literal[2, 3] = 2
|
|
286
|
+
other_array_kwargs: Mapping[str, Any] = {}
|
|
287
|
+
|
|
288
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
289
|
+
|
|
290
|
+
@classmethod
|
|
291
|
+
def from_scaling_factors(
|
|
292
|
+
cls,
|
|
293
|
+
levels_paths: tuple[str, ...],
|
|
294
|
+
scaling_factors: tuple[float, ...],
|
|
295
|
+
base_shape: tuple[int, ...],
|
|
296
|
+
base_scale: tuple[float, ...],
|
|
297
|
+
axes: tuple[str, ...],
|
|
298
|
+
chunks: ChunksLike = "auto",
|
|
299
|
+
shards: ShardsLike | None = None,
|
|
300
|
+
data_type: str = "uint16",
|
|
301
|
+
dimension_separator: Literal[".", "/"] = "/",
|
|
302
|
+
compressors: Any = "auto",
|
|
303
|
+
zarr_format: Literal[2, 3] = 2,
|
|
304
|
+
other_array_kwargs: Mapping[str, Any] | None = None,
|
|
305
|
+
) -> "ImagePyramidBuilder":
|
|
306
|
+
shapes = shapes_from_scaling_factors(
|
|
307
|
+
base_shape=base_shape,
|
|
308
|
+
scaling_factors=scaling_factors,
|
|
309
|
+
num_levels=len(levels_paths),
|
|
310
|
+
)
|
|
311
|
+
return cls.from_shapes(
|
|
312
|
+
shapes=shapes,
|
|
313
|
+
base_scale=base_scale,
|
|
314
|
+
axes=axes,
|
|
315
|
+
levels_paths=levels_paths,
|
|
263
316
|
chunks=chunks,
|
|
264
|
-
|
|
317
|
+
shards=shards,
|
|
318
|
+
data_type=data_type,
|
|
319
|
+
dimension_separator=dimension_separator,
|
|
320
|
+
compressors=compressors,
|
|
321
|
+
zarr_format=zarr_format,
|
|
322
|
+
other_array_kwargs=other_array_kwargs,
|
|
265
323
|
)
|
|
266
324
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
325
|
+
@classmethod
|
|
326
|
+
def from_shapes(
|
|
327
|
+
cls,
|
|
328
|
+
shapes: Sequence[tuple[int, ...]],
|
|
329
|
+
base_scale: tuple[float, ...],
|
|
330
|
+
axes: tuple[str, ...],
|
|
331
|
+
levels_paths: Sequence[str] | None = None,
|
|
332
|
+
chunks: ChunksLike = "auto",
|
|
333
|
+
shards: ShardsLike | None = None,
|
|
334
|
+
data_type: str = "uint16",
|
|
335
|
+
dimension_separator: Literal[".", "/"] = "/",
|
|
336
|
+
compressors: Any = "auto",
|
|
337
|
+
zarr_format: Literal[2, 3] = 2,
|
|
338
|
+
other_array_kwargs: Mapping[str, Any] | None = None,
|
|
339
|
+
) -> "ImagePyramidBuilder":
|
|
340
|
+
levels = []
|
|
341
|
+
if levels_paths is None:
|
|
342
|
+
levels_paths = tuple(str(i) for i in range(len(shapes)))
|
|
343
|
+
_check_order(shapes)
|
|
344
|
+
scale_ = base_scale
|
|
345
|
+
for i, (path, shape) in enumerate(zip(levels_paths, shapes, strict=True)):
|
|
346
|
+
levels.append(
|
|
347
|
+
PyramidLevel(
|
|
348
|
+
path=path,
|
|
349
|
+
shape=shape,
|
|
350
|
+
scale=scale_,
|
|
351
|
+
chunks=chunks,
|
|
352
|
+
shards=shards,
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
if i + 1 < len(shapes):
|
|
356
|
+
# This only works for downsampling pyramids
|
|
357
|
+
# The _check_order function ensures that
|
|
358
|
+
# shapes are decreasing
|
|
359
|
+
next_shape = shapes[i + 1]
|
|
360
|
+
scaling_factor = tuple(
|
|
361
|
+
s1 / s2
|
|
362
|
+
for s1, s2 in zip(
|
|
363
|
+
shape,
|
|
364
|
+
next_shape,
|
|
365
|
+
strict=True,
|
|
366
|
+
)
|
|
367
|
+
)
|
|
368
|
+
scale_ = tuple(
|
|
369
|
+
s * f for s, f in zip(scale_, scaling_factor, strict=True)
|
|
370
|
+
)
|
|
371
|
+
other_array_kwargs = other_array_kwargs or {}
|
|
372
|
+
return cls(
|
|
373
|
+
levels=levels,
|
|
374
|
+
axes=axes,
|
|
375
|
+
data_type=data_type,
|
|
376
|
+
dimension_separator=dimension_separator,
|
|
377
|
+
compressors=compressors,
|
|
378
|
+
zarr_format=zarr_format,
|
|
379
|
+
other_array_kwargs=other_array_kwargs,
|
|
272
380
|
)
|
|
273
|
-
|
|
381
|
+
|
|
382
|
+
def to_zarr(self, group: zarr.Group) -> None:
|
|
383
|
+
"""Save the pyramid specification to a Zarr group.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
group (zarr.Group): The Zarr group to save the pyramid specification to.
|
|
387
|
+
"""
|
|
388
|
+
array_static_kwargs = {
|
|
389
|
+
"dtype": self.data_type,
|
|
390
|
+
"overwrite": True,
|
|
391
|
+
"compressors": self.compressors,
|
|
392
|
+
**self.other_array_kwargs,
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if self.zarr_format == 2:
|
|
396
|
+
array_static_kwargs["chunk_key_encoding"] = {
|
|
397
|
+
"name": "v2",
|
|
398
|
+
"separator": self.dimension_separator,
|
|
399
|
+
}
|
|
400
|
+
else:
|
|
401
|
+
array_static_kwargs["chunk_key_encoding"] = {
|
|
402
|
+
"name": "default",
|
|
403
|
+
"separator": self.dimension_separator,
|
|
404
|
+
}
|
|
405
|
+
array_static_kwargs["dimension_names"] = self.axes
|
|
406
|
+
for p_level in self.levels:
|
|
407
|
+
group.create_array(
|
|
408
|
+
name=p_level.path,
|
|
409
|
+
shape=tuple(p_level.shape),
|
|
410
|
+
chunks=p_level.chunks,
|
|
411
|
+
shards=p_level.shards,
|
|
412
|
+
**array_static_kwargs,
|
|
413
|
+
)
|