diff --git a/build.gradle b/build.gradle index 1add4db0..3ad0d2a6 100644 --- a/build.gradle +++ b/build.gradle @@ -42,7 +42,7 @@ configurations.all { dependencies { implementation 'net.java.dev.jna:jna:5.10.0' - implementation 'ome:formats-gpl:8.5.0' + implementation 'ome:formats-gpl:9.0.0-SNAPSHOT' implementation 'info.picocli:picocli:4.7.5' implementation 'com.univocity:univocity-parsers:2.8.4' implementation 'dev.zarr:zarr-java:0.1.3' diff --git a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java index d506ee1f..a7b4d09e 100644 --- a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java +++ b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java @@ -46,11 +46,16 @@ import loci.formats.ClassList; import loci.formats.FormatException; import loci.formats.FormatTools; +import loci.formats.IAxisOrientationReader; import loci.formats.IFormatReader; import loci.formats.ImageReader; import loci.formats.Memoizer; import loci.formats.MinMaxCalculator; import loci.formats.MissingLibraryException; +import loci.formats.Orientation; +import loci.formats.OrientationTerm; +import loci.formats.OrientationType; +import loci.formats.ReaderWrapper; import loci.formats.in.DynamicMetadataOptions; import loci.formats.meta.IMetadata; import loci.formats.ome.OMEXMLMetadata; @@ -3060,7 +3065,27 @@ else if (axisChar.equals("y")) { } multiscale.put("datasets", datasets); - List> axes = new ArrayList>(); + Orientation[] axisOrientations = null; + String orientationOrder = null; + try { + v = readers.take(); + if (v instanceof ReaderWrapper) { + IFormatReader unwrapped = ((ReaderWrapper) v).unwrap(); + if (unwrapped instanceof IAxisOrientationReader) { + axisOrientations = + ((IAxisOrientationReader) unwrapped).getAxisOrientations(); + orientationOrder = unwrapped.getDimensionOrder().toLowerCase(); + } + } + } + catch (FormatException e) { + LOGGER.error("Could not get axis orientations", e); + } + finally { + readers.put(v); + } + + List> axes = new ArrayList>(); for (int i=0; i thisAxis = new HashMap(); + Map thisAxis = new HashMap(); thisAxis.put("name", axis); thisAxis.put("type", type); if (scale != null) { @@ -3094,6 +3119,25 @@ else if (scale instanceof Time) { thisAxis.put("unit", unitName); } } + if (axisOrientations != null && orientationOrder != null) { + int orientationIndex = orientationOrder.indexOf(axis); + if (orientationIndex >= 0) { + Orientation axisOrientation = axisOrientations[orientationIndex]; + Map orientationMap = null; + if (axisOrientation != null) { + orientationMap = new HashMap(); + OrientationType orientType = axisOrientation.getOrientationType(); + OrientationTerm orientTerm = axisOrientation.getOrientationTerm(); + if (orientType != null) { + orientationMap.put("type", orientType.getDefinedType()); + } + if (orientTerm != null) { + orientationMap.put("value", orientTerm.getDefinedTerm()); + } + } + thisAxis.put("orientation", orientationMap); + } + } axes.add(thisAxis); } multiscale.put("axes", axes); diff --git a/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrV3Test.java b/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrV3Test.java index 1976847d..25f093fb 100644 --- a/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrV3Test.java +++ b/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrV3Test.java @@ -10,6 +10,7 @@ import java.nio.ByteOrder; import java.nio.file.Files; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -40,6 +41,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -491,6 +493,70 @@ public void testPixelType(String type, DataType dataType) throws Exception { assertFalse(ome.getImage(0).getPixels().getBigEndian()); } + /** + * Test anatomical orientation (RFC-4). + */ + @Test + public void testAnatomicalOrientation() throws Exception { + HashMap opts = new HashMap(); + opts.put("sizeZ", "4"); + opts.put("sizeC", "3"); + opts.put("sizeT", "2"); + HashMap seriesOpts = new HashMap(); + seriesOpts.put("AxisCount", "5"); + seriesOpts.put("AxisOrientationType_0", "anatomical"); + seriesOpts.put("AxisOrientationTerm_0", "left-to-right"); + seriesOpts.put("AxisOrientationType_1", "anatomical"); + seriesOpts.put("AxisOrientationTerm_1", "posterior-to-anterior"); + seriesOpts.put("AxisOrientationType_2", "anatomical"); + seriesOpts.put("AxisOrientationTerm_2", "rostral-to-caudal"); + Map> allSeries = + new HashMap>(); + allSeries.put(0, seriesOpts); + input = fake(opts, allSeries); + + assertTool("--ngff-version", getNGFFVersion()); + + Array array = Array.open(store.resolve("0", "0")); + assertArrayEquals(new long[] {2, 3, 4, 512, 512}, array.metadata().shape); + + Group rootGroup = Group.open(store.resolve("0")); + Attributes attrs = rootGroup.metadata().attributes; + Attributes omeAttrs = attrs.getAttributes("ome"); + + List> multiscales = + (List>) omeAttrs.get("multiscales"); + assertEquals(1, multiscales.size()); + Map multiscale = multiscales.get(0); + checkMultiscale(multiscale, "image"); + + List> datasets = + (List>) multiscale.get("datasets"); + assertTrue(datasets.size() > 0); + assertEquals("0", datasets.get(0).get("path")); + + List> axes = + (List>) multiscale.get("axes"); + checkAxes(axes, "TCZYX", null); + + for (int i=0; i axis = axes.get(i); + int originalAxisIndex = axes.size() - i - 1; + if (axis.get("orientation") == null) { + assertNull(seriesOpts.get("AxisOrientationType_" + originalAxisIndex)); + assertNull(seriesOpts.get("AxisOrientationTerm_" + originalAxisIndex)); + } + else { + Map orientation = + (Map) axis.get("orientation"); + assertEquals(orientation.get("type"), + seriesOpts.get("AxisOrientationType_" + originalAxisIndex)); + assertEquals(orientation.get("value"), + seriesOpts.get("AxisOrientationTerm_" + originalAxisIndex)); + } + } + } + /** * @return pairs of pixel type strings and Zarr data types */