Skip to content

Latest commit

 

History

699 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zarr-MATLAB

A Zarr v3 and v2 implementation based on zarrs for MATLAB.

Features

  • Read and write Zarr v3 and Zarr v2 arrays
  • The format of an existing array or group is detected automatically on open
  • Support for chunking and sharding (sharding is v3 only)
  • Filter codecs: transpose (default)
  • Compression codecs: zstd (default), gzip, blosc; v2 additionally supports zlib and bz2
  • Filesystem access (read-write) and HTTP/HTTPS remote access (read-only)
  • Group hierarchy support
  • Attributes for arrays and groups
  • Supported data types: bool, uint8, uint16, uint32, uint64, int8, int16, int32, int64, float32, float64

Usage

ZarrArray

Create a new array:

% Create with default options
arr = ZarrArray.create('/path/to/array', [200, 200, 200], 'uint16');

% Create with explicit chunk shape
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'uint16', 'chunkShape', [32, 32, 32]);

% Create a 1D array
arr = ZarrArray.create('/path/to/array', [10000], 'float64', 'chunkShape', [1000]);

% Create a 2D array
arr = ZarrArray.create('/path/to/array', [1024, 768], 'uint8', 'chunkShape', [256, 256]);

% Create with sharding (shard contains multiple chunks)
arr = ZarrArray.create('/path/to/array', [256, 256, 256], 'uint16', ...
    'chunkShape', [32, 32, 32], 'shardShape', [128, 128, 128]);

% Create without compression (zstd is the default)
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'uint16', 'compressors', 'none');

% Create with compressor and custom configuration
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'float32', ...
    'chunkShape', [32, 32, 32], 'compressors', struct('name', 'zstd', 'configuration', struct('level', 10)));

% Create without filters (no transpose, for C-order data)
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'uint16', 'filters', 'none');

% Create with no filters and no compression
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'uint16', ...
    'filters', 'none', 'compressors', 'none');

% Create with multiple compressors (sequence)
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'uint16', ...
    'compressors', {struct('name', 'zstd'), struct('name', 'crc32c')});

% Create with custom fill value for uninitialized chunks
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'float32', 'fillValue', NaN);

% Create with v2-style chunk key encoding (uses '.' separator)
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'uint16', ...
    'chunkKeyEncoding', struct('name', 'v2', 'separator', '.'));

Create a Zarr v2 array instead of the default v3:

% Writes .zarray (and .zattrs for attributes) instead of zarr.json
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'uint16', ...
    'chunkShape', [32, 32, 32], 'zarrFormat', 2);

% v2 also accepts the zlib and bz2 numcodecs compressors
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'uint16', ...
    'zarrFormat', 2, 'compressors', 'zlib');

% Chunk keys are flat in v2; only the separator is configurable
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'uint16', ...
    'zarrFormat', 2, 'chunkKeyEncoding', '.');

% createFromData takes zarrFormat too
arr = ZarrArray.createFromData('/path/to/array', data, 'zarrFormat', 2);

Create an array from existing data (shape and data type are inferred):

% Create from existing MATLAB array
data = uint16(rand(100, 100, 100) * 65535);
arr = ZarrArray.createFromData('/path/to/array', data);

% Create from data with gzip instead of default zstd
data = rand(64, 64, 64);  % double -> float64
arr = ZarrArray.createFromData('/path/to/array', data, 'compressors', 'gzip');

% Create from data with explicit chunk shape
arr = ZarrArray.createFromData('/path/to/array', data, 'chunkShape', [32, 32, 32]);

% Create from logical array (stored as bool)
mask = rand(64, 64, 64) > 0.5;  % logical -> bool
arr = ZarrArray.createFromData('/path/to/mask', mask);

Notes:

  • Filters: Applied before bytes codec. Default is transpose (to store Fortran-order data). Use 'none' to disable.
  • Compressors: Applied after bytes codec. Default is zstd. Supported: zstd, gzip, blosc, or 'none' to disable.
  • Both filters and compressors accept: a single codec (string or struct), a cell array of codecs (sequence), or 'none'.
  • Zarr v2 differences ('zarrFormat', 2):
    • No sharding: passing shardShape is an error.
    • A single compressor only: a cell array of more than one compressor is an error. zlib and bz2 are available in addition to zstd, gzip and blosc, and blosc takes an integer shuffle (0 none, 1 byte-wise, 2 bit-wise) rather than the v3 string form.
    • There is no transpose codec. filters selects the chunk byte layout instead: the default writes order: "F" to match MATLAB's column-major arrays, and 'none' writes order: "C". Either way reads and writes return the same MATLAB array.
    • chunkKeyEncoding contributes only a separator, written as dimension_separator.
    • Attributes live in .zattrs rather than inside the array metadata.

Open an existing array and read/write data:

% Open existing array
arr = ZarrArray('/path/to/array');

% Get array info
info = arr.info();     % Struct with shape, dataType, chunkShape, shardShape, zarrFormat
shape = arr.shape();   % Returns shape as vector
format = arr.zarrFormat(); % 2 or 3, detected from the metadata on disk

% Read data (bounding box is [start, end] for each dimension, 1-indexed)
bbox = [1, 33; 1, 33; 1, 33];  % Read a 32x32x32 region
data = arr.read(bbox);

% Read entire array (bbox is optional)
allData = arr.read();

% Write data to a region
data = uint16(rand(32, 32, 32) * 65535);
arr.write(data, bbox);

% Write entire array at origin (bbox is optional)
fullData = uint16(rand(100, 100, 100) * 65535);
arr.write(fullData);  % Writes starting at [1, 1, 1]

% Write with automatic resize (extends array if needed)
largeData = uint16(rand(50, 50, 50) * 65535);
arr.write(largeData, [151, 200; 151, 200; 151, 200], 'allowResize', true);

% Write to 1D arrays as column vector
arr.write([34; 94; 28]);

% Resize array explicitly
arr.resize([200, 200, 200]);

ZarrGroup

Create and navigate group hierarchies:

% Create a new group
root = ZarrGroup.create('/path/to/dataset');

% Create subgroups
segmentation = root.createGroup('segmentation');
raw = root.createGroup('raw');

% Create arrays within groups
seg_data = segmentation.createArray('data', [1000, 1000, 500], 'uint32', ...
    'chunkShape', [64, 64, 64], 'shardShape', [256, 256, 256]);
raw_data = raw.createArray('data', [1000, 1000, 500], 'uint8');

% Create array from existing data
data = uint16(rand(100, 100, 50) * 65535);
arr = root.createArrayFromData('processed', data);

% List contents
names = root.list();                    % Returns {'raw', 'segmentation'}
[groups, arrays] = root.listContents(); % Separate groups and arrays

Create a Zarr v2 hierarchy:

% Writes .zgroup instead of zarr.json
root = ZarrGroup.create('/path/to/dataset', 'zarrFormat', 2);

% Subgroups and arrays inherit the parent's format
raw = root.createGroup('raw');            % also v2
arr = raw.createArray('data', [512, 512, 128], 'uint8');  % also v2

% Unless overridden explicitly
v3arr = raw.createArray('other', [64, 64], 'uint16', 'zarrFormat', 3);

Open existing groups:

% Open existing group (v2 and v3 are both detected automatically)
root = ZarrGroup('/path/to/dataset');
format = root.zarrFormat();  % 2 or 3

% Navigate to subgroups
segmentation = root.openGroup('segmentation');

% Open arrays within groups
arr = segmentation.openArray('data');
data = arr.read([1, 65; 1, 65; 1, 65]);

list() and listContents() recognise nodes in either format, so a hierarchy that mixes v2 and v3 children is listed correctly.

Remote Access (HTTP)

Read from remote Zarr arrays and groups:

% Open remote array
arr = ZarrArray('https://example.com/data/zarr_v3/dataset/segmentation/1');
info = arr.info();
data = arr.read([1, 33; 1, 33; 1, 33]);

% Open remote group and navigate
root = ZarrGroup('https://example.com/data/zarr_v3/dataset');
segmentation = root.openGroup('segmentation');
arr = segmentation.openArray('1');

Note: Remote access is read-only. The list() and listContents() methods are not available for HTTP URLs.

Attributes

Both arrays and groups support attributes:

% Set attributes on a group
grp = ZarrGroup.create('/path/to/dataset');
grp.setAttribute('name', 'My Dataset');
grp.setAttribute('resolution', [4, 4, 30]);

% Set multiple attributes at once
grp.setAttributes(struct('version', 2, 'author', 'John Doe'));

% Get attributes
name = grp.getAttribute('name');
attrs = grp.getAttributes();  % Returns struct with all attributes

% Attributes work the same way on arrays
arr = ZarrArray.create('/path/to/array', [100, 100, 100], 'uint16');
arr.setAttribute('units', 'nm');
arr.setAttribute('scale', [1.0, 1.0, 2.0]);

Note: Writing attributes is not available for HTTP URLs (read-only).

Installation

From Toolbox (Recommended)

Download zarr-matlab.mltbx from the releases page and double-click to install, or run:

matlab.addons.install('zarr-matlab.mltbx')

Building from Source

Requires Rust toolchain installed.

cd zarr-matlab
zarrBuild()

Running Tests

cd zarr-matlab
results = runtests('tests');

There is also a separate suite that round-trips zarr-matlab against a real zarr-python install (both directions, both Zarr versions). It requires uv and is not run by runtests('tests') — see zarr-matlab/interop-tests/README.md.

Credits

Uses the Rust-based zarrs library for the Zarr IO. Developed by Lachlan Deakin and other contributors.

Uses the Rust-MATLAB binding originally developed for the WKW format. Developed by Alessandro Motta at the Max Planck Institute for Brain Research

Thanks to Ilya Belevich for integration and performance testing. See Microscopy Image Browser (MIB).

License

MIT

About

No description, website, or topics provided.

Resources

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages