|
| 1 | +"""Synthetic multichannel OME-Zarr example from ``skimage.data.cells3d``. |
| 2 | +
|
| 3 | +Writes a two-channel fluorescence volume (cell membranes + nuclei) as a |
| 4 | +3-level OME-Zarr v0.5 store with a channel axis, suitable for the multichannel |
| 5 | +viewer. The sample data is downloaded on first use via ``pooch`` (a |
| 6 | +``scikit-image`` dependency), so the first call needs network access. |
| 7 | +""" |
| 8 | + |
| 9 | +from __future__ import annotations |
| 10 | + |
| 11 | +from pathlib import Path |
| 12 | + |
| 13 | +import numpy as np |
| 14 | + |
| 15 | +# Physical spacing (micrometers): z=0.29, y/x=0.26 at level 0, y/x halved per |
| 16 | +# level. c is a channel axis (unitless, scale 1.0). |
| 17 | +_SCALE_Z = 0.29 |
| 18 | +_SCALE_YX_L0 = 0.26 |
| 19 | +_N_LEVELS = 3 |
| 20 | +_CHUNK_CZYX = (1, 1, 64, 64) |
| 21 | + |
| 22 | +_DEFAULT_PATH = Path("cells3d.ome.zarr") |
| 23 | + |
| 24 | + |
| 25 | +def make_cells3d_zarr(output_path: Path | str = _DEFAULT_PATH) -> Path: |
| 26 | + """Create a two-channel OME-Zarr from ``skimage.data.cells3d``. |
| 27 | +
|
| 28 | + The stored array is ordered ``(c, z, y, x)`` -- channel-first, as required |
| 29 | + by the OME-Zarr v0.5 axis-ordering rule -- with two channels (channel 0: |
| 30 | + membranes, channel 1: nuclei) and three resolution levels downsampled in |
| 31 | + ``y``/``x`` only. |
| 32 | +
|
| 33 | + Parameters |
| 34 | + ---------- |
| 35 | + output_path : Path or str |
| 36 | + Directory to write the OME-Zarr store. Defaults to |
| 37 | + ``cells3d.ome.zarr`` in the current working directory. If the path |
| 38 | + already exists it is left untouched and returned. |
| 39 | +
|
| 40 | + Returns |
| 41 | + ------- |
| 42 | + Path |
| 43 | + Resolved path to the written (or pre-existing) store. |
| 44 | + """ |
| 45 | + import zarr |
| 46 | + from skimage.data import cells3d |
| 47 | + from skimage.measure import block_reduce |
| 48 | + |
| 49 | + output_path = Path(output_path) |
| 50 | + if output_path.exists(): |
| 51 | + print(f"Example dataset already exists at {output_path}") |
| 52 | + return output_path.resolve() |
| 53 | + |
| 54 | + print(f"Creating multichannel cells3d dataset at {output_path} ...") |
| 55 | + print("Loading skimage.data.cells3d() (downloads on first use) ...") |
| 56 | + # skimage returns (z, c, y, x); transpose to (c, z, y, x) so the channel |
| 57 | + # axis precedes all spatial axes. |
| 58 | + data_l0 = np.transpose(cells3d(), (1, 0, 2, 3)).astype(np.uint16) |
| 59 | + |
| 60 | + levels = [data_l0] |
| 61 | + for _ in range(_N_LEVELS - 1): |
| 62 | + down = block_reduce(levels[-1], block_size=(1, 1, 2, 2), func=np.mean).astype( |
| 63 | + np.uint16 |
| 64 | + ) |
| 65 | + levels.append(down) |
| 66 | + |
| 67 | + root = zarr.open_group(str(output_path), mode="w") |
| 68 | + datasets_meta = [] |
| 69 | + for level, arr in enumerate(levels): |
| 70 | + zarr_arr = root.create_array( |
| 71 | + str(level), |
| 72 | + shape=arr.shape, |
| 73 | + chunks=_CHUNK_CZYX, |
| 74 | + dtype=np.uint16, |
| 75 | + ) |
| 76 | + zarr_arr[:] = arr |
| 77 | + yx = _SCALE_YX_L0 * (2.0**level) |
| 78 | + datasets_meta.append( |
| 79 | + { |
| 80 | + "path": str(level), |
| 81 | + "coordinateTransformations": [ |
| 82 | + {"type": "scale", "scale": [1.0, _SCALE_Z, yx, yx]}, |
| 83 | + ], |
| 84 | + } |
| 85 | + ) |
| 86 | + print(f" Level {level}: shape={arr.shape} scale=(z={_SCALE_Z}, yx={yx:.4f})") |
| 87 | + |
| 88 | + root.attrs["ome"] = { |
| 89 | + "version": "0.5", |
| 90 | + "multiscales": [ |
| 91 | + { |
| 92 | + "axes": [ |
| 93 | + {"name": "c", "type": "channel"}, |
| 94 | + {"name": "z", "type": "space", "unit": "micrometer"}, |
| 95 | + {"name": "y", "type": "space", "unit": "micrometer"}, |
| 96 | + {"name": "x", "type": "space", "unit": "micrometer"}, |
| 97 | + ], |
| 98 | + "datasets": datasets_meta, |
| 99 | + "name": "cells3d", |
| 100 | + } |
| 101 | + ], |
| 102 | + } |
| 103 | + |
| 104 | + print("Done. Two channels (0: membranes, 1: nuclei), 3 resolution levels.") |
| 105 | + return output_path.resolve() |
0 commit comments