Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store dimension order using new "axes" spec #121

Merged
merged 6 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions src/main/java/com/glencoesoftware/bioformats2raw/Converter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,8 @@ public void saveResolutions(int series)
.nested(nested)
.compressor(CompressorFactory.create(
compressionType.toString(), compressionProperties));
ZarrArray.create(getRootPath().resolve(resolutionString), arrayParams);
ZarrArray.create(getRootPath().resolve(resolutionString),
arrayParams, getArrayAttributes());

nTile = new AtomicInteger(0);
tileCount = (int) Math.ceil((double) scaledWidth / tileWidth)
Expand Down Expand Up @@ -1472,9 +1473,10 @@ private void saveHCSMetadata(IMetadata meta) throws IOException {
* @param resolutions Total number of resolutions from which
* names will be generated.
* @throws IOException
* @throws InterruptedException
*/
private void setSeriesLevelMetadata(int series, int resolutions)
throws IOException
throws IOException, InterruptedException
{
LOGGER.debug("setSeriesLevelMetadata({}, {})", series, resolutions);
String resolutionString = String.format(
Expand Down Expand Up @@ -1514,6 +1516,37 @@ private void setSeriesLevelMetadata(int series, int resolutions)
datasets.add(Collections.singletonMap("path", lastPath));
}
multiscale.put("datasets", datasets);

String axisOrder = null;
if (dimensionOrder != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under which case would dimensionOrder be null? In this scenario, the multi-scale would contain no axes and be invalid or can we infer a default value?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I originally assumed here that dimensionOrder can't really be null, and this is just being extra safe. Going back and looking at the history of the --dimension-order option though, dimensionOrder can be null if --dimension-order original is used. In that case, you're correct that the axes metadata is missing, so I need to fix that here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7595d77 should fix this so that axes is populated for any value of dimensionOrder.

axisOrder = dimensionOrder.toString();
}
else {
IFormatReader reader = readers.take();
try {
axisOrder = reader.getDimensionOrder();
}
finally {
readers.put(reader);
}
}
List<Map<String, String>> axes = new ArrayList<Map<String, String>>();
for (int i=axisOrder.length()-1; i>=0; i--) {
String axis = axisOrder.substring(i, i + 1).toLowerCase();
String type = "space";
if (axis.equals("t")) {
type = "time";
}
else if (axis.equals("c")) {
type = "channel";
}
Map<String, String> thisAxis = new HashMap<String, String>();
thisAxis.put("name", axis);
thisAxis.put("type", type);
axes.add(thisAxis);
}
multiscale.put("axes", axes);

Path subGroupPath = getRootPath().resolve(seriesString);
LOGGER.debug(" creating subgroup {}", subGroupPath);
ZarrGroup subGroup = ZarrGroup.create(subGroupPath);
Expand Down Expand Up @@ -1761,6 +1794,28 @@ private Class<?> getBaseReaderClass() throws FormatException, IOException {
}
}

private Map<String, Object> getArrayAttributes() throws InterruptedException {
Map<String, Object> attrs = new HashMap<String, Object>();
String order = null;
if (dimensionOrder != null) {
order = dimensionOrder.toString();
}
else {
IFormatReader reader = readers.take();
try {
order = reader.getDimensionOrder();
}
finally {
readers.put(reader);
}
}

String[] axes =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned yesterday and as per ome/ngff#96, this block is no longer a requirement of the specification (and is effectively broken in the default layout) so we can safely remove it.

new StringBuilder(order.toLowerCase()).reverse().toString().split("");
attrs.put("_ARRAY_DIMENSIONS", axes);
return attrs;
}

/**
* Check if the given metadata object contains at least one plate
* with at least one well that links to an image.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ public void testMultiscalesMetadata() throws Exception {
(List<Map<String, Object>>) multiscale.get("datasets");
assertTrue(datasets.size() > 0);
assertEquals("0", datasets.get(0).get("path"));

List<Map<String, Object>> axes =
(List<Map<String, Object>>) multiscale.get("axes");
checkAxes(axes, "TCZYX");
}

/**
Expand All @@ -284,6 +288,15 @@ public void testSetXYCZTDimensionOrder() throws Exception {
ZarrGroup z = ZarrGroup.open(output.toString());
ZarrArray array = z.openArray("0/0");
assertArrayEquals(new int[] {1, 1, 2, 512, 512}, array.getShape());

z = ZarrGroup.open(output.resolve("0").toString());
List<Map<String, Object>> multiscales = (List<Map<String, Object>>)
z.getAttributes().get("multiscales");
assertEquals(1, multiscales.size());
Map<String, Object> multiscale = multiscales.get(0);
List<Map<String, Object>> axes =
(List<Map<String, Object>>) multiscale.get("axes");
checkAxes(axes, "TZCYX");
}

/**
Expand All @@ -296,6 +309,15 @@ public void testSetOriginalDimensionOrder() throws Exception {
ZarrGroup z = ZarrGroup.open(output.toString());
ZarrArray array = z.openArray("0/0");
assertArrayEquals(new int[] {1, 1, 2, 512, 512}, array.getShape());

z = ZarrGroup.open(output.resolve("0").toString());
List<Map<String, Object>> multiscales = (List<Map<String, Object>>)
z.getAttributes().get("multiscales");
assertEquals(1, multiscales.size());
Map<String, Object> multiscale = multiscales.get(0);
List<Map<String, Object>> axes =
(List<Map<String, Object>>) multiscale.get("axes");
checkAxes(axes, "TZCYX");
}

/**
Expand Down Expand Up @@ -1413,6 +1435,15 @@ private void checkWell(ZarrGroup wellGroup, int fieldCount)
}
}

private void checkAxes(List<Map<String, Object>> axes, String order) {
assertEquals(axes.size(), order.length());
for (int i=0; i<axes.size(); i++) {
String name = axes.get(i).get("name").toString();
assertEquals(name, order.toLowerCase().substring(i, i + 1));
assertTrue(axes.get(i).containsKey("type"));
}
}

private Path getTestFile(String resourceName) throws IOException {
try {
return Paths.get(this.getClass().getResource(resourceName).toURI());
Expand Down