From 7a51c3d66f4a6c67f71245e6d474940d9f017260 Mon Sep 17 00:00:00 2001 From: David Gault Date: Mon, 14 Mar 2022 10:09:39 +0000 Subject: [PATCH 1/5] Chunk API: Add chunk functionality for readers and writers --- .../src/loci/formats/FormatReader.java | 30 ++++++ .../src/loci/formats/FormatTools.java | 69 ++++++++++++++ .../src/loci/formats/FormatWriter.java | 91 +++++++++++++++++++ .../src/loci/formats/IFormatReader.java | 16 ++++ .../src/loci/formats/IFormatWriter.java | 15 +++ .../src/loci/formats/ImageReader.java | 12 +++ .../src/loci/formats/ImageWriter.java | 16 ++++ 7 files changed, 249 insertions(+) diff --git a/components/formats-api/src/loci/formats/FormatReader.java b/components/formats-api/src/loci/formats/FormatReader.java index 9734b20152d..0610d8b0d3a 100644 --- a/components/formats-api/src/loci/formats/FormatReader.java +++ b/components/formats-api/src/loci/formats/FormatReader.java @@ -924,6 +924,29 @@ public byte[] openBytes(int no, int x, int y, int w, int h) public abstract byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) throws FormatException, IOException; + /* @see IFormatReader#openBytes(int) */ + @Override + public byte[] openBytes(byte[] buf, int [] shape, int [] offsets) throws FormatException, IOException { + FormatTools.checkParameters(this, buf.length, shape, offsets); + String order = getDimensionOrder(); + int[] XYZTCshape = FormatTools.getXYZCTIndexes(order, shape); + int[] XYZTCoffsets = FormatTools.getXYZCTIndexes(order, offsets); + int num = getSizeZ() * getSizeC() * getSizeT(); + int bufOffset = 0; + for (int z = 0; z < XYZTCshape[2]; z++) { + for (int t = 0; t < XYZTCshape[3]; t++) { + for (int c = 0; c < XYZTCshape[4]; c++) { + int no = FormatTools.getIndex(order, getSizeZ(), getSizeC(), getSizeT(), num, + XYZTCoffsets[2] + z, XYZTCoffsets[4] + c, XYZTCoffsets[3] + t); + byte[] plane = openBytes(no, XYZTCoffsets[0], XYZTCoffsets[1], XYZTCshape[0], XYZTCshape[1]); + System.arraycopy(plane, 0, buf, bufOffset, plane.length); + bufOffset += plane.length; + } + } + } + return buf; + } + /* @see IFormatReader#openPlane(int, int, int, int, int int) */ @Override public Object openPlane(int no, int x, int y, int w, int h) @@ -1287,6 +1310,13 @@ public int getOptimalTileHeight() { return (int) Math.min(maxHeight, getSizeY()); } + /* @see IFormatReader#getOptimalChunkSize() */ + @Override + public int[] getOptimalChunkSize() { + int[] chunkSize = {getOptimalTileWidth(), getOptimalTileHeight(), 1, 1, 1}; + return chunkSize; + } + // -- Sub-resolution API methods -- @Override diff --git a/components/formats-api/src/loci/formats/FormatTools.java b/components/formats-api/src/loci/formats/FormatTools.java index 40394790ac8..aa734be2285 100644 --- a/components/formats-api/src/loci/formats/FormatTools.java +++ b/components/formats-api/src/loci/formats/FormatTools.java @@ -697,6 +697,21 @@ public static int[] getZCTCoords(String order, }; } + public static int[] getXYZCTIndexes(String order, int[] values) { + int xIndex = order.indexOf("X"); + int yIndex = order.indexOf("Y"); + int zIndex = order.indexOf("Z"); + int tIndex = order.indexOf("T"); + int cIndex = order.indexOf("C"); + return new int[] { + values[xIndex], + values[yIndex], + values[zIndex], + values[tIndex], + values[cIndex], + }; + } + /** * Converts index from the given dimension order to the reader's native one. * This method is useful for shuffling the planar order around @@ -1003,6 +1018,20 @@ public static void checkPlaneParameters(IFormatReader r, int no, if (bufLength >= 0) checkBufferSize(r, bufLength, w, h); } + /** + * Convenience method for checking that the chunk shape, chunk offsets and + * buffer sizes are all valid for the given reader. + * If 'bufLength' is less than 0, then the buffer length check is not + * performed. + */ + public static void checkParameters(IFormatReader r, int bufLength, + int[] shape, int[] offsets) throws FormatException + { + assertId(r.getCurrentFile(), true, 2); + checkChunkSize(r, shape, offsets); + if (bufLength >= 0) checkBufferSize(r, bufLength, shape); + } + /** Checks that the given plane number is valid for the given reader. */ public static void checkPlaneNumber(IFormatReader r, int no) throws FormatException @@ -1028,6 +1057,22 @@ public static void checkTileSize(IFormatReader r, int x, int y, int w, int h) } } + /** Checks that the given chunk size is valid for the given reader. */ + public static void checkChunkSize(IFormatReader r, int[] shape, int[] offsets) + throws FormatException + { + int[] dimensionSizes = {r.getSizeX(), r.getSizeY(), r.getSizeZ(), r.getSizeT(), r.getSizeC()}; + int[] XYZTCshape = getXYZCTIndexes(r.getDimensionOrder(), shape); + int[] XYZTCoffsets = getXYZCTIndexes(r.getDimensionOrder(), offsets); + for (int i = 0; i < XYZTCoffsets.length; i++) { + if (XYZTCoffsets[i] < 0 || (XYZTCoffsets[i] + XYZTCshape[i]) > dimensionSizes[i]) { + char dim = DimensionOrder.XYZCT.toString().charAt(i); + throw new FormatException("Invalid chunk size: " + dim + " shape = " + XYZTCshape[i] + " " + dim + + " offset = " + XYZTCoffsets[i] + " " + dim + " maxSize = " + dimensionSizes[i]); + } + } + } + public static void checkBufferSize(IFormatReader r, int len) throws FormatException { @@ -1049,6 +1094,21 @@ public static void checkBufferSize(IFormatReader r, int len, int w, int h) } } + /** + * Checks that the given buffer size is large enough to hold a 5D chunk + * image as returned by the given reader. + * @throws FormatException if the buffer is too small + */ + public static void checkBufferSize(IFormatReader r, int len, int[] shape) + throws FormatException + { + int size = getChunkSize(r, shape); + if (size > len) { + throw new FormatException("Buffer too small (got " + len + + ", expected " + size + ")."); + } + } + /** * Returns true if the given RandomAccessInputStream conatins at least * 'len' bytes. @@ -1071,6 +1131,15 @@ public static int getPlaneSize(IFormatReader r, int w, int h) { return w * h * r.getRGBChannelCount() * getBytesPerPixel(r.getPixelType()); } + /** Returns the size in bytes of a w * h tile. */ + public static int getChunkSize(IFormatReader r, int[] shape) { + int size = r.getRGBChannelCount() * getBytesPerPixel(r.getPixelType()); + for (int i = 0; i < shape.length; i++) { + size *= shape[i]; + } + return size; + } + // -- Utility methods -- export public static String getTileFilename(int tileX, int tileY, diff --git a/components/formats-api/src/loci/formats/FormatWriter.java b/components/formats-api/src/loci/formats/FormatWriter.java index 50ca427e683..526047a97ef 100644 --- a/components/formats-api/src/loci/formats/FormatWriter.java +++ b/components/formats-api/src/loci/formats/FormatWriter.java @@ -35,8 +35,10 @@ import java.awt.image.ColorModel; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import ome.xml.model.enums.DimensionOrder; import ome.xml.model.primitives.PositiveInteger; import loci.common.DataTools; @@ -162,6 +164,36 @@ public void savePlane(int no, Object plane, int x, int y, int w, int h) saveBytes(no, (byte[]) plane, x, y, w, h); } + /* @see IFormatWriter#savePlane(byte[], int[], int[]) */ + @Override + public void saveBytes(byte[] buf, int[] shape, int[] offsets) + throws FormatException, IOException + { + checkParams(buf, shape, offsets); + MetadataRetrieve r = getMetadataRetrieve(); + String order = r.getPixelsDimensionOrder(series).toString(); + int[] XYZTCshape = FormatTools.getXYZCTIndexes(order, shape); + int[] XYZTCoffsets = FormatTools.getXYZCTIndexes(order, offsets); + int num = r.getPixelsSizeZ(series).getValue() * r.getPixelsSizeC(series).getValue() * r.getPixelsSizeT(series).getValue(); + int bufOffset = 0; + for (int z = 0; z < XYZTCshape[2]; z++) { + for (int t = 0; t < XYZTCshape[3]; t++) { + for (int c = 0; c < XYZTCshape[4]; c++) { + int no = FormatTools.getIndex(order, r.getPixelsSizeZ(series).getValue(), r.getPixelsSizeC(series).getValue(), + r.getPixelsSizeT(series).getValue(), num, XYZTCoffsets[2] + z, XYZTCoffsets[4] + c, XYZTCoffsets[3] + t); + int pixelType = FormatTools.pixelTypeFromString(r.getPixelsType(series).toString()); + int bpp = FormatTools.getBytesPerPixel(pixelType); + PositiveInteger samples = r.getChannelSamplesPerPixel(series, 0); + if (samples == null) samples = new PositiveInteger(1); + int planeSize = bpp * XYZTCshape[0] * XYZTCshape[1] * samples.getValue(); + byte[] plane = Arrays.copyOfRange(buf, bufOffset, planeSize); + saveBytes(no, plane, XYZTCoffsets[0], XYZTCoffsets[1], XYZTCshape[0], XYZTCshape[1]); + bufOffset += planeSize; + } + } + } + } + /* @see IFormatWriter#savePlane(int, Object, Region) */ @Override public void savePlane(int no, Object plane, Region tile) @@ -330,6 +362,20 @@ public int setTileSizeY(int tileSize) throws FormatException { return height; } + /* @see IFormatWriter#getChunkSize() */ + @Override + public int[] getChunkSize() throws FormatException { + int[] defaultChunkSize = {getTileSizeX(), getTileSizeY(), 1, 1, 1}; + return defaultChunkSize; + } + + /* @see IFormatWriter#setChunkSize(int[]) */ + @Override + public int[] setChunkSize(int[] chunkSize) throws FormatException { + int[] returnChunkSize = {setTileSizeX(chunkSize[0]), setTileSizeY(chunkSize[1]), 1, 1, 1}; + return returnChunkSize; + } + /* @see IFormatWriter#setResolutions(List) */ @Override public void setResolutions(List resolutions) { @@ -483,6 +529,51 @@ protected void checkParams(int no, byte[] buf, int x, int y, int w, int h) } } + /** + * Ensure that the arguments that are being passed to saveBytes(...) are + * valid. + * @throws FormatException if any of the arguments is invalid. + */ + protected void checkParams(byte[] buf, int[] shape, int[] offsets) + throws FormatException + { + MetadataRetrieve r = getMetadataRetrieve(); + MetadataTools.verifyMinimumPopulated(r, series); + + int pixelType = + FormatTools.pixelTypeFromString(r.getPixelsType(series).toString()); + PositiveInteger samples = r.getChannelSamplesPerPixel(series, 0); + if (samples == null) samples = new PositiveInteger(1); + int minSize = samples.getValue() * FormatTools.getBytesPerPixel(pixelType); + for (int i = 0; i < shape.length; i++) { + minSize *= shape[i]; + } + if (buf.length < minSize) { + throw new FormatException("Buffer is too small; expected " + minSize + + " bytes, got " + buf.length + " bytes."); + } + + String order = r.getPixelsDimensionOrder(series).toString(); + int sizeZ = r.getPixelsSizeZ(series).getValue().intValue(); + int sizeT = r.getPixelsSizeT(series).getValue().intValue(); + int sizeC = r.getChannelCount(series); + int[] dimensionSizes = {getSizeX(), getSizeY(), sizeZ, sizeT, sizeC}; + int[] XYZTCshape = FormatTools.getXYZCTIndexes(order, shape); + int[] XYZTCoffsets = FormatTools.getXYZCTIndexes(order, offsets); + for (int i = 0; i < XYZTCoffsets.length; i++) { + if (XYZTCoffsets[i] < 0 || (XYZTCoffsets[i] + XYZTCshape[i]) > dimensionSizes[i]) { + char dim = DimensionOrder.XYZCT.toString().charAt(i); + throw new FormatException("Invalid chunk size: " + dim + " shape = " + XYZTCshape[i] + " " + dim + + " offset = " + XYZTCoffsets[i] + " " + dim + " maxSize = " + dimensionSizes[i]); + } + } + + if (!DataTools.containsValue(getPixelTypes(compression), pixelType)) { + throw new FormatException("Unsupported image type '" + + FormatTools.getPixelTypeString(pixelType) + "'."); + } + } + /** * Seek to the given (x, y) coordinate of the image that starts at * the given offset. diff --git a/components/formats-api/src/loci/formats/IFormatReader.java b/components/formats-api/src/loci/formats/IFormatReader.java index cccd4486e5b..04dec10a52f 100644 --- a/components/formats-api/src/loci/formats/IFormatReader.java +++ b/components/formats-api/src/loci/formats/IFormatReader.java @@ -283,6 +283,19 @@ byte[] openBytes(int no, byte[] buf) */ byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) throws FormatException, IOException; + + /** + * Obtains a chunk of the specified image into a pre-allocated byte array. + * + * @param buf a pre-allocated buffer. + * @param shape the shape of the chunk consisting of the size of each dimension + * @param offsets the offset of each dimension for the chunk + * @return the pre-allocated buffer buf for convenience. + * @throws FormatException if there was a problem parsing the metadata of the + * file. + * @throws IOException if there was a problem reading the file. + */ + byte[] openBytes(byte[] buf, int[] shape, int[] offsets) throws FormatException, IOException; /** * Obtains the specified image plane (or sub-image thereof) in the reader's @@ -591,6 +604,9 @@ Object openPlane(int no, int x, int y, int w, int h) /** Returns the optimal sub-image height for use with openBytes. */ int getOptimalTileHeight(); + /** Returns the optimal chunk size for use with openBytes. */ + int[] getOptimalChunkSize(); + // -- Sub-resolution API methods -- /** Returns the first core index corresponding to the specified series. diff --git a/components/formats-api/src/loci/formats/IFormatWriter.java b/components/formats-api/src/loci/formats/IFormatWriter.java index f06d61f4c68..e48f25d88ef 100644 --- a/components/formats-api/src/loci/formats/IFormatWriter.java +++ b/components/formats-api/src/loci/formats/IFormatWriter.java @@ -82,6 +82,17 @@ void saveBytes(int no, byte[] buf, int x, int y, int w, int h) void saveBytes(int no, byte[] buf, Region tile) throws FormatException, IOException; + /** + * Saves the given chunk to the current series in the current file. + * + * @param buf the byte array that represents the image chunk. + * @param shape the size of the chunk for every dimension. + * @param offsets the offset of the chunk for every dimension. + * @throws FormatException if one of the parameters is invalid. + * @throws IOException if there was a problem writing to the file. + */ + void saveBytes(byte[] buf, int[] shape, int[] offsets) throws FormatException, IOException; + /** * Saves the given image plane to the current series in the current file. * @@ -248,4 +259,8 @@ void savePlane(int no, Object plane, Region tile) */ List getResolutions(); + int[] setChunkSize(int[] chunkSize) throws FormatException; + + int[] getChunkSize() throws FormatException; + } diff --git a/components/formats-api/src/loci/formats/ImageReader.java b/components/formats-api/src/loci/formats/ImageReader.java index 315700daf48..c167d016741 100644 --- a/components/formats-api/src/loci/formats/ImageReader.java +++ b/components/formats-api/src/loci/formats/ImageReader.java @@ -465,6 +465,12 @@ public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) return getReader().openBytes(no, buf, x, y, w, h); } + /* @see IFormatReader#openBytes(byte[] buf, int[] shape, int[] offsets) */ + @Override + public byte[] openBytes(byte[] buf, int[] shape, int[] offsets) throws FormatException, IOException { + return getReader().openBytes(buf, shape, offsets); + } + /* @see IFormatReader#openPlane(int, int, int, int, int) */ @Override public Object openPlane(int no, int x, int y, int w, int h) @@ -747,6 +753,12 @@ public int getOptimalTileHeight() { return getReader().getOptimalTileHeight(); } + /* @see IFormatReader#getOptimalChunkSize() */ + @Override + public int[] getOptimalChunkSize() { + return getReader().getOptimalChunkSize(); + } + /* @see IFormatReader#getCoreIndex() */ @Override public int getCoreIndex() { diff --git a/components/formats-api/src/loci/formats/ImageWriter.java b/components/formats-api/src/loci/formats/ImageWriter.java index fc210a886b0..3424342f6a8 100644 --- a/components/formats-api/src/loci/formats/ImageWriter.java +++ b/components/formats-api/src/loci/formats/ImageWriter.java @@ -201,6 +201,16 @@ public int setTileSizeY(int tileSize) throws FormatException { return getWriter().setTileSizeY(tileSize); } + @Override + public int[] getChunkSize() throws FormatException { + return getWriter().getChunkSize(); + } + + @Override + public int[] setChunkSize(int[] chunkSize) throws FormatException { + return getWriter().setChunkSize(chunkSize); + } + @Override public void setResolutions(List resolutions) { for (IFormatWriter w : writers) { @@ -268,6 +278,12 @@ public void saveBytes(int no, byte[] buf, Region tile) getWriter().saveBytes(no, buf, tile); } + /* @see IFormatWriter#saveBytes(byte[] buf, int[] shape, int[] offsets) */ + @Override + public void saveBytes(byte[] buf, int[] shape, int[] offsets) throws FormatException, IOException { + getWriter().saveBytes(buf, shape, offsets); + } + /* @see IFormatWriter#savePlane(int, Object) */ @Override public void savePlane(int no, Object plane) From 3cbc78c79a2d72b485b8624c9b76df935c84b134 Mon Sep 17 00:00:00 2001 From: David Gault Date: Mon, 14 Mar 2022 13:46:42 +0000 Subject: [PATCH 2/5] Chunk API: Add new API to ReaderWrapper --- .../formats-api/src/loci/formats/ReaderWrapper.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/components/formats-api/src/loci/formats/ReaderWrapper.java b/components/formats-api/src/loci/formats/ReaderWrapper.java index 1ec164832e8..b48a4fa0066 100644 --- a/components/formats-api/src/loci/formats/ReaderWrapper.java +++ b/components/formats-api/src/loci/formats/ReaderWrapper.java @@ -348,6 +348,13 @@ public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) return reader.openBytes(no, buf, x, y, w, h); } + @Override + public byte[] openBytes(byte[] buf, int [] shape, int [] offsets) + throws FormatException, IOException + { + return reader.openBytes(buf, shape, offsets); + } + @Override public Object openPlane(int no, int x, int y, int w, int h) throws FormatException, IOException @@ -578,6 +585,11 @@ public int getOptimalTileHeight() { return reader.getOptimalTileHeight(); } + @Override + public int[] getOptimalChunkSize() { + return reader.getOptimalChunkSize(); + } + @Override public int getCoreIndex() { return reader.getCoreIndex(); From cf6d2d43a1b037931d7dcc33c01a790ba1f07b89 Mon Sep 17 00:00:00 2001 From: David Gault Date: Mon, 14 Mar 2022 13:52:09 +0000 Subject: [PATCH 3/5] Chunk API: Add new API to WriterWrapper --- .../src/loci/formats/WriterWrapper.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/components/formats-api/src/loci/formats/WriterWrapper.java b/components/formats-api/src/loci/formats/WriterWrapper.java index def4823b467..476880a9332 100644 --- a/components/formats-api/src/loci/formats/WriterWrapper.java +++ b/components/formats-api/src/loci/formats/WriterWrapper.java @@ -205,6 +205,13 @@ public void saveBytes(int no, byte[] buf, Region tile) writer.saveBytes(no, buf, tile); } + @Override + public void saveBytes(byte[] buf, int[] shape, int[] offsets) + throws FormatException, IOException + { + writer.saveBytes(buf, shape, offsets); + } + @Override public void savePlane(int no, Object plane) throws FormatException, IOException @@ -351,6 +358,18 @@ public int setTileSizeY(int tileSize) throws FormatException { return writer.setTileSizeY(tileSize); } + /* @see IFormatWriter#getChunkSize() */ + @Override + public int[] getChunkSize() throws FormatException { + return writer.getChunkSize(); + } + + /* @see IFormatWriter#setChunkSize(int[]) */ + @Override + public int[] setChunkSize(int[] chunkSize) throws FormatException { + return writer.setChunkSize(chunkSize); + } + /* @see IFormatWriter#setResolutions(List) */ @Override public void setResolutions(List resolutions) { From 68b3c815c837ae9569be99fc95a450dcb4ef1d27 Mon Sep 17 00:00:00 2001 From: David Gault Date: Tue, 15 Mar 2022 09:44:57 +0000 Subject: [PATCH 4/5] Chunk API: Update use of getXYZCT --- components/formats-api/src/loci/formats/FormatReader.java | 6 +++--- components/formats-api/src/loci/formats/FormatTools.java | 6 +++--- components/formats-api/src/loci/formats/FormatWriter.java | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/components/formats-api/src/loci/formats/FormatReader.java b/components/formats-api/src/loci/formats/FormatReader.java index 0610d8b0d3a..63c6ab6323a 100644 --- a/components/formats-api/src/loci/formats/FormatReader.java +++ b/components/formats-api/src/loci/formats/FormatReader.java @@ -934,10 +934,10 @@ public byte[] openBytes(byte[] buf, int [] shape, int [] offsets) throws FormatE int num = getSizeZ() * getSizeC() * getSizeT(); int bufOffset = 0; for (int z = 0; z < XYZTCshape[2]; z++) { - for (int t = 0; t < XYZTCshape[3]; t++) { - for (int c = 0; c < XYZTCshape[4]; c++) { + for (int c = 0; c < XYZTCshape[3]; c++) { + for (int t = 0; t < XYZTCshape[4]; t++) { int no = FormatTools.getIndex(order, getSizeZ(), getSizeC(), getSizeT(), num, - XYZTCoffsets[2] + z, XYZTCoffsets[4] + c, XYZTCoffsets[3] + t); + XYZTCoffsets[2] + z, XYZTCoffsets[3] + c, XYZTCoffsets[4] + t); byte[] plane = openBytes(no, XYZTCoffsets[0], XYZTCoffsets[1], XYZTCshape[0], XYZTCshape[1]); System.arraycopy(plane, 0, buf, bufOffset, plane.length); bufOffset += plane.length; diff --git a/components/formats-api/src/loci/formats/FormatTools.java b/components/formats-api/src/loci/formats/FormatTools.java index aa734be2285..978fc76c291 100644 --- a/components/formats-api/src/loci/formats/FormatTools.java +++ b/components/formats-api/src/loci/formats/FormatTools.java @@ -701,14 +701,14 @@ public static int[] getXYZCTIndexes(String order, int[] values) { int xIndex = order.indexOf("X"); int yIndex = order.indexOf("Y"); int zIndex = order.indexOf("Z"); - int tIndex = order.indexOf("T"); int cIndex = order.indexOf("C"); + int tIndex = order.indexOf("T"); return new int[] { values[xIndex], values[yIndex], values[zIndex], - values[tIndex], values[cIndex], + values[tIndex], }; } @@ -1061,7 +1061,7 @@ public static void checkTileSize(IFormatReader r, int x, int y, int w, int h) public static void checkChunkSize(IFormatReader r, int[] shape, int[] offsets) throws FormatException { - int[] dimensionSizes = {r.getSizeX(), r.getSizeY(), r.getSizeZ(), r.getSizeT(), r.getSizeC()}; + int[] dimensionSizes = {r.getSizeX(), r.getSizeY(), r.getSizeZ(), r.getSizeC(), r.getSizeT()}; int[] XYZTCshape = getXYZCTIndexes(r.getDimensionOrder(), shape); int[] XYZTCoffsets = getXYZCTIndexes(r.getDimensionOrder(), offsets); for (int i = 0; i < XYZTCoffsets.length; i++) { diff --git a/components/formats-api/src/loci/formats/FormatWriter.java b/components/formats-api/src/loci/formats/FormatWriter.java index 526047a97ef..7b84ab04ed4 100644 --- a/components/formats-api/src/loci/formats/FormatWriter.java +++ b/components/formats-api/src/loci/formats/FormatWriter.java @@ -177,10 +177,10 @@ public void saveBytes(byte[] buf, int[] shape, int[] offsets) int num = r.getPixelsSizeZ(series).getValue() * r.getPixelsSizeC(series).getValue() * r.getPixelsSizeT(series).getValue(); int bufOffset = 0; for (int z = 0; z < XYZTCshape[2]; z++) { - for (int t = 0; t < XYZTCshape[3]; t++) { - for (int c = 0; c < XYZTCshape[4]; c++) { + for (int c = 0; c < XYZTCshape[3]; c++) { + for (int t = 0; t < XYZTCshape[4]; t++) { int no = FormatTools.getIndex(order, r.getPixelsSizeZ(series).getValue(), r.getPixelsSizeC(series).getValue(), - r.getPixelsSizeT(series).getValue(), num, XYZTCoffsets[2] + z, XYZTCoffsets[4] + c, XYZTCoffsets[3] + t); + r.getPixelsSizeT(series).getValue(), num, XYZTCoffsets[2] + z, XYZTCoffsets[3] + c, XYZTCoffsets[4] + t); int pixelType = FormatTools.pixelTypeFromString(r.getPixelsType(series).toString()); int bpp = FormatTools.getBytesPerPixel(pixelType); PositiveInteger samples = r.getChannelSamplesPerPixel(series, 0); @@ -557,7 +557,7 @@ protected void checkParams(byte[] buf, int[] shape, int[] offsets) int sizeZ = r.getPixelsSizeZ(series).getValue().intValue(); int sizeT = r.getPixelsSizeT(series).getValue().intValue(); int sizeC = r.getChannelCount(series); - int[] dimensionSizes = {getSizeX(), getSizeY(), sizeZ, sizeT, sizeC}; + int[] dimensionSizes = {getSizeX(), getSizeY(), sizeZ, sizeC, sizeT}; int[] XYZTCshape = FormatTools.getXYZCTIndexes(order, shape); int[] XYZTCoffsets = FormatTools.getXYZCTIndexes(order, offsets); for (int i = 0; i < XYZTCoffsets.length; i++) { From e7185379302bff62a21a45a86c75adadd3799981 Mon Sep 17 00:00:00 2001 From: David Gault Date: Mon, 27 Feb 2023 10:52:01 +0000 Subject: [PATCH 5/5] BDV: Add support for new chunk API to BDVReader --- .../src/loci/formats/in/BDVReader.java | 142 +++++++++--------- 1 file changed, 68 insertions(+), 74 deletions(-) diff --git a/components/formats-bsd/src/loci/formats/in/BDVReader.java b/components/formats-bsd/src/loci/formats/in/BDVReader.java index 821a8c850fb..4fd56bca831 100644 --- a/components/formats-bsd/src/loci/formats/in/BDVReader.java +++ b/components/formats-bsd/src/loci/formats/in/BDVReader.java @@ -279,59 +279,65 @@ public byte[][] get8BitLookupTable() { } /** - * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int) + * @see loci.formats.IFormatReader#openBytes(byte[], int[], int[]) */ @Override - public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) + public byte[] openBytes(byte[] buf, int[] shape, int[] offsets) throws FormatException, IOException { - FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h); - lastChannel = getZCTCoords(no)[1]; + // TODO: checkPlaneParameters to be re-added once FormatTools updated + //FormatTools.checkParameters(this, buf.length, shape, offsets); + lastChannel = offsets[1]; // pixel data is stored in XYZ blocks - Object image = getImageData(no, x, y, w, h); + Object image = getImageData(shape, offsets); boolean little = isLittleEndian(); + int size = shape[2] * shape[3] * shape[4]; - // images is of type byte[][]. Left these checks and unpacking + // image is of type byte[]. Left these checks and unpacking // in the code for feature data types - int bpp = FormatTools.getBytesPerPixel(getPixelType()); - for (int row = 0; row < h; row++) { - int base = row * w * bpp; - if (image instanceof byte[][]) { - byte[][] data = (byte[][]) image; - byte[] rowData = data[row]; - System.arraycopy(rowData, x, buf, row * w, w); - } else if (image instanceof short[][]) { - short[][] data = (short[][]) image; - short[] rowData = data[row]; - for (int i = 0; i < w; i++) { - DataTools.unpackBytes(rowData[i], buf, base + 2 * i, 2, little); - } - } else if (image instanceof int[][]) { - int[][] data = (int[][]) image; - int[] rowData = data[row]; - for (int i = 0; i < w; i++) { - DataTools.unpackBytes(rowData[i], buf, base + i * 4, 4, little); - } - } else if (image instanceof float[][]) { - float[][] data = (float[][]) image; - float[] rowData = data[row]; - for (int i = 0; i < w; i++) { - int v = Float.floatToIntBits(rowData[i]); - DataTools.unpackBytes(v, buf, base + i * 4, 4, little); - } - } else if (image instanceof double[][]) { - double[][] data = (double[][]) image; - double[] rowData = data[row]; - for (int i = 0; i < w; i++) { - long v = Double.doubleToLongBits(rowData[i]); - DataTools.unpackBytes(v, buf, base + i * 8, 8, little); - } - } - } - return buf; + if (image instanceof byte[]) { + byte[] data = (byte[]) image; + System.arraycopy(data, 0, buf, 0, size); + } else if (image instanceof short[]) { + short[] data = (short[]) image; + buf = DataTools.shortsToBytes(data, little); + } else if (image instanceof int[]) { + int[] data = (int[]) image; + buf = DataTools.intsToBytes(data, little); + } else if (image instanceof float[]) { + float[] data = (float[]) image; + buf = DataTools.floatsToBytes(data, little); + } else if (image instanceof double[]) { + double[] data = (double[]) image; + buf = DataTools.doublesToBytes(data, little); + } + + return buf; + } + + + /** + * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int) + */ + @Override + public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) + throws FormatException, IOException + { + FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h); + int[] coordinates = getZCTCoords(no); + + int [] shape = {1, 1, 1, h, w}; + int [] offsets = {coordinates[2], coordinates[1], coordinates[0], y, x}; + return openBytes(buf, shape, offsets); } + + /* @see loci.formats.IFormatReader#getOptimalChunkSize() */ + //@Override +// public int[] getOptimalChunkSize() { +// TODO: implement optimal chunk size +// } /* @see loci.formats.IFormatReader#close(boolean) */ @Override @@ -380,13 +386,12 @@ private void initializeJHDFService(String id) throws IOException, MissingLibrary throw new MissingLibraryException(JHDFServiceImpl.NO_JHDF_MSG, e); } } - - private Object getImageData(int no, int x, int y, int width, int height) throws FormatException + + private Object getImageData(int [] shape, int [] offsets) throws FormatException { - int[] zct = getZCTCoords(no); - int zslice = zct[0]; - int channel = zct[1]; - int time = zct[2]; + int time = offsets[0]; + int channel = offsets[1]; + int zslice = offsets[2]; int seriesIndex = series; int requiredResolution = getResolution(); @@ -436,44 +441,33 @@ private Object getImageData(int no, int x, int y, int width, int height) throws } int elementSize = jhdf.getElementSize(imagePath.pathToImageData); + int height = shape[3]; + int width = shape[4]; + int depth = shape[2]; - int[] arrayOrigin = new int[] {zslice, y, x}; - int[] arrayDimension = new int[] {1, height, width}; + int[] arrayOrigin = new int[] {zslice, offsets[3], offsets[4]}; + int[] arrayDimension = new int[] {depth, height, width}; + + int size = width * height * depth; MDIntArray subBlock = jhdf.readIntBlockArray(imagePath.pathToImageData, arrayOrigin, arrayDimension); - + int[] subBlockArray =subBlock.getAsFlatArray(); if (elementSize == 1) { - byte[][] image = new byte[height][width]; - - // Slice x, y dimension - for (int yy = 0; yy < height; yy++) { - for (int xx = 0; xx < width; xx++) { - image[yy][xx] = (byte) subBlock.get(0, yy, xx); - } + byte[] image = new byte[size]; + for (int i = 0; i < size; i++) { + image[i] = (byte) subBlockArray[i]; } return image; } else if (elementSize == 2) { - short[][] image = new short[height][width]; - - // Slice x, y dimension - for (int yy = 0; yy < height; yy++) { - for (int xx = 0; xx < width; xx++) { - image[yy][xx] = (short) subBlock.get(0, yy, xx); - } + short[] image = new short[size]; + for (int i = 0; i < size; i++) { + image[i] = (short) subBlockArray[i]; } return image; } else { - int[][] image = new int[height][width]; - - // Slice x, y dimension - for (int yy = 0; yy < height; yy++) { - for (int xx = 0; xx < width; xx++) { - image[yy][xx] = (int) subBlock.get(0, yy, xx); - } - } - return image; + return subBlockArray; } }