Skip to content

Commit 8b576d4

Browse files
committed
Initial version of a blockedImage adapter for OME Zarr files
1 parent 50c4950 commit 8b576d4

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed

OMEZarrAdapter.m

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
classdef OMEZarrAdapter < images.blocked.Adapter
2+
% OMEZarrAdapter A blockedImage adapter for Zarr files with OME data.
3+
% adapter = OMEZarrAdapter() creates a blockedImage adapter for use with
4+
% the blockedImage object. This adapter uses the "MATLAB support for
5+
% Zarr files" project to represent an OME formatted Zarr source as a
6+
% blockedImage object in MATLAB.
7+
%
8+
% Supported Name-Value pairs:
9+
% PermuteOrder 1xN array of integer indices specifying the data
10+
% dimension permutation order. Defaults to N:-1:1
11+
% where N is the data dimensionality. Useful when raw
12+
% data does NOT adher to tczyx order.
13+
%
14+
% Example:
15+
% % Sample data downloaded from https://ome.github.io/ome-ngff-tools/
16+
% zfile = "/data/work/zarr/9846318.zarr/0";
17+
% bim = blockedImage(zfile, Adapter=OMEZarrAdapter)
18+
% bim.Size
19+
% imageshow(bim)
20+
21+
% Copyright 2025 The MathWorks, Inc.
22+
23+
properties (Access = private)
24+
ZarrRootPath (1,1) string
25+
Info (1,1) struct
26+
end
27+
28+
properties(SetAccess=private)
29+
PermuteOrder double = [];
30+
end
31+
32+
methods
33+
34+
function obj = OMEZarrAdapter(options)
35+
arguments
36+
options.PermuteOrder double = [];
37+
end
38+
obj.PermuteOrder = options.PermuteOrder;
39+
end
40+
41+
function openToRead(obj, zpath)
42+
obj.ZarrRootPath = zpath;
43+
end
44+
45+
function info = getInfo(obj)
46+
zinfo = zarrinfo(obj.ZarrRootPath);
47+
48+
assert(~isfield(zinfo,'plate'),"Plate format is not yet supported");
49+
50+
obj.Info.UserData.RootInfo = zinfo;
51+
52+
if ~isfield(zinfo,"multiscales")
53+
error("OMEZarrAdapter:notMultiscale",...
54+
obj.ZarrRootPath+" does not have multiscale data.");
55+
end
56+
57+
% TODO - Inspect and use "order" property (C (row major) or F (col major, rare)).
58+
59+
for lvlInd = 1:numel(zinfo.multiscales.datasets)
60+
dzinfo = zarrinfo(obj.ZarrRootPath+"/"+zinfo.multiscales.datasets(lvlInd).path);
61+
obj.Info.UserData.DataSetInfo(lvlInd) = dzinfo;
62+
obj.Info.Size(lvlInd,:) = dzinfo.shape';
63+
obj.Info.IOBlockSize(lvlInd,:) = dzinfo.chunks';
64+
obj.Info.Datatype(lvlInd,:) = string(z2mtype(dzinfo.dtype));
65+
end
66+
obj.Info.InitialValue = zeros(1,1,obj.Info.Datatype(1));
67+
68+
obj.Info.Size = obj.Info.Size(:,obj.PermuteOrder);
69+
obj.Info.IOBlockSize = obj.Info.IOBlockSize(:,obj.PermuteOrder);
70+
71+
try
72+
for aInd = 1:numel(zinfo.multiscales.axes)
73+
obj.Info.UserData.Dimensions(aInd).Name = string(zinfo.multiscales.axes(aInd).name);
74+
obj.Info.UserData.Dimensions(aInd).Type = string(zinfo.multiscales.axes(aInd).type);
75+
if isfield(zinfo.multiscales.axes(aInd),'unit')
76+
obj.Info.UserData.Dimensions(aInd).Unit = string(zinfo.multiscales.axes(aInd).unit);
77+
else
78+
obj.Info.UserData.Dimensions(aInd).Unit = "none";
79+
end
80+
end
81+
obj.Info.UserData.Dimensions = struct2table(obj.Info.UserData.Dimensions);
82+
catch ME
83+
% TODO - some files dont have this property, is that
84+
% allowed?
85+
% warning("OMEZarrAdapter:noAxes","No Axes information in multiscales property");
86+
end
87+
88+
89+
% TODO - Make the default look at the existing order and figure
90+
% out the permutation to make it xyzct instead of just
91+
% flipping. (Note: Most files seem to be tczyx order, so below
92+
% ought to work just the same). Some files do not have this
93+
% axes information.
94+
if isempty(obj.PermuteOrder)
95+
obj.PermuteOrder = fliplr(1:numel(dzinfo.shape));
96+
end
97+
if isfield(obj.Info.UserData,'Dimensions')
98+
obj.Info.UserData.Dimensions = obj.Info.UserData.Dimensions(obj.PermuteOrder,:);
99+
end
100+
101+
% TODO - potentially update IOBlockSize for channels to expand
102+
% to 3 for RGB images (helps combine 3 getIOBlock calls to
103+
% one).
104+
105+
info = obj.Info;
106+
end
107+
108+
function data = getIOBlock(obj, ioblocksub, level)
109+
zpath = obj.ZarrRootPath+"/"+obj.Info.UserData.RootInfo.multiscales.datasets(level).path;
110+
start = (ioblocksub - 1) .* obj.Info.IOBlockSize(level,:) + 1;
111+
count = obj.Info.IOBlockSize(level,:);
112+
113+
start = start(obj.PermuteOrder);
114+
count = count(obj.PermuteOrder);
115+
data = zarrread(zpath,'Start',start,'Count',count);
116+
117+
% TODO - squeeze removes all singleton dimensions, could result
118+
% in a bug in the rare case of partial (1 element wide) blocks.
119+
data = squeeze(permute(data,obj.PermuteOrder));
120+
end
121+
122+
function data = getFullImage(obj, level)
123+
zpath = obj.ZarrRootPath+"/"+obj.Info.UserData.RootInfo.multiscales.datasets(level).path;
124+
data = zarrread(zpath);
125+
data = squeeze(permute(data,obj.PermuteOrder));
126+
end
127+
128+
end
129+
end
130+
131+
132+
% !LLM generated
133+
function mtype = z2mtype(ztype)
134+
% Z2MTYPE Converts a Zarr data type string to a MATLAB data type string.
135+
%
136+
% mtype = Z2MTYPE(ztype)
137+
%
138+
% Inputs:
139+
% ztype - A string representing the Zarr data type (e.g., '<u2', 'f4', 'b1').
140+
%
141+
% Outputs:
142+
% mtype - A string representing the corresponding MATLAB data type
143+
% (e.g., 'uint16', 'single', 'logical').
144+
145+
% Remove endianness or byte order indicator for mapping
146+
if startsWith(ztype, '<') || startsWith(ztype, '>') || startsWith(ztype, '|')
147+
ztype_clean = ztype(2:end);
148+
else
149+
ztype_clean = ztype;
150+
end
151+
152+
% Handle common Zarr data types
153+
switch lower(ztype_clean)
154+
case 'b1'
155+
mtype = 'logical';
156+
case {'i1'}
157+
mtype = 'int8';
158+
case {'u1'}
159+
mtype = 'uint8';
160+
case {'i2'}
161+
mtype = 'int16';
162+
case {'u2'}
163+
mtype = 'uint16';
164+
case {'i4'}
165+
mtype = 'int32';
166+
case {'u4'}
167+
mtype = 'uint32';
168+
case {'i8'}
169+
mtype = 'int64';
170+
case {'u8'}
171+
mtype = 'uint64';
172+
case {'f4'}
173+
mtype = 'single';
174+
case {'f8'}
175+
mtype = 'double';
176+
otherwise
177+
% Unknown Zarr type
178+
error('OMEZarrAdapter:UnknownDataType', 'Unknown Zarr data type: %s. Returning empty string.', ztype);
179+
end
180+
181+
end

0 commit comments

Comments
 (0)