Skip to content

Commit a3cd67f

Browse files
committed
fix(ngff): do not reverse axes for fOrder zarr arrays
* add test
1 parent 94c129a commit a3cd67f

File tree

9 files changed

+186
-11
lines changed

9 files changed

+186
-11
lines changed

src/main/java/org/janelia/saalfeldlab/n5/universe/metadata/ome/ngff/v04/OmeNgffMetadataParser.java

+9-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04.coordinateTransformations.CoordinateTransformation;
2020
import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04.coordinateTransformations.CoordinateTransformationAdapter;
2121
import org.janelia.saalfeldlab.n5.zarr.ZarrDatasetAttributes;
22+
import org.janelia.saalfeldlab.n5.zarr.ZarrKeyValueReader;
2223

2324
import com.google.gson.Gson;
2425
import com.google.gson.GsonBuilder;
@@ -84,7 +85,6 @@ public Optional<OmeNgffMetadata> parseMetadata(final N5Reader n5, final N5TreeNo
8485

8586
final OmeNgffMultiScaleMetadata ms = multiscales[j];
8687

87-
// ms.path = node.getPath();
8888
final String[] paths = ms.getPaths();
8989
final DatasetAttributes[] attrs = new DatasetAttributes[ms.getPaths().length];
9090

@@ -94,14 +94,20 @@ public Optional<OmeNgffMetadata> parseMetadata(final N5Reader n5, final N5TreeNo
9494
attrs[i] = dsetMeta[i].getAttributes();
9595
}
9696

97-
// final NgffSingleScaleAxesMetadata[] msChildrenMeta = ms.buildChildren(nd, attrs, ms.coordinateTransformations, ms.axes);
97+
// if zarr is used for storage, and arrays are stored in F-order, axes should not be reversed
98+
// reverse Axes if C-order where "row major" == C-order in this context.
99+
boolean reverseAxes = true;
100+
if( n5 instanceof ZarrKeyValueReader)
101+
reverseAxes = cOrder(dsetMeta[0].getAttributes());
102+
98103
final NgffSingleScaleAxesMetadata[] msChildrenMeta = OmeNgffMultiScaleMetadata.buildMetadata(
99104
nd, node.getPath(), ms.datasets, attrs, ms.coordinateTransformations, ms.metadata, ms.axes);
100105

101106
MetadataUtils.updateChildrenMetadata(node, msChildrenMeta, false);
102107

103108
// axes need to be flipped after the child is created
104-
ArrayUtils.reverse(ms.axes);
109+
if (reverseAxes)
110+
ArrayUtils.reverse(ms.axes);
105111

106112
multiscales[j] = new OmeNgffMultiScaleMetadata( ms, msChildrenMeta );
107113
}

src/test/java/org/janelia/saalfeldlab/n5/universe/metadata/ome/ngff/v04/CoordinateTransformParsingTest.java

+13-8
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,19 @@ public void testCoordinateTransformParsing() {
3131
final N5Reader zarr = new N5Factory().openReader(rootF.toString());
3232

3333
final OmeNgffMetadataParser grpParser = new OmeNgffMetadataParser();
34-
test(grpParser.parseMetadata(zarr, setupNode("coordTforms/ss", "s0")),
34+
test(grpParser.parseMetadata(zarr, setupNode(zarr, "coordTforms/ss", "s0")),
3535
new double[]{4, 4},
3636
new double[]{0, 0});
3737

38-
test(grpParser.parseMetadata(zarr, setupNode("coordTforms/st", "s0")),
38+
test(grpParser.parseMetadata(zarr, setupNode(zarr, "coordTforms/st", "s0")),
3939
new double[]{2, 2},
4040
new double[]{10, 10});
4141

42-
test(grpParser.parseMetadata(zarr, setupNode("coordTforms/ts", "s0")),
42+
test(grpParser.parseMetadata(zarr, setupNode(zarr, "coordTforms/ts", "s0")),
4343
new double[]{2, 2},
4444
new double[]{20, 20});
4545

46-
test(grpParser.parseMetadata(zarr, setupNode("coordTforms/tt", "s0")),
46+
test(grpParser.parseMetadata(zarr, setupNode(zarr, "coordTforms/tt", "s0")),
4747
new double[]{1, 1},
4848
new double[]{20, 20});
4949
}
@@ -61,12 +61,17 @@ private void test(final Optional<OmeNgffMetadata> metaOpt, final double[] expect
6161
assertArrayEquals(expectedTranslation, tform.getTranslationCopy(), EPS);
6262
}
6363

64-
private N5TreeNode setupNode(final String path, final String childPath) {
64+
protected static N5TreeNode setupNode(final N5Reader n5, final String path, final String childPath) {
6565

66+
final String absoluteChildPath = path + "/" + childPath;
6667
final N5TreeNode node = new N5TreeNode(path);
67-
final N5TreeNode child = new N5TreeNode(path + "/" + childPath);
68-
final DatasetAttributes attrs = new DatasetAttributes(new long[]{4, 4}, new int[]{4, 4}, DataType.UINT8, new RawCompression());
69-
child.setMetadata(new N5SingleScaleMetadata(path + "/" + childPath, new AffineTransform3D(),
68+
final N5TreeNode child = new N5TreeNode(absoluteChildPath);
69+
70+
DatasetAttributes attrs = n5.getDatasetAttributes(absoluteChildPath);
71+
if (attrs == null) // add dummy attributes of non are present
72+
attrs = new DatasetAttributes(new long[]{4, 4}, new int[]{4, 4}, DataType.UINT8, new RawCompression());
73+
74+
child.setMetadata(new N5SingleScaleMetadata(absoluteChildPath, new AffineTransform3D(),
7075
new double[]{1, 1}, new double[]{1, 1}, new double[]{0, 0}, "", attrs, false));
7176

7277
node.add(child);

src/test/java/org/janelia/saalfeldlab/n5/universe/metadata/ome/ngff/v04/NgffAxisTests.java

+42
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
package org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v04;
22

33
import static org.junit.Assert.assertArrayEquals;
4+
import static org.junit.Assert.assertTrue;
45

6+
import java.net.URI;
7+
import java.nio.file.Paths;
8+
import java.util.Arrays;
9+
import java.util.Optional;
10+
11+
import org.apache.commons.lang3.ArrayUtils;
12+
import org.janelia.saalfeldlab.n5.N5Reader;
13+
import org.janelia.saalfeldlab.n5.universe.N5Factory;
14+
import org.janelia.saalfeldlab.n5.universe.N5TreeNode;
515
import org.janelia.saalfeldlab.n5.universe.metadata.axes.Axis;
616
import org.janelia.saalfeldlab.n5.universe.metadata.axes.AxisUtils;
717
import org.junit.Test;
@@ -84,4 +94,36 @@ public void testSpatial3D() {
8494
st5.getRowPackedCopy(), 1e-9);
8595
}
8696

97+
@Test
98+
public void testAxisOrderStorageOrder() {
99+
100+
URI rootF = Paths.get("src", "test", "resources", "metadata.zarr").toUri();
101+
final N5Reader zarr = new N5Factory().openReader(rootF.toString());
102+
103+
final String[] names = new String[]{"c", "x", "y", "z"};
104+
105+
final OmeNgffMetadataParser parser = new OmeNgffMetadataParser();
106+
107+
// no not flip when f-Order
108+
final N5TreeNode fOrderNode = CoordinateTransformParsingTest.setupNode(zarr, "fOrder", "1");
109+
axisOrderTest(parser.parseMetadata(zarr, fOrderNode), names);
110+
111+
// flip when c-Order
112+
ArrayUtils.reverse(names);
113+
final N5TreeNode cOrderNode = CoordinateTransformParsingTest.setupNode(zarr, "cOrder", "1");
114+
axisOrderTest(parser.parseMetadata(zarr, cOrderNode), names);
115+
}
116+
117+
private void axisOrderTest(final Optional<OmeNgffMetadata> metaOpt, final String[] expectedNames) {
118+
119+
assertTrue("ss not parsable", metaOpt.isPresent());
120+
121+
final OmeNgffMetadata meta = metaOpt.get();
122+
assertTrue("no multiscales found", meta.multiscales.length > 0);
123+
124+
final Axis[] axes = meta.multiscales[0].axes;
125+
final String[] names = Arrays.stream(axes).map(a -> a.getName()).toArray(N -> new String[N]);
126+
assertArrayEquals("names don't match", expectedNames, names);
127+
}
128+
87129
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"multiscales": [
3+
{
4+
"version": "0.4",
5+
"axes": [
6+
{
7+
"name": "c",
8+
"type": "channel"
9+
},
10+
{
11+
"name": "x",
12+
"type": "space",
13+
"unit": "nanometer"
14+
},
15+
{
16+
"name": "y",
17+
"type": "space",
18+
"unit": "nanometer"
19+
},
20+
{
21+
"name": "z",
22+
"type": "space",
23+
"unit": "nanometer"
24+
}
25+
],
26+
"datasets": [
27+
{
28+
"path": "1",
29+
"coordinateTransformations": [
30+
{
31+
"type": "scale",
32+
"scale": [
33+
1.0,
34+
11.239999771118164,
35+
11.239999771118164,
36+
28.0
37+
]
38+
}
39+
]
40+
}
41+
]
42+
}
43+
]
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"zarr_format":2}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"chunks": [ 1, 32, 32, 32 ],
3+
"compressor": {
4+
"blocksize": 0,
5+
"clevel": 3,
6+
"cname": "zstd",
7+
"id": "blosc",
8+
"shuffle": 1
9+
},
10+
"dtype": "|u1",
11+
"fill_value": 0,
12+
"filters": null,
13+
"order": "C",
14+
"shape": [ 1, 4096, 4096, 1536 ],
15+
"zarr_format": 2
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"multiscales": [
3+
{
4+
"version": "0.4",
5+
"axes": [
6+
{
7+
"name": "c",
8+
"type": "channel"
9+
},
10+
{
11+
"name": "x",
12+
"type": "space",
13+
"unit": "nanometer"
14+
},
15+
{
16+
"name": "y",
17+
"type": "space",
18+
"unit": "nanometer"
19+
},
20+
{
21+
"name": "z",
22+
"type": "space",
23+
"unit": "nanometer"
24+
}
25+
],
26+
"datasets": [
27+
{
28+
"path": "1",
29+
"coordinateTransformations": [
30+
{
31+
"type": "scale",
32+
"scale": [
33+
1.0,
34+
11.239999771118164,
35+
11.239999771118164,
36+
28.0
37+
]
38+
}
39+
]
40+
}
41+
]
42+
}
43+
]
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"zarr_format":2}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"chunks": [ 1, 32, 32, 32 ],
3+
"compressor": {
4+
"blocksize": 0,
5+
"clevel": 3,
6+
"cname": "zstd",
7+
"id": "blosc",
8+
"shuffle": 1
9+
},
10+
"dtype": "|u1",
11+
"fill_value": 0,
12+
"filters": null,
13+
"order": "F",
14+
"shape": [ 1, 4096, 4096, 1536 ],
15+
"zarr_format": 2
16+
}

0 commit comments

Comments
 (0)