Prepare data

Preparing your imaging data

OME-Zarr storage layout, sharding, and pyramids for fast viewing.

The viewer streams OME-Zarr image data over HTTP, tile by tile. Storage layout determines loading speed, memory use, and interactivity.

At a glance

RecommendedAvoid
Zarr versionZarr v3 (OME-NGFF 0.5) with sharding_indexedZarr v2 or unsharded v3
Pyramid≥ 4 resolution levels (LODs)Single LOD
Inner chunk (XY)512 × 512 px or 256 × 256Anything that covers a full XY plane

Why storage format matters

Storage layout directly affects image loading speed and interactivity. Multi-scale pyramids and appropriately sized chunks help the viewer load only the image data needed for the current view.


OME-NGFF 0.5 stores data as Zarr v3. The sharding_indexed codec packs many small inner chunks into larger shard files on disk, which is ideal for HTTP access:

  • Each shard file ends with an index table. An uncached read normally requires one byte-range request for the index and another for the compressed inner chunk.
  • Packing all channels into one shard can let an index read cover the same spatial region across channels.
FieldRecommendation
Chunk shapeKeep inner XY dimensions at or below 512 × 512, for example (1, 1, 1, 256, 256)
Shard shapeGroup multiple inner chunks while keeping shard files practical for the storage backend
CodecUse sharding_indexed with a supported inner compression codec

The inner chunk shape controls each image fetch. Shard size, dtype, compression, and storage latency also affect performance, so benchmark the intended backend with representative images.

Verifying your layout

Any zarr inspector that exposes shape + chunking will do. iohub (a separate tool) needs no permanent install:

uvx iohub info --verbose <plate.zarr>

Look for inner chunks ≤ 512 × 512:

Chunk size:  (1, 1, 1, 256, 256)          ← inner chunk
No. bytes decompressed: 1.4 TiB           ← sanity check on total size

And 4–5 resolution levels per position:

0  (1, 12, 1, 104683, 104776)  float32    ← LOD 0: full res
1  (1, 12, 1,  52342,  52388)  float32
2  (1, 12, 1,  26171,  26194)  float32
3  (1, 12, 1,  13086,  13097)  float32
4  (1, 12, 1,   6543,   6549)  float32    ← LOD 4: thumbnail

Multi-scale pyramids

Pyramids (multi-scale / multi-resolution) are strongly recommended. Without them the background thumbnail must read full-resolution chunks, which delays each field-of-view change.

Generating pyramids with iohub

from iohub import open_ome_zarr

with open_ome_zarr("plate.zarr", mode="r+") as plate:
    for _, position in plate.positions():
        position.make_multiscale(
            scale_factors=[[1, 1, 2, 2]],  # downsample XY by 2×
            chunks=(1, 1, 1, 512, 512),
            num_levels=4,
        )

Zarr v2

Zarr v2 (OME-NGFF 0.4) works, with caveats:

  • No byte-range sharding. Each chunk lives in its own file. A 512 × 512 px crop fetches one file per chunk. This works for small datasets but is costly at scale.
  • Chunk size still matters. Keep XY chunk dimensions below the full image plane. A (1, 1, 1, 748, 1135) chunk forces the client to download a 748 × 1135 px plane to display a 10 × 10 px region.

Pyramids matter as much for v2 as for v3 because the background-loading path does not change with storage version.