Skip to content

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