@@ -13,13 +13,18 @@ of 2 in the X and Y dimensions.
1313Alternatively, the :py:func: `ome_zarr.writer.write_multiscale ` can be used, which takes a
1414"pyramid" of pre-computed `numpy ` arrays.
1515
16+ The default version of OME-NGFF is v0.5, is based on Zarr v3. A zarr v3 store is created
17+ by `parse_url() ` below. To write OME-NGFF v0.4 (Zarr v2), use the `fmt=FormatV04() ` argument
18+ in `parse_url() `, which will create a Zarr v2 store.
19+
1620The following code creates a 3D Image in OME-Zarr::
1721
1822 import numpy as np
1923 import zarr
2024
2125 from ome_zarr.io import parse_url
22- from ome_zarr.writer import write_image
26+ from ome_zarr.format import FormatV04
27+ from ome_zarr.writer import write_image, add_metadata
2328
2429 path = "test_ngff_image.zarr"
2530
@@ -28,10 +33,11 @@ The following code creates a 3D Image in OME-Zarr::
2833 rng = np.random.default_rng(0)
2934 data = rng.poisson(lam=10, size=(size_z, size_xy, size_xy)).astype(np.uint8)
3035
31- # write the image data
36+ # Use fmt=FormatV04() to write v0.4 format (zarr v2)
3237 store = parse_url(path, mode="w").store
3338 root = zarr.group(store=store)
34- write_image(image=data, group=root, axes="zyx", storage_options=dict(chunks=(1, size_xy, size_xy)))
39+ write_image(image=data, group=root, axes="zyx",
40+ storage_options=dict(chunks=(1, size_xy, size_xy)))
3541
3642
3743This image can be viewed in `napari ` using the
@@ -41,18 +47,18 @@ This image can be viewed in `napari` using the
4147
4248Rendering settings
4349------------------
44- Render settings can be added to an existing zarr group::
50+ Rendering settings can be added to an existing zarr group::
4551
4652 store = parse_url(path, mode="w").store
4753 root = zarr.group(store=store)
48- root.attrs[ "omero"] = {
54+ add_metadata( root, { "omero": {
4955 "channels": [{
5056 "color": "00FFFF",
5157 "window": {"start": 0, "end": 20, "min": 0, "max": 255},
5258 "label": "random",
5359 "active": True,
5460 }]
55- }
61+ }})
5662
5763Writing labels
5864--------------
@@ -64,10 +70,11 @@ The following code creates a 3D Image in OME-Zarr with labels::
6470 import os
6571
6672 from skimage.data import binary_blobs
73+ from ome_zarr.format import FormatV04
6774 from ome_zarr.io import parse_url
68- from ome_zarr.writer import write_image
75+ from ome_zarr.writer import write_image, add_metadata
6976
70- path = "test_ngff_image .zarr"
77+ path = "test_ngff_image_labels .zarr"
7178 os.mkdir(path)
7279
7380 mean_val=10
@@ -76,19 +83,20 @@ The following code creates a 3D Image in OME-Zarr with labels::
7683 rng = np.random.default_rng(0)
7784 data = rng.poisson(mean_val, size=(size_z, size_xy, size_xy)).astype(np.uint8)
7885
79- # write the image data
86+ # Use fmt=FormatV04() to write v0.4 format (zarr v2)
8087 store = parse_url(path, mode="w").store
8188 root = zarr.group(store=store)
82- write_image(image=data, group=root, axes="zyx", storage_options=dict(chunks=(1, size_xy, size_xy)))
89+ write_image(image=data, group=root, axes="zyx",
90+ storage_options=dict(chunks=(1, size_xy, size_xy)))
8391 # optional rendering settings
84- root.attrs[ "omero"] = {
92+ add_metadata( root, { "omero": {
8593 "channels": [{
8694 "color": "00FFFF",
8795 "window": {"start": 0, "end": 20, "min": 0, "max": 255},
8896 "label": "random",
8997 "active": True,
9098 }]
91- }
99+ }})
92100
93101
94102 # add labels...
@@ -104,18 +112,19 @@ The following code creates a 3D Image in OME-Zarr with labels::
104112 labels_grp = root.create_group("labels")
105113 # the 'labels' .zattrs lists the named labels data
106114 label_name = "blobs"
107- labels_grp.attrs[ "labels"] = [label_name]
115+ add_metadata( labels_grp, { "labels": [label_name]})
108116 label_grp = labels_grp.create_group(label_name)
109- # need 'image-label' attr to be recognized as label
110- label_grp.attrs["image-label"] = {
117+ write_image(label, label_grp, axes="zyx")
118+
119+ # we need 'image-label' attr to be recognized as label
120+ add_metadata(label_grp, {"image-label": {
111121 "colors": [
112122 {"label-value": 1, "rgba": [255, 0, 0, 255]},
113123 {"label-value": 2, "rgba": [0, 255, 0, 255]},
114124 {"label-value": 3, "rgba": [255, 255, 0, 255]}
115125 ]
116- }
126+ }})
117127
118- write_image(label, label_grp, axes="zyx")
119128
120129Writing HCS datasets to OME-NGFF
121130--------------------------------
@@ -125,6 +134,7 @@ This sample code shows how to write a high-content screening dataset (i.e. cultu
125134 import numpy as np
126135 import zarr
127136
137+ from ome_zarr.format import FormatV04
128138 from ome_zarr.io import parse_url
129139 from ome_zarr.writer import write_image, write_plate_metadata, write_well_metadata
130140
@@ -144,6 +154,7 @@ This sample code shows how to write a high-content screening dataset (i.e. cultu
144154 data = rng.poisson(mean_val, size=(num_wells, num_fields, size_z, size_xy, size_xy)).astype(np.uint8)
145155
146156 # write the plate of images and corresponding metadata
157+ # Use fmt=FormatV04() in parse_url() to write v0.4 format (zarr v2)
147158 store = parse_url(path, mode="w").store
148159 root = zarr.group(store=store)
149160 write_plate_metadata(root, row_names, col_names, well_paths)
@@ -154,7 +165,8 @@ This sample code shows how to write a high-content screening dataset (i.e. cultu
154165 write_well_metadata(well_group, field_paths)
155166 for fi, field in enumerate(field_paths):
156167 image_group = well_group.require_group(str(field))
157- write_image(image=data[wi, fi], group=image_group, axes="zyx", storage_options=dict(chunks=(1, size_xy, size_xy)))
168+ write_image(image=data[wi, fi], group=image_group, axes="zyx",
169+ storage_options=dict(chunks=(1, size_xy, size_xy)))
158170
159171
160172This image can be viewed in `napari ` using the
@@ -177,11 +189,9 @@ the data is available as `dask` arrays::
177189 from ome_zarr.reader import Reader
178190 import napari
179191
180- url = "https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4 /idr0062A/6001240 .zarr"
192+ url = "https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.5 /idr0062A/6001240_labels .zarr"
181193
182194 # read the image data
183- store = parse_url(url, mode="r").store
184-
185195 reader = Reader(parse_url(url))
186196 # nodes may include images, labels etc
187197 nodes = list(reader())
@@ -207,26 +217,32 @@ Writing big image from tiles::
207217 import os
208218 import zarr
209219 from ome_zarr.io import parse_url
220+ from ome_zarr.format import CurrentFormat, FormatV04
210221 from ome_zarr.reader import Reader
211222 from ome_zarr.writer import write_multiscales_metadata
212223 from ome_zarr.dask_utils import resize as da_resize
213224 import numpy as np
214225 import dask.array as da
215226 from math import ceil
216227
228+ fmt = CurrentFormat()
229+ # Use fmt=FormatV04() to write v0.4 format (zarr v2)
230+
217231 url = "https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.3/9836842.zarr"
218232 reader = Reader(parse_url(url))
219233 nodes = list(reader())
220234 # first level of the pyramid
221235 dask_data = nodes[0].data[0]
222236 tile_size = 512
237+ axes = [{"name": "c", "type": "channel"}, {"name": "y", "type": "space"}, {"name": "x", "type": "space"}]
223238
224239 def downsample_pyramid_on_disk(parent, paths):
225240 """
226241 Takes a high-resolution Zarr array at paths[0] in the zarr group
227242 and down-samples it by a factor of 2 for each of the other paths
228243 """
229- group_path = parent.store.path
244+ group_path = str(parent.store_path)
245+ img_path = parent.store_path / parent.path
230246 image_path = os.path.join(group_path, parent.path)
231247 print("downsample_pyramid_on_disk", image_path)
232248 for count, path in enumerate(paths[1:]):
@@ -246,10 +262,16 @@ Writing big image from tiles::
246262 dask_image, tuple(dims), preserve_range=True, anti_aliasing=False
247263 )
248264
265+ options = {}
266+ if fmt.zarr_format == 2:
267+ options["dimension_separator"] = "/"
268+ else:
269+ options["chunk_key_encoding"] = fmt.chunk_key_encoding
270+ options["dimension_names"] = [axis["name"] for axis in axes]
249271 # write to disk
250272 da.to_zarr(
251- arr=output, url=image_path , component=path,
252- dimension_separator=parent._store._dimension_separator,
273+ arr=output, url=img_path , component=path,
274+ zarr_format=fmt.zarr_format, **options
253275 )
254276 return paths
255277
@@ -270,16 +292,18 @@ Writing big image from tiles::
270292 row_count = ceil(shape[-2]/tile_size)
271293 col_count = ceil(shape[-1]/tile_size)
272294
273- store = parse_url("9836842.zarr", mode="w").store
295+ store = parse_url("9836842.zarr", mode="w", fmt=fmt ).store
274296 root = zarr.group(store=store)
275297
276298 # create empty array at root of pyramid
277- zarray = root.require_dataset (
299+ zarray = root.require_array (
278300 "0",
279301 shape=shape,
280302 exact=True,
281303 chunks=chunks,
282304 dtype=d_type,
305+ chunk_key_encoding=fmt.chunk_key_encoding,
306+ dimension_names=[axis["name"] for axis in axes], # omit for v0.4
283307 )
284308
285309 print("row_count", row_count, "col_count", col_count)
@@ -296,7 +320,6 @@ Writing big image from tiles::
296320 zarray[ch_index, y1:y2, x1:x2] = tile
297321
298322 paths = ["0", "1", "2"]
299- axes = [{"name": "c", "type": "channel"}, {"name": "y", "type": "space"}, {"name": "x", "type": "space"}]
300323
301324 # We have "0" array. This downsamples (in X and Y dims only) to create "1" and "2"
302325 downsample_pyramid_on_disk(root, paths)
@@ -313,7 +336,8 @@ Writing big image from tiles::
313336 write_multiscales_metadata(root, datasets, axes=axes)
314337
315338
316- Using dask to fetch::
339+ Using dask to fetch. Here concatenate lazy "delayed" source of tiles into a full image.
340+ When that dask data is passed to write_image() the tiles will be loaded on the fly::
317341
318342 # Created for https://forum.image.sc/t/writing-tile-wise-ome-zarr-with-pyramid-size/85063
319343
@@ -323,9 +347,11 @@ Using dask to fetch::
323347 from dask import delayed
324348
325349 from ome_zarr.io import parse_url
326- from ome_zarr.writer import write_image, write_multiscales_metadata
350+ from ome_zarr.format import FormatV04
351+ from ome_zarr.writer import write_image, add_metadata
327352
328353 zarr_name = "test_dask.zarr"
354+ # Use fmt=FormatV04() in parse_url() to write v0.4 format (zarr v2)
329355 store = parse_url(zarr_name, mode="w").store
330356 root = zarr.group(store=store)
331357
@@ -374,7 +400,7 @@ Using dask to fetch::
374400 # This will create a downsampled 'multiscales' pyramid
375401 write_image(dask_data, root, axes="czyx")
376402
377- root.attrs[ "omero"] = {
403+ add_metadata( root, { "omero": {
378404 "channels": [
379405 {
380406 "color": "FF0000",
@@ -389,7 +415,7 @@ Using dask to fetch::
389415 "active": True,
390416 },
391417 ]
392- }
418+ }})
393419
394420 print("Created image. Open with...")
395421 print(f"ome_zarr view {zarr_name}")
0 commit comments