Skip to content

Commit 14f407f

Browse files
authored
Merge pull request #303 from will-moore/sample_writing_scripts
2 parents 1e3e3e2 + 4d2d0ef commit 14f407f

File tree

1 file changed

+198
-0
lines changed

1 file changed

+198
-0
lines changed

docs/source/python.rst

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)