Skip to content

Commit a2665d9

Browse files
committed
ENH: tensorstore Zarr 3, OME-Zarr 0.5 support
Via OME-NGFF RFC 2.
1 parent 37f6623 commit a2665d9

File tree

4 files changed

+222
-80
lines changed

4 files changed

+222
-80
lines changed

ngff_zarr/to_ngff_zarr.py

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,23 +71,66 @@ def _prep_for_to_zarr(store: StoreLike, arr: dask.array.Array) -> dask.array.Arr
7171
)
7272

7373

74-
def _write_with_tensorstore(store_path: str, array, region, chunks) -> None:
74+
def _numpy_to_zarr_dtype(dtype):
75+
dtype_map = {
76+
"bool": "bool",
77+
"int8": "int8",
78+
"int16": "int16",
79+
"int32": "int32",
80+
"int64": "int64",
81+
"uint8": "uint8",
82+
"uint16": "uint16",
83+
"uint32": "uint32",
84+
"uint64": "uint64",
85+
"float16": "float16",
86+
"float32": "float32",
87+
"float64": "float64",
88+
"complex64": "complex64",
89+
"complex128": "complex128",
90+
}
91+
92+
dtype_str = str(dtype)
93+
94+
# Handle endianness - strip byte order chars
95+
if dtype_str.startswith(("<", ">", "|")):
96+
dtype_str = dtype_str[1:]
97+
98+
# Look up corresponding zarr dtype
99+
try:
100+
return dtype_map[dtype_str]
101+
except KeyError:
102+
raise ValueError(f"dtype {dtype} cannot be mapped to Zarr v3 core dtype")
103+
104+
105+
def _write_with_tensorstore(
106+
store_path: str, array, region, chunks, zarr_format
107+
) -> None:
75108
"""Write array using tensorstore backend"""
76109
import tensorstore as ts
77110

78111
spec = {
79-
"driver": "zarr",
80112
"kvstore": {
81113
"driver": "file",
82114
"path": store_path,
83115
},
84116
"metadata": {
85-
"chunks": chunks,
86117
"shape": array.shape,
87-
"dtype": array.dtype.str,
88-
"dimension_separator": "/",
89118
},
90119
}
120+
if zarr_format == 2:
121+
spec["driver"] = "zarr"
122+
spec["metadata"]["chunks"] = chunks
123+
spec["metadata"]["dimension_separator"] = "/"
124+
spec["metadata"]["dtype"] = array.dtype.str
125+
elif zarr_format == 3:
126+
spec["driver"] = "zarr3"
127+
spec["metadata"]["chunk_grid"] = {
128+
"name": "regular",
129+
"configuration": {"chunk_shape": chunks},
130+
}
131+
spec["metadata"]["data_type"] = _numpy_to_zarr_dtype(array.dtype)
132+
else:
133+
raise ValueError(f"Unsupported zarr format: {zarr_format}")
91134
dataset = ts.open(spec, create=True, dtype=array.dtype).result()
92135
dataset[...] = array[region]
93136

@@ -134,7 +177,7 @@ def to_ngff_zarr(
134177
if isinstance(store, (str, Path)):
135178
store_path = str(store)
136179
else:
137-
raise ValueError("Tensorstore requires a path-like store")
180+
raise ValueError("use_tensorstore currently requires a path-like store")
138181

139182
if version != "0.4" and version != "0.5":
140183
raise ValueError(f"Unsupported version: {version}")
@@ -145,7 +188,6 @@ def to_ngff_zarr(
145188
zarr_format = 2 if version == "0.4" else 3
146189
format_kwargs = {"zarr_format": zarr_format} if zarr_version_major >= 3 else {}
147190
if version == "0.4":
148-
# root = zarr.group(store, overwrite=overwrite, chunk_store=chunk_store)
149191
root = zarr.open_group(
150192
store,
151193
mode="w" if overwrite else "a",
@@ -342,6 +384,7 @@ def to_ngff_zarr(
342384
optimized,
343385
region,
344386
[c[0] for c in arr_region.chunks],
387+
zarr_format=zarr_format,
345388
**kwargs,
346389
)
347390
else:
@@ -365,7 +408,12 @@ def to_ngff_zarr(
365408
scale_path = f"{store_path}/{path}"
366409
region = tuple([slice(arr.shape[i]) for i in range(arr.ndim)])
367410
_write_with_tensorstore(
368-
scale_path, arr, region, [c[0] for c in arr.chunks], **kwargs
411+
scale_path,
412+
arr,
413+
region,
414+
[c[0] for c in arr.chunks],
415+
zarr_format=zarr_format,
416+
**kwargs,
369417
)
370418
else:
371419
arr = _prep_for_to_zarr(store, arr)

0 commit comments

Comments
 (0)