diff --git a/src/main/java/com/glencoesoftware/omero/zarr/ZarrPixelBuffer.java b/src/main/java/com/glencoesoftware/omero/zarr/ZarrPixelBuffer.java index fc5fd54..763bebc 100644 --- a/src/main/java/com/glencoesoftware/omero/zarr/ZarrPixelBuffer.java +++ b/src/main/java/com/glencoesoftware/omero/zarr/ZarrPixelBuffer.java @@ -26,6 +26,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -75,6 +76,12 @@ public class ZarrPixelBuffer implements PixelBuffer { /** Zarr array corresponding to the current resolution level */ private ZarrArray array; + /** + * Mapping of Z plane indexes in full resolution to + * Z plane indexes in current resolution. + */ + private Map zIndexMap; + /** { resolutionLevel, z, c, t, x, y, w, h } vs. tile byte array cache */ private final AsyncLoadingCache, byte[]> tileCache; @@ -191,49 +198,64 @@ private void read(byte[] buffer, int[] shape, int[] offset) // Check planar read size (sizeX and sizeY only) checkReadSize(Arrays.copyOfRange(shape, 3, 5)); + // if reading from a resolution downsampled in Z, + // adjust the shape/offset for the Z coordinate only + // this ensures that the correct Zs are read from the correct offsets + // since the requested shape/offset may not match the underlying array + int planes = 1; + int originalZIndex = offset[2]; + if (getSizeZ() != getTrueSizeZ()) { + offset[2] = zIndexMap.get(originalZIndex); + planes = shape[2]; + shape[2] = 1; + } + try { ByteBuffer asByteBuffer = ByteBuffer.wrap(buffer); DataType dataType = array.getDataType(); - switch (dataType) { - case u1: - case i1: - array.read(buffer, shape, offset); - break; - case u2: - case i2: - { - short[] data = (short[]) array.read(shape, offset); - asByteBuffer.asShortBuffer().put(data); - break; - } - case u4: - case i4: - { - int[] data = (int[]) array.read(shape, offset); - asByteBuffer.asIntBuffer().put(data); - break; - } - case i8: - { - long[] data = (long[]) array.read(shape, offset); - asByteBuffer.asLongBuffer().put(data); - break; - } - case f4: - { - float[] data = (float[]) array.read(shape, offset); - asByteBuffer.asFloatBuffer().put(data); - break; - } - case f8: - { - double[] data = (double[]) array.read(shape, offset); - asByteBuffer.asDoubleBuffer().put(data); - break; - } - default: - throw new IllegalArgumentException( - "Data type " + dataType + " not supported"); + for (int z=0; z(); + } + else { + zIndexMap.clear(); + } try { array = zarrArrayCache.get( root.resolve(Integer.toString(this.resolutionLevel))).get(); + + ZarrArray fullResolutionArray = zarrArrayCache.get( + root.resolve("0")).get(); + + // map each Z index in the full resolution array + // to a Z index in the subresolution array + // if no Z downsampling, this is just an identity map + int fullResZ = fullResolutionArray.getShape()[2]; + int arrayZ = array.getShape()[2]; + for (int z=0; z zArray = mapper.readValue( + Files.readAllBytes(output.resolve("0/" + r + "/.zarray")), + HashMap.class); + List shape = (List) zArray.get("shape"); + shape.set(2, sizeZ / (int) Math.pow(2, r)); + mapper.writeValue(output.resolve("0/" + r + "/.zarray").toFile(), zArray); + } + + try (ZarrPixelBuffer zpbuf = + createPixelBuffer(pixels, output.resolve("0"), sizeX, sizeY)) { + // get the last Z section, for each resolution level + for (int r=0; r