@@ -195,3 +195,201 @@ the data is available as `dask` arrays::
195195 viewer = napari.view_image(dask_data, channel_axis=0)
196196 if __name__ == '__main__':
197197 napari.run()
198+
199+
200+ More writing examples
201+ ---------------------
202+
203+ Writing big image from tiles::
204+
205+ # Created for https://forum.image.sc/t/writing-tile-wise-ome-zarr-with-pyramid-size/85063
206+
207+ import os
208+ import zarr
209+ from ome_zarr.io import parse_url
210+ from ome_zarr.reader import Reader
211+ from ome_zarr.writer import write_multiscales_metadata
212+ from ome_zarr.dask_utils import resize as da_resize
213+ import numpy as np
214+ import dask.array as da
215+ from math import ceil
216+
217+ url = "https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.3/9836842.zarr"
218+ reader = Reader(parse_url(url))
219+ nodes = list(reader())
220+ # first level of the pyramid
221+ dask_data = nodes[0].data[0]
222+ tile_size = 512
223+
224+ def downsample_pyramid_on_disk(parent, paths):
225+ """
226+ Takes a high-resolution Zarr array at paths[0] in the zarr group
227+ and down-samples it by a factor of 2 for each of the other paths
228+ """
229+ group_path = parent.store.path
230+ image_path = os.path.join(group_path, parent.path)
231+ print("downsample_pyramid_on_disk", image_path)
232+ for count, path in enumerate(paths[1:]):
233+ target_path = os.path.join(image_path, path)
234+ if os.path.exists(target_path):
235+ print("path exists: %s" % target_path)
236+ continue
237+ # open previous resolution from disk via dask...
238+ path_to_array = os.path.join(image_path, paths[count])
239+ dask_image = da.from_zarr(path_to_array)
240+
241+ # resize in X and Y
242+ dims = list(dask_image.shape)
243+ dims[-1] = dims[-1] // 2
244+ dims[-2] = dims[-2] // 2
245+ output = da_resize(
246+ dask_image, tuple(dims), preserve_range=True, anti_aliasing=False
247+ )
248+
249+ # write to disk
250+ da.to_zarr(
251+ arr=output, url=image_path, component=path,
252+ dimension_separator=parent._store._dimension_separator,
253+ )
254+ return paths
255+
256+ def get_tile(ch, row, col):
257+ # read the tile data from somewhere - we use the dask array
258+ y1 = row * tile_size
259+ y2 = y1 + tile_size
260+ x1 = col * tile_size
261+ x2 = x1 + tile_size
262+ return dask_data[ch, y1:y2, x1:x2]
263+
264+ # (4,1920,1920)
265+ shape = dask_data.shape
266+ chunks = (1, tile_size, tile_size)
267+ d_type = np.dtype('<u2')
268+
269+ channel_count = shape[0]
270+ row_count = ceil(shape[-2]/tile_size)
271+ col_count = ceil(shape[-1]/tile_size)
272+
273+ store = parse_url("9836842.zarr", mode="w").store
274+ root = zarr.group(store=store)
275+
276+ # create empty array at root of pyramid
277+ zarray = root.require_dataset(
278+ "0",
279+ shape=shape,
280+ exact=True,
281+ chunks=chunks,
282+ dtype=d_type,
283+ )
284+
285+ print("row_count", row_count, "col_count", col_count)
286+ # Go through all tiles and write data to "0" array
287+ for ch_index in range(channel_count):
288+ for row in range(row_count):
289+ for col in range(col_count):
290+ tile = get_tile(ch_index, row, col).compute()
291+ y1 = row * tile_size
292+ y2 = y1 + tile_size
293+ x1 = col * tile_size
294+ x2 = x1 + tile_size
295+ print("ch_index", ch_index, "row", row, "col", col)
296+ zarray[ch_index, y1:y2, x1:x2] = tile
297+
298+ paths = ["0", "1", "2"]
299+ axes = [{"name": "c", "type": "channel"}, {"name": "y", "type": "space"}, {"name": "x", "type": "space"}]
300+
301+ # We have "0" array. This downsamples (in X and Y dims only) to create "1" and "2"
302+ downsample_pyramid_on_disk(root, paths)
303+
304+ transformations = [
305+ [{"type": "scale", "scale": [1.0, 1.0, 1.0]}],
306+ [{"type": "scale", "scale": [1.0, 2.0, 2.0]}],
307+ [{"type": "scale", "scale": [1.0, 4.0, 4.0]}]
308+ ]
309+ datasets = []
310+ for p, t in zip(paths, transformations):
311+ datasets.append({"path": p, "coordinateTransformations": t})
312+
313+ write_multiscales_metadata(root, datasets, axes=axes)
314+
315+
316+ Using dask to fetch::
317+
318+ # Created for https://forum.image.sc/t/writing-tile-wise-ome-zarr-with-pyramid-size/85063
319+
320+ import dask.array as da
321+ import numpy as np
322+ import zarr
323+ from dask import delayed
324+
325+ from ome_zarr.io import parse_url
326+ from ome_zarr.writer import write_image, write_multiscales_metadata
327+
328+ zarr_name = "test_dask.zarr"
329+ store = parse_url(zarr_name, mode="w").store
330+ root = zarr.group(store=store)
331+
332+ size_xy = 100
333+ channel_count = 2
334+ size_z = 10
335+ row_count = 3
336+ col_count = 5
337+ dtype = np.uint8
338+ tile_shape = (size_xy, size_xy)
339+
340+
341+ def get_tile(ch, z, row, column):
342+ print("get_tile", ch, z, row, column)
343+ mean_val = ((row + 1) * (column + 1) * 4) + (10 * z)
344+ rng = np.random.default_rng(1000 * ch)
345+ return rng.poisson(mean_val, size=tile_shape).astype(dtype)
346+
347+
348+ delayed_reader = delayed(get_tile)
349+
350+ dask_channels = []
351+
352+ for ch in range(channel_count):
353+ dask_planes = []
354+ for z_index in range(size_z):
355+ dask_rows = []
356+ for row in range(row_count):
357+ dask_tiles = []
358+ for col in range(col_count):
359+ dask_tile = da.from_delayed(
360+ delayed_reader(ch, z_index, row, col), shape=tile_shape, dtype=dtype
361+ )
362+ dask_tiles.append(dask_tile)
363+ dask_row = da.concatenate(dask_tiles, axis=1)
364+ dask_rows.append(dask_row)
365+ dask_plane = da.concatenate(dask_rows, axis=0)
366+ dask_planes.append(dask_plane)
367+ # stack 2D planes to 3D for each channel
368+ dask_channels.append(da.stack(dask_planes, axis=0))
369+ # stack 3D (zyx) data to 4D (czyx)
370+ dask_data = da.stack(dask_channels, axis=0)
371+
372+ print("dask_data", dask_data)
373+
374+ # This will create a downsampled 'multiscales' pyramid
375+ write_image(dask_data, root, axes="czyx")
376+
377+ root.attrs["omero"] = {
378+ "channels": [
379+ {
380+ "color": "FF0000",
381+ "window": {"min": 0, "start": 0, "end": 200, "max": 256},
382+ "label": "random_red",
383+ "active": True,
384+ },
385+ {
386+ "color": "00FF00",
387+ "window": {"min": 0, "start": 0, "end": 200, "max": 256},
388+ "label": "random_green",
389+ "active": True,
390+ },
391+ ]
392+ }
393+
394+ print("Created image. Open with...")
395+ print(f"ome_zarr view {zarr_name}")
0 commit comments