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

Fix boundary issues for ome-zarr writing #55

Merged
merged 4 commits into from
Jan 30, 2022
Merged
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
17 changes: 16 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@

<!-- EMBL CBA -->
<imagej-utils.version>0.6.4</imagej-utils.version>

<!-- spim_data.version required for fix that closes xml files properly after creation - should soon be in main fiji -->
<spim_data.version>2.2.5</spim_data.version>

<!-- NB: Deploy releases to the SciJava Maven repository. -->
<releaseProfiles>sign,deploy-to-scijava</releaseProfiles>
</properties>
Expand Down Expand Up @@ -148,6 +152,11 @@
<groupId>sc.fiji</groupId>
<artifactId>bigdataviewer_fiji</artifactId>
</dependency>
<dependency>
<groupId>sc.fiji</groupId>
<artifactId>spim_data</artifactId>
<version>${spim_data.version}</version>
</dependency>
<dependency>
<groupId>org.janelia.saalfeldlab</groupId>
<artifactId>n5-aws-s3</artifactId>
Expand Down Expand Up @@ -203,7 +212,13 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.5.2</version>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ static GsonBuilder initGsonBuilder(final GsonBuilder gsonBuilder) {

gsonBuilder.registerTypeAdapter(DType.class, new DType.JsonAdapter());
gsonBuilder.registerTypeAdapter(ZarrCompressor.class, ZarrCompressor.jsonAdapter);
gsonBuilder.serializeNulls();
gsonBuilder.registerTypeAdapter(ZarrAxes.class, new ZarrAxesAdapter());
gsonBuilder.registerTypeAdapter(N5Reader.Version.class, new VersionAdapter());
gsonBuilder.setPrettyPrinting();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import net.imglib2.util.Cast;
import org.embl.mobie.io.n5.util.DownsampleBlock;
import org.embl.mobie.io.n5.util.ExportScalePyramid;
import org.embl.mobie.io.ome.zarr.util.N5OMEZarrCacheArrayLoader;
import org.embl.mobie.io.ome.zarr.util.OmeZarrMultiscales;
import org.embl.mobie.io.ome.zarr.util.ZarrAxes;
import org.embl.mobie.io.ome.zarr.util.ZarrDatasetAttributes;
Expand Down Expand Up @@ -337,6 +338,22 @@ private long[] addSetupAndTimeToShape(long[] zyxShape) {
return shape;
}

private long[] removeSetupAndTimeFromShape( long[] shape ) {
long[] xyzShape = new long[3];
for (int i = 0; i < 3; i++) {
xyzShape[i] = shape[i];
}
return xyzShape;
}

private int[] removeSetupAndTimeFromShape( int[] shape ) {
int[] xyzShape = new int[3];
for (int i = 0; i < 3; i++) {
xyzShape[i] = shape[i];
}
return xyzShape;
}

@Override
public OmeZarrDataset createDataset(final int level, final long[] zyxDimensions, final int[] zyxBlockSize) throws IOException {
// create dataset directory + metadata
Expand Down Expand Up @@ -370,12 +387,14 @@ public void flush(final OmeZarrDataset dataset) {

@Override
public RandomAccessibleInterval<T> getImage(final int level) throws IOException {
final String pathName = getPathName(level);
final DatasetAttributes attributes = zarrWriter.getDatasetAttributes("s" + level);
final long[] dimensions = attributes.getDimensions();
final int[] cellDimensions = attributes.getBlockSize();
// this needs to return the zyx image for the current timepoint and channel (as we are only chunking in zyx)
final String pathName = String.format("s%d", level); // only include level as rest is handled by N5OmeZarrCacheArrayLoader
final DatasetAttributes attributes = zarrWriter.getDatasetAttributes( pathName );
final long[] dimensions = removeSetupAndTimeFromShape( attributes.getDimensions() );
final int[] cellDimensions = removeSetupAndTimeFromShape( attributes.getBlockSize() );
final CellGrid grid = new CellGrid(dimensions, cellDimensions);
final SimpleCacheArrayLoader<?> cacheArrayLoader = N5ImageLoader.createCacheArrayLoader(zarrWriter, pathName);
final SimpleCacheArrayLoader<?> cacheArrayLoader =
new N5OMEZarrCacheArrayLoader<>( zarrWriter, pathName, setupId, timepointId, attributes, grid, axes );
return new ReadOnlyCachedCellImgFactory().createWithCacheLoader(
dimensions, type,
key -> {
Expand Down
110 changes: 102 additions & 8 deletions src/test/java/spimdata/OmeZarrWithWriterTest.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,106 @@
package spimdata;

import ij.IJ;
import ij.ImagePlus;
import mpicbg.spim.data.SpimDataException;
import net.imglib2.realtransform.AffineTransform3D;
import org.embl.mobie.io.ImageDataFormat;
import org.embl.mobie.io.SpimDataOpener;
import org.embl.mobie.io.n5.util.DownsampleBlock;
import org.embl.mobie.io.n5.writers.WriteImgPlusToN5;
import org.embl.mobie.io.ome.zarr.writers.imgplus.WriteImgPlusToN5BdvOmeZarr;
import org.embl.mobie.io.ome.zarr.writers.imgplus.WriteImgPlusToN5OmeZarr;
import org.janelia.saalfeldlab.n5.Compression;
import org.janelia.saalfeldlab.n5.GzipCompression;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;

import static org.apache.commons.io.FilenameUtils.removeExtension;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class OmeZarrWithWriterTest {
// public static void main( String[] args ) {
// ImagePlus imp = IJ.openImage("");
// new WriteImgPlusToN5OmeZarr().export(imp, "src/test/resources/zyx.ome.zarr",
// DownsampleBlock.DownsamplingMethod.Average, new GzipCompression());
//
// new WriteImgPlusToN5BdvOmeZarr().export(imp, "src/test/resources/writingTest.xml",
// DownsampleBlock.DownsamplingMethod.Average, new GzipCompression());
// }

private String imageName;
private AffineTransform3D sourceTransform;
private File tempDir;

@BeforeEach
void setUp( @TempDir Path tempDir ) throws IOException {
this.tempDir = tempDir.toFile();
imageName = "testImage";
sourceTransform = new AffineTransform3D();
}

public static ImagePlus makeImage( String imageName ) {
// make an image with random values, same size as the imagej sample head image
return IJ.createImage(imageName, "8-bit noise", 186, 226, 27);
}

String writeImageAndGetPath( ImageDataFormat imageDataFormat ) {
ImagePlus imp = makeImage( imageName );
DownsampleBlock.DownsamplingMethod downsamplingMethod = DownsampleBlock.DownsamplingMethod.Average;
Compression compression = new GzipCompression();
String filePath;

// gzip compression by default
switch( imageDataFormat ) {
case BdvN5:
filePath = new File(tempDir, imageName + ".xml").getAbsolutePath();
new WriteImgPlusToN5().export(imp, filePath, sourceTransform, downsamplingMethod,
compression, new String[]{imageName} );
break;

case BdvOmeZarr:
filePath = new File(tempDir, imageName + ".xml").getAbsolutePath();
new WriteImgPlusToN5BdvOmeZarr().export(imp, filePath, sourceTransform,
downsamplingMethod, compression, new String[]{imageName} );
break;

case OmeZarr:
filePath = new File(tempDir, imageName + ".ome.zarr").getAbsolutePath();
new WriteImgPlusToN5OmeZarr().export(imp, filePath, sourceTransform,
downsamplingMethod, compression, new String[]{imageName});
break;

default:
throw new UnsupportedOperationException();

}

return filePath;
}

@Test
void writeAndReadImageBdvN5() throws SpimDataException {
ImageDataFormat format = ImageDataFormat.BdvN5;
String xmlPath = writeImageAndGetPath( format );
assertTrue( new File(xmlPath).exists() );
assertTrue( new File(removeExtension(xmlPath) + ".n5").exists() );

new SpimDataOpener().openSpimData( xmlPath, format );
}

@Test
void writeAndReadImageOmeZarr() throws SpimDataException {
ImageDataFormat format = ImageDataFormat.OmeZarr;
String zarrPath = writeImageAndGetPath( ImageDataFormat.OmeZarr );
assertTrue( new File(zarrPath).exists() );

new SpimDataOpener().openSpimData( zarrPath, format );
}

@Test
void writeAndReadImageBdvOmeZarr() throws SpimDataException {
ImageDataFormat format = ImageDataFormat.BdvOmeZarr;
String xmlPath = writeImageAndGetPath( ImageDataFormat.BdvOmeZarr );
assertTrue( new File(xmlPath).exists() );
assertTrue( new File(removeExtension(xmlPath) + ".ome.zarr").exists() );

new SpimDataOpener().openSpimData( xmlPath, format );
}
}