ndi.fun.doc.lightsheet.chooseTileShape
NDI.FUN.DOC.LIGHTSHEET.CHOOSETILESHAPE - pick a chunk shape that hits a byte budget
CHUNKS = NDI.FUN.DOC.LIGHTSHEET.CHOOSETILESHAPE( ...
SHAPE, AXESORDER, VOXELSIZE, DTYPE, BUDGETBYTES)
Sizes an OME-Zarr chunk to land near BUDGETBYTES (uncompressed)
and to be ISOTROPIC IN WORLD SPACE: each spatial axis gets a
chunk length inversely proportional to its voxel size, so a
lightsheet volume with 1 um XY and 5 um Z produces pancake tiles
rather than voxel cubes.
SHAPE - row vector, axis lengths in AXESORDER
AXESORDER - char row like 'tczyx' (one letter per axis)
VOXELSIZE - row vector, one per axis, same order (may include
zeros or NaNs on t/c)
DTYPE - char, NGFF-style ('uint16', 'float32', '<u2', ...)
BUDGETBYTES - target uncompressed bytes for one chunk. Default
chosen by MAKEPYRAMID is 32*2^20 (32 MB), sized to
the measured signing+fetch+decode sweet spot for a
viewer on an ~200 MB/s link.
Non-spatial axes (t, c) get chunk = 1 regardless of budget. The
spatial axes share the budget from
s = (voxelsPerTile * prod(voxel_i))^(1/nSpatial)
chunk_i = round(s / voxel_i)
Each chunk is clamped to the level's shape, so a coarse level
whose spatial axes are already smaller than the ideal tile ends
up as one whole chunk. When any axis clamps, the leftover budget
is redistributed across the still-slack axes in one pass.
Example (uint16, 32 MB budget, isotropic 1 um):
chooseTileShape([1200 2000 2000], 'zyx', [1 1 1], 'uint16', ...
32*2^20)
-> [256 256 256] % ~256^3 * 2 bytes = 32.0 MB
Example (uint16, 32 MB, anisotropic 1/1/5 um):
chooseTileShape([1200 2000 2000], 'zyx', [5 1 1], 'uint16', ...
32*2^20)
-> [88 438 438] % ~88*438*438*2 ~= 33.8 MB, world-cube
See also: ndi.fun.doc.lightsheet.makePyramid