Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ specification but may be suitable for other applications.
Specifies the version of the [OME-Zarr specification](https://ngff.openmicroscopy.org/specifications/index.html)
that should be used while writing Zarr. Current supported values are 0.4 and 0.5.

If `--ngff-version 0.5` is used, and the output file name ends with `.ozx`, then a single Zip file
will be written in accordance with [RFC-9](https://ngff.openmicroscopy.org/rfc/9/index.html).

#### --pyramid-name

Specifies a subdirectory of the output directory where Zarr data should be written.
Expand Down
32 changes: 22 additions & 10 deletions src/main/java/com/glencoesoftware/bioformats2raw/Converter.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@
import dev.zarr.zarrjava.core.Attributes;
import dev.zarr.zarrjava.core.Group;
import dev.zarr.zarrjava.core.chunkkeyencoding.Separator;
import dev.zarr.zarrjava.store.BufferedZipStore;
import dev.zarr.zarrjava.store.FilesystemStore;
import dev.zarr.zarrjava.store.Store;
import dev.zarr.zarrjava.store.StoreHandle;
import dev.zarr.zarrjava.utils.IndexingUtils;
import dev.zarr.zarrjava.utils.Utils;
Expand All @@ -120,6 +122,8 @@ public class Converter implements Callable<Integer> {
*/
private static final String METADATA_FILE = "METADATA.ome.xml";

private static final String ZIP_EXTENSION = ".ozx";

/**
* Minimum size of the largest XY dimension in the smallest resolution,
* when calculating the number of resolutions to generate.
Expand Down Expand Up @@ -175,7 +179,7 @@ public class Converter implements Callable<Integer> {

private volatile SupportedVersions ngffVersion = SupportedVersions.NGFF_04;
private volatile boolean v3 = false;
private volatile FilesystemStore store = null;
private volatile Store store = null;

private volatile int maxWorkers;
private volatile int maxCachedTiles;
Expand Down Expand Up @@ -1505,6 +1509,7 @@ public void convert()
EnumerationException, ZarrException
{
checkOutputPaths();
initializeStore();

Cache<TilePointer, byte[]> tileCache = CacheBuilder.newBuilder()
.maximumSize(maxCachedTiles)
Expand Down Expand Up @@ -1634,13 +1639,9 @@ public void convert()
}
String xml = service.getOMEXML(meta);

// write the original OME-XML to a file
Path metadataPath = getRootPath().resolve("OME");
if (!Files.exists(metadataPath)) {
Files.createDirectories(metadataPath);
}
Path omexmlFile = metadataPath.resolve(METADATA_FILE);
Files.write(omexmlFile, xml.getBytes(Constants.ENCODING));
// write the original OME-XML to the store
store.set(new String[] {"OME", METADATA_FILE},
ByteBuffer.wrap(xml.getBytes(Constants.ENCODING)));
}
}
catch (ServiceException se) {
Expand Down Expand Up @@ -1702,6 +1703,9 @@ public void convert()
LOGGER.error("Exception while closing reader", e);
}
});
if (store != null && store instanceof BufferedZipStore) {
((BufferedZipStore) store).close();
}
}

// delete the memo file if it was saved and it's not explicitly kept
Expand All @@ -1713,11 +1717,19 @@ public void convert()
}
}

private void writeZarrMetadata() throws IOException, ZarrException {
private void initializeStore() throws IOException, ZarrException {
if (store == null) {
store = new FilesystemStore(getRootPath());
Path rootPath = getRootPath();
if (rootPath.toString().endsWith(ZIP_EXTENSION) && getV3()) {
store = new BufferedZipStore(rootPath, true);
}
else {
store = new FilesystemStore(rootPath);
}
}
}

private void writeZarrMetadata() throws IOException, ZarrException {
// fileset level metadata
if (!noRootGroup) {
Attributes attributes = new Attributes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import com.scalableminds.bloscjava.Blosc;
import dev.zarr.zarrjava.core.Attributes;
import dev.zarr.zarrjava.store.ReadOnlyZipStore;
import dev.zarr.zarrjava.v3.Array;
import dev.zarr.zarrjava.v3.ArrayMetadata;
import dev.zarr.zarrjava.v3.DataType;
Expand Down Expand Up @@ -118,6 +119,69 @@ public void testDefault() throws Exception {
}
}

/**
* Test basic v3 .ozx conversion.
*/
@Test
public void testDefaultZip() throws Exception {
input = fake();
output = output.resolve("zipstore.ozx");
assertTool("--ngff-version", getNGFFVersion());

assertFalse(Files.exists(output.resolve("zarr.json")));
assertFalse(
Files.exists(output.resolve("OME").resolve("METADATA.ome.xml")));
assertTrue(Files.exists(output));

ReadOnlyZipStore zipStore = new ReadOnlyZipStore(output);

Group rootGroup = Group.open(zipStore.resolve());
Attributes attrs = rootGroup.metadata().attributes;
Attributes omeAttrs = attrs.getAttributes("ome");
assertEquals(getNGFFVersion(), omeAttrs.get("version"));
assertEquals(3, omeAttrs.get("bioformats2raw.layout"));

Array array = Array.open(zipStore.resolve("0", "0"));
assertArrayEquals(new long[] {1, 1, 1, 512, 512}, array.metadata().shape);

rootGroup = Group.open(zipStore.resolve("0"));
attrs = rootGroup.metadata().attributes;
omeAttrs = attrs.getAttributes("ome");
assertEquals("0.5", omeAttrs.get("version"));

List<Map<String, Object>> multiscales =
(List<Map<String, Object>>) omeAttrs.get("multiscales");
assertEquals(1, multiscales.size());
Map<String, Object> multiscale = multiscales.get(0);
checkMultiscale(multiscale, "image");

List<Map<String, Object>> datasets =
(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", null);

for (int r=0; r<datasets.size(); r++) {
Map<String, Object> dataset = datasets.get(r);
List<Map<String, Object>> transforms =
(List<Map<String, Object>>) dataset.get("coordinateTransformations");
assertEquals(1, transforms.size());
Map<String, Object> scale = transforms.get(0);
assertEquals("scale", scale.get("type"));
List<Double> axisValues = (List<Double>) scale.get("scale");

assertEquals(5, axisValues.size());
double factor = Math.pow(2, r);
// X and Y are the only dimensions that are downsampled,
// so the TCZ physical scales remain the same across all resolutions
assertEquals(axisValues, Arrays.asList(new Double[] {
1.0, 1.0, 1.0, factor, factor}));
}
}

/**
* Test HCS v3 conversion.
*/
Expand Down Expand Up @@ -231,8 +295,8 @@ public void testNoOMEOption() throws Exception {
input = fake();
assertTool("--no-ome-meta-export", "--ngff-version", getNGFFVersion());

assertTrue(
!Files.exists(output.resolve("OME").resolve("METADATA.ome.xml")));
assertFalse(
Files.exists(output.resolve("OME").resolve("METADATA.ome.xml")));
}

/**
Expand Down