From fc9faee7b6b5013be6776983a49c054fdb58eea9 Mon Sep 17 00:00:00 2001 From: jmoore Date: Mon, 8 Feb 2021 21:45:30 +0100 Subject: [PATCH 1/6] Nested storage: --(no-)nested flag for "/" chunk sep. Make use of the new ZarrArray.nested flag that's currently open as a PR. This should significantly increase the performance of reading existing zarrs (which also happens during downsampling) when the number of chunk files reaches the millions. see: https://github.com/bcdev/jzarr/pull/17 --- build.gradle | 3 ++- .../glencoesoftware/bioformats2raw/Converter.java | 8 ++++++++ .../bioformats2raw/test/ZarrTest.java | 13 +++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7b489023..4403b42d 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,7 @@ sourceCompatibility = 1.8 targetCompatibility = 1.8 repositories { + mavenLocal() jcenter() maven { url 'https://artifacts.glencoesoftware.com/artifactory/ome.releases' @@ -37,7 +38,7 @@ dependencies { implementation 'ome:formats-gpl:6.5.1' implementation 'info.picocli:picocli:4.2.0' implementation 'com.univocity:univocity-parsers:2.8.4' - implementation 'com.bc.zarr:jzarr:0.3.2' + implementation 'com.bc.zarr:jzarr:0.3.99-SNAPSHOT' implementation 'org.openpnp:opencv:3.4.2-1' diff --git a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java index af7c285d..03b9d755 100644 --- a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java +++ b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java @@ -192,6 +192,13 @@ public class Converter implements Callable { PyramidTiffReader.class, MiraxReader.class }; + @Option( + names = "--nested", negatable=true, + description = "Whether to use '/' as the chunk path seprator " + + "(false by default)" + ) + private volatile boolean nested = false; + @Option( names = "--pyramid-name", description = "Name of pyramid (default: ${DEFAULT-VALUE}) " + @@ -910,6 +917,7 @@ public void saveResolutions(int series) workingReader, scaledWidth, scaledHeight)) .chunks(new int[] {1, 1, 1, activeTileHeight, activeTileWidth}) .dataType(dataType) + .nested(nested) .compressor(CompressorFactory.create( compressionType.toString(), compressionProperties)); root.createArray(resolutionString, arrayParams); diff --git a/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java b/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java index 1e9a030d..c84c94ce 100644 --- a/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java +++ b/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java @@ -32,6 +32,7 @@ import loci.formats.in.FakeReader; import loci.formats.ome.OMEXMLMetadata; import loci.formats.services.OMEXMLService; +import org.junit.jupiter.params.provider.ValueSource; import picocli.CommandLine; import picocli.CommandLine.ExecutionException; @@ -644,4 +645,16 @@ public void testDownsampleTypes(Downsampling type) throws IOException { } } + /** + * Test that nested storage works equivalently. + * + * @param nested whether to use "/" or "." as the chunk separator. + */ + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void testNestedStorage(boolean nested) throws IOException { + input = fake(); + assertTool(nested ? "--nested" : "--no-nested"); + } + } From 2a4c737c7d5a50342f6f05db00e6b6f38f26e4ab Mon Sep 17 00:00:00 2001 From: Melissa Linkert Date: Mon, 22 Mar 2021 12:52:36 -0500 Subject: [PATCH 2/6] Remove "data.zarr" from output hierarchy by default. The old behavior can be preserved using "--pyramid-name data.zarr" --- .../bioformats2raw/Converter.java | 31 +++++--- .../bioformats2raw/test/ZarrTest.java | 78 +++++++------------ 2 files changed, 51 insertions(+), 58 deletions(-) diff --git a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java index cdf344d2..1636207b 100644 --- a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java +++ b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java @@ -221,7 +221,7 @@ public class Converter implements Callable { description = "Name of pyramid (default: ${DEFAULT-VALUE}) " + "[Can break compatibility with raw2ometiff]" ) - private volatile String pyramidName = "data.zarr"; + private volatile String pyramidName = null; @Option( names = "--scale-format-string", @@ -511,7 +511,7 @@ public void convert() String xml = getService().getOMEXML(meta); // write the original OME-XML to a file - Path metadataPath = outputPath.resolve(pyramidName); + Path metadataPath = getRootPath(); if (!Files.exists(metadataPath)) { Files.createDirectories(metadataPath); } @@ -580,6 +580,21 @@ public void write(int series) saveResolutions(series); } + /** + * Get the root path of the dataset, which will contain Zarr and OME-XML. + * By default, this is the specified output directory. + * If "--pyramid-name" was used, then this will be the pyramid name + * as a subdirectory of the output directory. + * + * @return directory into which Zarr and OME-XML data is written + */ + private Path getRootPath() { + if (pyramidName == null) { + return outputPath; + } + return outputPath.resolve(pyramidName); + } + /** * Retrieves the scaled format string arguments, adding additional ones * from a provided CSV file. @@ -771,7 +786,7 @@ private byte[] getTileDownsampled( final String pathName = String.format(scaleFormatString, getScaleFormatStringArgs(series, resolution - 1)); - final ZarrGroup root = ZarrGroup.open(outputPath.resolve(pyramidName)); + final ZarrGroup root = ZarrGroup.open(getRootPath()); final ZarrArray zarr = root.openArray(pathName); int[] dimensions = zarr.getShape(); @@ -902,7 +917,7 @@ private void processTile( String pathName = String.format(scaleFormatString, getScaleFormatStringArgs(series, resolution)); - final ZarrGroup root = ZarrGroup.open(outputPath.resolve(pyramidName)); + final ZarrGroup root = ZarrGroup.open(getRootPath()); final ZarrArray zarr = root.openArray(pathName); IFormatReader reader = readers.take(); boolean littleEndian = reader.isLittleEndian(); @@ -990,7 +1005,7 @@ public void saveResolutions(int series) ); // fileset level metadata - final String pyramidPath = outputPath.resolve(pyramidName).toString(); + final String pyramidPath = getRootPath().toString(); final ZarrGroup root = ZarrGroup.create(pyramidPath); Map attributes = new HashMap(); attributes.put("bioformats2raw.layout", LAYOUT); @@ -1116,7 +1131,7 @@ private void saveHCSMetadata(IMetadata meta) throws IOException { } // assumes only one plate defined - Path rootPath = outputPath.resolve(pyramidName); + Path rootPath = getRootPath(); ZarrGroup root = ZarrGroup.open(rootPath); int plate = 0; Map plateMap = new HashMap(); @@ -1517,9 +1532,7 @@ public static DataType getZarrType(int type) { } private void checkOutputPaths() { - if ((!pyramidName.equals("data.zarr")) || - !scaleFormatString.equals("%d/%d")) - { + if (pyramidName != null || !scaleFormatString.equals("%d/%d")) { LOGGER.info("Output will be incompatible with raw2ometiff " + "(pyramidName: {}, scaleFormatString: {})", pyramidName, scaleFormatString); diff --git a/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java b/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java index 5769dc54..2b3918e6 100644 --- a/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java +++ b/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java @@ -88,9 +88,8 @@ void assertTool(String...additionalArgs) throws IOException { try { converter = new Converter(); CommandLine.call(converter, args.toArray(new String[]{})); - Path zarr = output.resolve("data.zarr"); - assertTrue(Files.exists(zarr)); - assertTrue(Files.exists(zarr.resolve("METADATA.ome.xml"))); + assertTrue(Files.exists(output.resolve(".zattrs"))); + assertTrue(Files.exists(output.resolve("METADATA.ome.xml"))); } catch (RuntimeException rt) { throw rt; @@ -204,10 +203,9 @@ public void testAdditionalScaleFormatStringArgs() throws Exception { "--scale-format-string", "%3$s/%4$s/%1$s/%2$s", "--additional-scale-format-string-args", csv.toString() ); - Path root = output.resolve("data.zarr"); - ZarrGroup series0 = ZarrGroup.open(root.resolve("abc/888/0").toString()); + ZarrGroup series0 = ZarrGroup.open(output.resolve("abc/888/0").toString()); series0.openArray("0"); - series0 = ZarrGroup.open(root.resolve("ghi/999/1").toString()); + series0 = ZarrGroup.open(output.resolve("ghi/999/1").toString()); series0.openArray("0"); } @@ -218,8 +216,7 @@ public void testAdditionalScaleFormatStringArgs() throws Exception { public void testDefaultLayoutIsSet() throws Exception { input = fake(); assertTool(); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); Integer layout = (Integer) z.getAttributes().get("bioformats2raw.layout"); assertEquals(Converter.LAYOUT, layout); @@ -233,7 +230,7 @@ public void testMultiscalesMetadata() throws Exception { input = fake(); assertTool(); ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").resolve("0").toString()); + ZarrGroup.open(output.resolve("0").toString()); List> multiscales = (List>) z.getAttributes().get("multiscales"); assertEquals(1, multiscales.size()); @@ -252,8 +249,7 @@ public void testMultiscalesMetadata() throws Exception { public void testXYCZTDimensionOrder() throws Exception { input = fake("sizeC", "2", "dimOrder", "XYCZT"); assertTool(); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); ZarrArray array = z.openArray("0/0"); assertArrayEquals(new int[] {1, 2, 1, 512, 512}, array.getShape()); } @@ -265,8 +261,7 @@ public void testXYCZTDimensionOrder() throws Exception { public void testSetXYCZTDimensionOrder() throws Exception { input = fake("sizeC", "2"); assertTool("--dimension-order", "XYCZT"); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); ZarrArray array = z.openArray("0/0"); assertArrayEquals(new int[] {1, 1, 2, 512, 512}, array.getShape()); } @@ -278,8 +273,7 @@ public void testSetXYCZTDimensionOrder() throws Exception { public void testSetOriginalDimensionOrder() throws Exception { input = fake("sizeC", "2", "dimOrder", "XYCZT"); assertTool("--dimension-order", "original"); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); ZarrArray array = z.openArray("0/0"); assertArrayEquals(new int[] {1, 1, 2, 512, 512}, array.getShape()); } @@ -291,8 +285,7 @@ public void testSetOriginalDimensionOrder() throws Exception { public void testSetSmallerDefault() throws Exception { input = fake(); assertTool("-h", "128", "-w", "128"); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); ZarrArray array = z.openArray("0/0"); assertArrayEquals(new int[] {1, 1, 1, 512, 512}, array.getShape()); assertArrayEquals(new int[] {1, 1, 1, 128, 128}, array.getChunks()); @@ -306,8 +299,7 @@ public void testSetSmallerDefault() throws Exception { public void testSetSmallerDefaultWithRemainder() throws Exception { input = fake(); assertTool("-h", "384", "-w", "384"); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); ZarrArray array = z.openArray("0/0"); assertArrayEquals(new int[] {1, 1, 1, 512, 512}, array.getShape()); assertArrayEquals(new int[] {1, 1, 1, 384, 384}, array.getChunks()); @@ -320,8 +312,7 @@ public void testSetSmallerDefaultWithRemainder() throws Exception { public void testMultiSeries() throws Exception { input = fake("series", "2"); assertTool(); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); // Check series 0 dimensions and special pixels ZarrArray series0 = z.openArray("0/0"); @@ -349,8 +340,7 @@ public void testMultiSeries() throws Exception { public void testSingleBeginningSeries() throws Exception { input = fake("series", "2"); assertTool("-s", "0"); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); // Check series 0 dimensions and special pixels ZarrArray series0 = z.openArray("0/0"); @@ -377,8 +367,7 @@ public void testSingleBeginningSeries() throws Exception { public void testSingleEndSeries() throws Exception { input = fake("series", "2"); assertTool("-s", "1"); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); // Check series 1 dimensions and special pixels ZarrArray series0 = z.openArray("0/0"); @@ -405,8 +394,7 @@ public void testSingleEndSeries() throws Exception { public void testSingleMiddleSeries() throws Exception { input = fake("series", "3"); assertTool("-s", "1"); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); // Check series 1 dimensions and special pixels ZarrArray series0 = z.openArray("0/0"); @@ -440,8 +428,7 @@ public void testSingleMiddleSeries() throws Exception { public void testMultiZ() throws Exception { input = fake("sizeZ", "2"); assertTool(); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); // Check dimensions and block size ZarrArray series0 = z.openArray("0/0"); @@ -468,8 +455,7 @@ public void testMultiZ() throws Exception { public void testMultiC() throws Exception { input = fake("sizeC", "2"); assertTool(); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); // Check dimensions and block size ZarrArray series0 = z.openArray("0/0"); @@ -496,8 +482,7 @@ public void testMultiC() throws Exception { public void testMultiT() throws Exception { input = fake("sizeT", "2"); assertTool(); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); // Check dimensions and block size ZarrArray series0 = z.openArray("0/0"); @@ -548,8 +533,7 @@ private int bytesPerPixel(DataType dataType) { public void testPixelType(String type, DataType dataType) throws Exception { input = fake("pixelType", type); assertTool(); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); // Check series dimensions and special pixels ZarrArray series0 = z.openArray("0/0"); @@ -623,8 +607,7 @@ static Stream getPixelTypes() { public void testDownsampleEdgeEffectsUInt8() throws Exception { input = fake("sizeX", "60", "sizeY", "300"); assertTool("-w", "25", "-h", "75"); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); // Check series dimensions ZarrArray series1 = z.openArray("0/1"); @@ -647,8 +630,7 @@ public void testDownsampleEdgeEffectsUInt8() throws Exception { public void testDownsampleEdgeEffectsUInt16() throws Exception { input = fake("sizeX", "60", "sizeY", "300", "pixelType", "uint16"); assertTool("-w", "25", "-h", "75"); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").toString()); + ZarrGroup z = ZarrGroup.open(output.toString()); ZarrArray series0 = z.openArray("0/0"); assertEquals(DataType.u2, series0.getDataType()); @@ -688,7 +670,7 @@ public void testOriginalMetadata() throws Exception { input = fake(null, null, originalMetadata); assertTool(); - Path omexml = output.resolve("data.zarr").resolve("METADATA.ome.xml"); + Path omexml = output.resolve("METADATA.ome.xml"); StringBuilder xml = new StringBuilder(); Files.lines(omexml).forEach(v -> xml.append(v)); @@ -738,8 +720,7 @@ public void testDownsampleTypes(Downsampling type) throws IOException { input = fake(); assertTool("--downsample-type", type.toString()); - ZarrGroup z = - ZarrGroup.open(output.resolve("data.zarr").resolve("0").toString()); + ZarrGroup z = ZarrGroup.open(output.resolve("0").toString()); List> multiscales = (List>) z.getAttributes().get("multiscales"); assertEquals(1, multiscales.size()); @@ -778,7 +759,7 @@ public void testNoHCSOption() throws IOException { "plateRows", "2", "plateCols", "3", "fields", "2"); assertTool("--no-hcs"); - ZarrGroup z = ZarrGroup.open(output.resolve("data.zarr")); + ZarrGroup z = ZarrGroup.open(output); // Check dimensions and block size ZarrArray series0 = z.openArray("0/0"); @@ -798,14 +779,13 @@ public void testHCSMetadata() throws IOException { "plateRows", "2", "plateCols", "3", "fields", "2"); assertTool(); - Path root = output.resolve("data.zarr"); - ZarrGroup z = ZarrGroup.open(root); + ZarrGroup z = ZarrGroup.open(output); // check valid group layout // METADATA.ome.xml, .zattrs (Plate), .zgroup (Plate) and 2 rows - assertEquals(5, Files.list(root).toArray().length); + assertEquals(5, Files.list(output).toArray().length); for (int row=0; row<2; row++) { - Path rowPath = root.resolve(Integer.toString(row)); + Path rowPath = output.resolve(Integer.toString(row)); // .zgroup (Row) and 3 columns assertEquals(4, Files.list(rowPath).toArray().length); for (int col=0; col<3; col++) { @@ -863,7 +843,7 @@ public void testHCSMetadata() throws IOException { for (Map column : columns) { String columnName = (String) column.get("name"); ZarrGroup wellGroup = ZarrGroup.open( - root.resolve(rowName).resolve(columnName)); + output.resolve(rowName).resolve(columnName)); Map well = (Map) wellGroup.getAttributes().get("well"); List> images = @@ -887,7 +867,7 @@ public void testRGBChannelSeparator() throws Exception { input = fake("sizeC", "3", "rgb", "3"); assertTool(); - Path xml = output.resolve("data.zarr").resolve("METADATA.ome.xml"); + Path xml = output.resolve("METADATA.ome.xml"); ServiceFactory sf = new ServiceFactory(); OMEXMLService xmlService = sf.getInstance(OMEXMLService.class); OME ome = (OME) xmlService.createOMEXMLRoot( From 4d61e23b83564171e8a71b85800f12026b1e4a3c Mon Sep 17 00:00:00 2001 From: jmoore Date: Tue, 23 Mar 2021 10:06:31 +0100 Subject: [PATCH 3/6] Update to the Glencoe snapshot --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index dfbe9f4b..b2a6b177 100644 --- a/build.gradle +++ b/build.gradle @@ -38,7 +38,7 @@ dependencies { implementation 'ome:formats-gpl:6.5.1' implementation 'info.picocli:picocli:4.2.0' implementation 'com.univocity:univocity-parsers:2.8.4' - implementation 'com.bc.zarr:jzarr:0.3.99-SNAPSHOT' + implementation 'com.bc.zarr:jzarr:0.3.3-gs-SNAPSHOT' implementation 'org.openpnp:opencv:3.4.2-1' implementation 'me.tongfei:progressbar:0.9.0' From 7565da66fd925a75883376b89e070a3ee5f82cb7 Mon Sep 17 00:00:00 2001 From: jmoore Date: Tue, 23 Mar 2021 10:46:13 +0100 Subject: [PATCH 4/6] Add jzarr-snapshots repo --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index b2a6b177..53daf2a9 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,9 @@ repositories { maven { url 'https://repo.glencoesoftware.com/repository/n5-zarr-snapshots/' } + maven { + url 'https://repo.glencoesoftware.com/repository/jzarr-snapshots' + } maven { url 'https://maven.scijava.org/content/groups/public' } From 8ed20dc2394c128fc54370a172371d91702c1865 Mon Sep 17 00:00:00 2001 From: Chris Allan Date: Wed, 24 Mar 2021 12:53:05 +0000 Subject: [PATCH 5/6] Do not assume a root group and allow it to be disabled --- .../bioformats2raw/Converter.java | 43 +++++++++++-------- .../bioformats2raw/test/ZarrTest.java | 26 ++++++++++- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java index 1636207b..60a757c2 100644 --- a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java +++ b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java @@ -305,6 +305,14 @@ public class Converter implements Callable { ) private volatile boolean noHCS = false; + @Option( + names = "--no-root-group", + description = "Turn off creation of root group and corresponding " + + "metadata [Will break compatibility with raw2ometiff]" + + ) + private volatile boolean noRootGroup = false; + /** Scaling implementation that will be used during downsampling. */ private volatile IImageScaler scaler = new SimpleImageScaler(); @@ -786,9 +794,7 @@ private byte[] getTileDownsampled( final String pathName = String.format(scaleFormatString, getScaleFormatStringArgs(series, resolution - 1)); - final ZarrGroup root = ZarrGroup.open(getRootPath()); - final ZarrArray zarr = root.openArray(pathName); - + final ZarrArray zarr = ZarrArray.open(getRootPath().resolve(pathName)); int[] dimensions = zarr.getShape(); int[] blockSizes = zarr.getChunks(); int activeTileWidth = blockSizes[blockSizes.length - 1]; @@ -917,8 +923,7 @@ private void processTile( String pathName = String.format(scaleFormatString, getScaleFormatStringArgs(series, resolution)); - final ZarrGroup root = ZarrGroup.open(getRootPath()); - final ZarrArray zarr = root.openArray(pathName); + final ZarrArray zarr = ZarrArray.open(getRootPath().resolve(pathName)); IFormatReader reader = readers.take(); boolean littleEndian = reader.isLittleEndian(); int bpp = FormatTools.getBytesPerPixel(reader.getPixelType()); @@ -1005,14 +1010,15 @@ public void saveResolutions(int series) ); // fileset level metadata - final String pyramidPath = getRootPath().toString(); - final ZarrGroup root = ZarrGroup.create(pyramidPath); - Map attributes = new HashMap(); - attributes.put("bioformats2raw.layout", LAYOUT); - root.writeAttributes(attributes); + if (!noRootGroup) { + final ZarrGroup root = ZarrGroup.create(getRootPath()); + Map attributes = new HashMap(); + attributes.put("bioformats2raw.layout", LAYOUT); + root.writeAttributes(attributes); + } // series level metadata - setSeriesLevelMetadata(root, series, resolutions); + setSeriesLevelMetadata(series, resolutions); for (int resCounter=0; resCounter> datasets = new ArrayList>(); for (int r = 0; r < resolutions; r++) { - resolutionString = "/" + String.format( + resolutionString = String.format( scaleFormatString, getScaleFormatStringArgs(series, r)); String lastPath = resolutionString.substring( resolutionString.lastIndexOf('/') + 1); datasets.add(Collections.singletonMap("path", lastPath)); } multiscale.put("datasets", datasets); - ZarrGroup subGroup = root.createSubGroup(seriesString); + ZarrGroup subGroup = ZarrGroup.create(getRootPath().resolve(seriesString)); Map attributes = new HashMap(); attributes.put("multiscales", multiscales); subGroup.writeAttributes(attributes); diff --git a/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java b/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java index 2b3918e6..03f4a19d 100644 --- a/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java +++ b/src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java @@ -88,7 +88,6 @@ void assertTool(String...additionalArgs) throws IOException { try { converter = new Converter(); CommandLine.call(converter, args.toArray(new String[]{})); - assertTrue(Files.exists(output.resolve(".zattrs"))); assertTrue(Files.exists(output.resolve("METADATA.ome.xml"))); } catch (RuntimeException rt) { @@ -877,4 +876,29 @@ public void testRGBChannelSeparator() throws Exception { assertEquals(3, pixels.sizeOfChannelList()); } + + /** + * Check that a root group and attributes are created and populated. + */ + @Test + public void testRootGroup() throws Exception { + input = fake(); + assertTool(); + + assertTrue(Files.exists(output.resolve(".zattrs"))); + assertTrue(Files.exists(output.resolve(".zgroup"))); + } + + /** + * Convert with the --no-root-group option. Conversion should succeed but + * no root group or attributes should be created or populated. + */ + @Test + public void testNoRootGroupOption() throws Exception { + input = fake(); + assertTool("--no-root-group"); + + assertTrue(!Files.exists(output.resolve(".zattrs"))); + assertTrue(!Files.exists(output.resolve(".zgroup"))); + } } From d4951000fecbb8785b383c144f89b496bbd1c65c Mon Sep 17 00:00:00 2001 From: Chris Allan Date: Wed, 24 Mar 2021 12:56:39 +0000 Subject: [PATCH 6/6] Fix unused Javadoc param tag --- src/main/java/com/glencoesoftware/bioformats2raw/Converter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java index 60a757c2..08ec1da8 100644 --- a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java +++ b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java @@ -1266,7 +1266,6 @@ private void saveHCSMetadata(IMetadata meta) throws IOException { * to attach the multiscales metadata to the group containing * the pyramids. * - * @param root Root {@link ZarrGroup}. * @param series Series which is currently being written. * @param resolutions Total number of resolutions from which * names will be generated.