From 53ec195bd4337e182d955f7773053c7e81eac61a Mon Sep 17 00:00:00 2001 From: Melissa Linkert Date: Tue, 8 Feb 2022 15:15:12 -0600 Subject: [PATCH] First try at scale transforms See https://github.com/ome/ngff/pull/57 --- .../bioformats2raw/Converter.java | 92 +++++++++++++++---- .../bioformats2raw/test/ZarrTest.java | 38 ++++++++ 2 files changed, 114 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java index ae0f4674..54536142 100644 --- a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java +++ b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java @@ -19,7 +19,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -56,6 +55,7 @@ import loci.formats.ome.OMEXMLMetadata; import loci.formats.services.OMEXMLService; import loci.formats.services.OMEXMLServiceImpl; +import ome.units.quantity.Quantity; import ome.xml.meta.OMEXMLMetadataRoot; import ome.xml.model.enums.DimensionOrder; import ome.xml.model.enums.EnumerationException; @@ -1506,33 +1506,63 @@ private void setSeriesLevelMetadata(int series, int resolutions) multiscale.put("metadata", metadata); multiscale.put("version", nested ? "0.2" : "0.1"); multiscales.add(multiscale); - List> datasets = new ArrayList>(); + + IFormatReader v = null; + IMetadata meta = null; + String axisOrder = null; + try { + v = readers.take(); + meta = (IMetadata) v.getMetadataStore(); + + if (dimensionOrder != null) { + axisOrder = dimensionOrder.toString(); + } + else { + axisOrder = v.getDimensionOrder(); + } + } + finally { + readers.put(v); + } + + List> datasets = new ArrayList>(); for (int r = 0; r < resolutions; r++) { resolutionString = String.format( scaleFormatString, getScaleFormatStringArgs(series, r)); String lastPath = resolutionString.substring( resolutionString.lastIndexOf('/') + 1); - datasets.add(Collections.singletonMap("path", lastPath)); - } - multiscale.put("datasets", datasets); - String axisOrder = null; - if (dimensionOrder != null) { - axisOrder = dimensionOrder.toString(); - } - else { - IFormatReader reader = readers.take(); - try { - axisOrder = reader.getDimensionOrder(); - } - finally { - readers.put(reader); + List> transforms = + new ArrayList>(); + Map scale = new HashMap(); + scale.put("type", "scale"); + List axisValues = new ArrayList(); + List axisIndices = new ArrayList(); + for (int i=axisOrder.length()-1; i>=0; i--) { + Quantity axisScale = getScale(meta, series, axisOrder, i); + if (axisScale != null) { + // TODO: does this need to be recalculated for r > 0? + axisValues.add(axisScale.value().doubleValue()); + axisIndices.add(axisOrder.length() - i - 1); + } } + scale.put("scale", axisValues); + scale.put("axisIndices", axisIndices); + + transforms.add(scale); + + Map dataset = new HashMap(); + dataset.put("path", lastPath); + dataset.put("transformations", transforms); + datasets.add(dataset); } + multiscale.put("datasets", datasets); + List> axes = new ArrayList>(); for (int i=axisOrder.length()-1; i>=0; i--) { String axis = axisOrder.substring(i, i + 1).toLowerCase(); String type = "space"; + Quantity scale = getScale(meta, series, axisOrder, i); if (axis.equals("t")) { type = "time"; } @@ -1542,6 +1572,9 @@ else if (axis.equals("c")) { Map thisAxis = new HashMap(); thisAxis.put("name", axis); thisAxis.put("type", type); + if (scale != null) { + thisAxis.put("unit", scale.unit().getSymbol()); + } axes.add(thisAxis); } multiscale.put("axes", axes); @@ -1555,6 +1588,33 @@ else if (axis.equals("c")) { LOGGER.debug(" finished writing subgroup attributes"); } + private Quantity getScale( + IMetadata meta, int series, String axisOrder, int axis) + { + if (meta == null) { + return null; + } + int seriesIndex = seriesList.indexOf(series); + + if (seriesIndex < 0 || seriesIndex >= meta.getImageCount()) { + return null; + } + + String axisChar = axisOrder.substring(axis, axis + 1).toLowerCase(); + switch (axisChar.charAt(0)) { + case 'x': + return meta.getPixelsPhysicalSizeX(seriesIndex); + case 'y': + return meta.getPixelsPhysicalSizeY(seriesIndex); + case 'z': + return meta.getPixelsPhysicalSizeZ(seriesIndex); + case 't': + return meta.getPixelsTimeIncrement(seriesIndex); + default: + return null; + } + } + /** * Takes exception from asynchronous execution and re-throw known exception * types. If the end is reached with no known exception detected, either the diff --git a/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java b/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java index 1a166ef1..5acf8696 100644 --- a/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java +++ b/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java @@ -320,6 +320,44 @@ public void testSetOriginalDimensionOrder() throws Exception { checkAxes(axes, "TZCYX"); } + /** + * Test that physical sizes are saved in axes/transformations metadata. + */ + @Test + public void testPhysicalSizes() throws Exception { + input = fake("physicalSizeX", "1.0mm", + "physicalSizeY", "0.5mm", + "physicalSizeZ", "2cm"); + assertTool(); + + ZarrGroup z = ZarrGroup.open(output.resolve("0").toString()); + List> multiscales = (List>) + z.getAttributes().get("multiscales"); + assertEquals(1, multiscales.size()); + Map multiscale = multiscales.get(0); + List> axes = + (List>) multiscale.get("axes"); + checkAxes(axes, "TCZYX"); + + List> datasets = + (List>) multiscale.get("datasets"); + assertEquals(2, datasets.size()); + + for (Map dataset : datasets) { + List> transforms = + (List>) dataset.get("transformations"); + assertEquals(1, transforms.size()); + Map scale = transforms.get(0); + assertEquals("scale", scale.get("type")); + List axisIndices = (List) scale.get("axisIndices"); + List axisValues = (List) scale.get("scale"); + + assertEquals(axisIndices.size(), axisValues.size()); + assertEquals(axisIndices, Arrays.asList(new Integer[] {2, 3, 4})); + assertEquals(axisValues, Arrays.asList(new Double[] {2.0, 0.5, 1.0})); + } + } + /** * Test using a different tile size from the default (1024). */