From c76d8c5fdfb4064856f466ec121e7886726ea7b1 Mon Sep 17 00:00:00 2001 From: Melissa Linkert Date: Fri, 22 Nov 2019 10:52:34 -0600 Subject: [PATCH 01/13] Move JPEG-XR codec and service to formats-bsd component --- components/formats-bsd/pom.xml | 5 +++++ .../src/loci/formats/codec/JPEGXRCodec.java | 0 .../src/loci/formats/services/JPEGXRService.java | 0 .../src/loci/formats/services/JPEGXRServiceImpl.java | 0 components/formats-gpl/pom.xml | 5 ----- 5 files changed, 5 insertions(+), 5 deletions(-) rename components/{formats-gpl => formats-bsd}/src/loci/formats/codec/JPEGXRCodec.java (100%) rename components/{formats-gpl => formats-bsd}/src/loci/formats/services/JPEGXRService.java (100%) rename components/{formats-gpl => formats-bsd}/src/loci/formats/services/JPEGXRServiceImpl.java (100%) diff --git a/components/formats-bsd/pom.xml b/components/formats-bsd/pom.xml index e5d79aede51..a921ff1f1c3 100644 --- a/components/formats-bsd/pom.xml +++ b/components/formats-bsd/pom.xml @@ -103,6 +103,11 @@ metadata-extractor 2.11.0 + + ome + jxrlib-all + ${jxrlib.version} + diff --git a/components/formats-gpl/src/loci/formats/codec/JPEGXRCodec.java b/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java similarity index 100% rename from components/formats-gpl/src/loci/formats/codec/JPEGXRCodec.java rename to components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java diff --git a/components/formats-gpl/src/loci/formats/services/JPEGXRService.java b/components/formats-bsd/src/loci/formats/services/JPEGXRService.java similarity index 100% rename from components/formats-gpl/src/loci/formats/services/JPEGXRService.java rename to components/formats-bsd/src/loci/formats/services/JPEGXRService.java diff --git a/components/formats-gpl/src/loci/formats/services/JPEGXRServiceImpl.java b/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java similarity index 100% rename from components/formats-gpl/src/loci/formats/services/JPEGXRServiceImpl.java rename to components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java diff --git a/components/formats-gpl/pom.xml b/components/formats-gpl/pom.xml index 27979e69c4d..97292155ce7 100644 --- a/components/formats-gpl/pom.xml +++ b/components/formats-gpl/pom.xml @@ -63,11 +63,6 @@ ome-poi ${ome-poi.version} - - ome - jxrlib-all - ${jxrlib.version} - edu.ucar cdm From 174f0cf0200d4260c10486521afe1dc749a10bbe Mon Sep 17 00:00:00 2001 From: Melissa Linkert Date: Fri, 22 Nov 2019 10:55:04 -0600 Subject: [PATCH 02/13] Fix license headers in JPEG-XR classes --- .../src/loci/formats/codec/JPEGXRCodec.java | 31 ++++++++++++------- .../loci/formats/services/JPEGXRService.java | 31 ++++++++++++------- .../formats/services/JPEGXRServiceImpl.java | 31 ++++++++++++------- 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java b/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java index 90926c54149..c0cb86b7c45 100644 --- a/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java +++ b/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java @@ -1,25 +1,32 @@ /* * #%L - * OME Bio-Formats package for reading and converting biological file formats. + * BSD implementations of Bio-Formats readers and writers * %% * Copyright (C) 2016 - 2017 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. * #L% */ diff --git a/components/formats-bsd/src/loci/formats/services/JPEGXRService.java b/components/formats-bsd/src/loci/formats/services/JPEGXRService.java index 08b7aa25647..61dc2bbed3c 100644 --- a/components/formats-bsd/src/loci/formats/services/JPEGXRService.java +++ b/components/formats-bsd/src/loci/formats/services/JPEGXRService.java @@ -1,25 +1,32 @@ /* * #%L - * OME Bio-Formats package for reading and converting biological file formats. + * BSD implementations of Bio-Formats readers and writers * %% * Copyright (C) 2016 - 2017 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. * #L% */ diff --git a/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java b/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java index 75b648db65d..65413d00a9d 100644 --- a/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java +++ b/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java @@ -1,25 +1,32 @@ /* * #%L - * OME Bio-Formats package for reading and converting biological file formats. + * BSD implementations of Bio-Formats readers and writers * %% * Copyright (C) 2016 - 2017 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. * #L% */ From 028abdb1f45a6d36052602e65ed5e770c5e47c3c Mon Sep 17 00:00:00 2001 From: Melissa Linkert Date: Fri, 22 Nov 2019 12:36:32 -0600 Subject: [PATCH 03/13] NDPI: add JPEG-XR support --- .../src/loci/formats/tiff/TiffCompression.java | 4 +++- .../src/loci/formats/tiff/TiffParser.java | 6 +++++- .../src/loci/formats/in/NDPIReader.java | 15 +++++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/components/formats-bsd/src/loci/formats/tiff/TiffCompression.java b/components/formats-bsd/src/loci/formats/tiff/TiffCompression.java index 6ed6580df53..e5c9fb2d348 100644 --- a/components/formats-bsd/src/loci/formats/tiff/TiffCompression.java +++ b/components/formats-bsd/src/loci/formats/tiff/TiffCompression.java @@ -47,6 +47,7 @@ import loci.formats.codec.JPEG2000Codec; import loci.formats.codec.JPEG2000CodecOptions; import loci.formats.codec.JPEGCodec; +import loci.formats.codec.JPEGXRCodec; import loci.formats.codec.LZWCodec; import loci.formats.codec.LuraWaveCodec; import loci.formats.codec.NikonCodec; @@ -193,7 +194,8 @@ public CodecOptions getCompressionCodecOptions(IFD ifd, CodecOptions opt) }, NIKON(34713, new NikonCodec(), "Nikon"), - LURAWAVE(65535, new LuraWaveCodec(), "LuraWave"); + LURAWAVE(65535, new LuraWaveCodec(), "LuraWave"), + JPEGXR(22610, new JPEGXRCodec(), "JPEG-XR"); // -- Constants -- diff --git a/components/formats-bsd/src/loci/formats/tiff/TiffParser.java b/components/formats-bsd/src/loci/formats/tiff/TiffParser.java index 9430e5dfbfd..61666557a1b 100644 --- a/components/formats-bsd/src/loci/formats/tiff/TiffParser.java +++ b/components/formats-bsd/src/loci/formats/tiff/TiffParser.java @@ -1154,7 +1154,11 @@ public static void unpackBytes(byte[] samples, int startIndex, byte[] bytes, TiffCompression compression = ifd.getCompression(); PhotoInterp photoInterp = ifd.getPhotometricInterpretation(); - if (compression == TiffCompression.JPEG) photoInterp = PhotoInterp.RGB; + if (compression == TiffCompression.JPEG || + compression == TiffCompression.JPEGXR) + { + photoInterp = PhotoInterp.RGB; + } int[] bitsPerSample = ifd.getBitsPerSample(); int nChannels = bitsPerSample.length; diff --git a/components/formats-gpl/src/loci/formats/in/NDPIReader.java b/components/formats-gpl/src/loci/formats/in/NDPIReader.java index 8a2df362bb7..e134950d4e7 100644 --- a/components/formats-gpl/src/loci/formats/in/NDPIReader.java +++ b/components/formats-gpl/src/loci/formats/in/NDPIReader.java @@ -40,6 +40,7 @@ import loci.formats.services.JPEGTurboServiceImpl; import loci.formats.tiff.IFD; import loci.formats.tiff.PhotoInterp; +import loci.formats.tiff.TiffCompression; import loci.formats.tiff.TiffIFDEntry; import loci.formats.tiff.TiffParser; @@ -102,7 +103,9 @@ public boolean isThisType(String name, boolean open) { if (ifd == null) { return false; } - return ifd.containsKey(MARKER_TAG); + // non-JPEG data won't have the marker tag, + // but should still have the metadata tag + return ifd.containsKey(MARKER_TAG) || ifd.containsKey(METADATA_TAG); } catch (IOException e) { LOGGER.debug("I/O exception during isThisType() evaluation.", e); @@ -127,11 +130,14 @@ public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) { FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h); + int ifdIndex = getIFDIndex(getCoreIndex(), no); + if (x == 0 && y == 0 && w == 1 && h == 1) { return buf; } - else if (getSizeX() <= MAX_SIZE || getSizeY() <= MAX_SIZE) { - int ifdIndex = getIFDIndex(getCoreIndex(), no); + else if (getSizeX() <= MAX_SIZE || getSizeY() <= MAX_SIZE || + ifds.get(ifdIndex).getCompression() != TiffCompression.JPEG) + { try (RandomAccessInputStream s = new RandomAccessInputStream(currentId)) { tiffParser = new TiffParser(s); tiffParser.setUse64BitOffsets(true); @@ -144,7 +150,7 @@ else if (getSizeX() <= MAX_SIZE || getSizeY() <= MAX_SIZE) { } if (initializedSeries != getCoreIndex() || initializedPlane != no) { - IFD ifd = ifds.get(getIFDIndex(getCoreIndex(), no)); + IFD ifd = ifds.get(ifdIndex); long offset = ifd.getStripOffsets()[0]; long byteCount = ifd.getStripByteCounts()[0]; @@ -285,6 +291,7 @@ protected void initStandardMetadata() throws FormatException, IOException { RandomAccessInputStream stream = new RandomAccessInputStream(currentId); for (int i=0; i Date: Mon, 25 Nov 2019 15:47:33 -0600 Subject: [PATCH 04/13] NDPI: parse additional TIFF tags and fix up fluorescence and >4GB reading --- .../src/loci/formats/tiff/TiffParser.java | 2 +- .../src/loci/formats/in/NDPIReader.java | 315 ++++++++++++++---- 2 files changed, 248 insertions(+), 69 deletions(-) diff --git a/components/formats-bsd/src/loci/formats/tiff/TiffParser.java b/components/formats-bsd/src/loci/formats/tiff/TiffParser.java index 61666557a1b..16549878393 100644 --- a/components/formats-bsd/src/loci/formats/tiff/TiffParser.java +++ b/components/formats-bsd/src/loci/formats/tiff/TiffParser.java @@ -524,7 +524,7 @@ public Object getIFDValue(TiffIFDEntry entry) throws IOException { } if (offset != in.getFilePointer()) { - if (fakeBigTiff && (offset < 0 || offset > in.getFilePointer())) { + if (fakeBigTiff && offset < 0) { offset &= 0xffffffffL; offset += 0x100000000L; } diff --git a/components/formats-gpl/src/loci/formats/in/NDPIReader.java b/components/formats-gpl/src/loci/formats/in/NDPIReader.java index e134950d4e7..64c5a29285a 100644 --- a/components/formats-gpl/src/loci/formats/in/NDPIReader.java +++ b/components/formats-gpl/src/loci/formats/in/NDPIReader.java @@ -39,8 +39,10 @@ import loci.formats.services.JPEGTurboService; import loci.formats.services.JPEGTurboServiceImpl; import loci.formats.tiff.IFD; +import loci.formats.tiff.IFDType; import loci.formats.tiff.PhotoInterp; import loci.formats.tiff.TiffCompression; +import loci.formats.tiff.TiffConstants; import loci.formats.tiff.TiffIFDEntry; import loci.formats.tiff.TiffParser; @@ -55,10 +57,41 @@ public class NDPIReader extends BaseTiffReader { // -- Constants -- private static final int MAX_SIZE = 2048; + + // Custom TIFF tags + private static final int OFFSET_HIGH_BYTES = 65324; + private static final int BYTE_COUNT_HIGH_BYTES = 65325; + private static final int VERSION = 65420; private static final int SOURCE_LENS = 65421; + private static final int X_POSITION = 65422; + private static final int Y_POSITION = 65423; + private static final int Z_POSITION = 65424; + private static final int TISSUE_INDEX = 65425; private static final int MARKER_TAG = 65426; - private static final int THUMB_TAG_2 = 65439; - private static final int METADATA_TAG = 65449; + private static final int REFERENCE = 65427; + private static final int FILTER_SET_NAME = 65434; + private static final int EXPOSURE_RATIO = 65435; + private static final int RED_MULTIPLIER = 65436; + private static final int GREEN_MULTIPLIER = 65437; + private static final int BLUE_MULTIPLIER = 65438; + private static final int THUMB_TAG_2 = 65439; // focus points + private static final int FOCUS_POINT_REGIONS = 65440; + private static final int CAPTURE_MODE = 65441; + private static final int SERIAL_NUMBER = 65442; + private static final int JPEG_QUALITY = 65444; + private static final int REFOCUS_INTERVAL = 65445; + private static final int FOCUS_OFFSET = 65446; + private static final int FIRMWARE_VERSION = 65448; + private static final int METADATA_TAG = 65449; // calibration info + private static final int LABEL_OBSCURED = 65450; + private static final int WAVELENGTH = 65451; + private static final int LAMP_AGE = 65453; + private static final int EXPOSURE_TIME = 65454; + private static final int FOCUS_TIME = 65455; + private static final int SCAN_TIME = 65456; + private static final int WRITE_TIME = 65457; + private static final int FULLY_AUTO_FOCUS = 65458; + private static final int DEFAULT_GAMMA = 65500; // -- Fields -- @@ -135,9 +168,7 @@ public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) if (x == 0 && y == 0 && w == 1 && h == 1) { return buf; } - else if (getSizeX() <= MAX_SIZE || getSizeY() <= MAX_SIZE || - ifds.get(ifdIndex).getCompression() != TiffCompression.JPEG) - { + else if (useTiffParser(ifds.get(ifdIndex))) { try (RandomAccessInputStream s = new RandomAccessInputStream(currentId)) { tiffParser = new TiffParser(s); tiffParser.setUse64BitOffsets(true); @@ -286,59 +317,105 @@ protected void initStandardMetadata() throws FormatException, IOException { super.initStandardMetadata(); ifds = tiffParser.getMainIFDs(); + long[] ifdOffsets = tiffParser.getIFDOffsets(); - // fix the offsets for > 4 GB files - RandomAccessInputStream stream = new RandomAccessInputStream(currentId); for (int i=0; i 0 && - (currentOffset < stripOffsets[j - 1])) || - (i > 0 && currentOffset < prevOffset + prevByteCount))) - { - stripOffsets[j] = newOffset; - currentOffset = stripOffsets[j]; - neededAdjustment = true; - } + ifd.remove(THUMB_TAG_2); + + TiffIFDEntry markerTag = (TiffIFDEntry) ifd.get(MARKER_TAG); + + if (markerTag != null) { + if (markerTag.getValueOffset() > in.length()) { + // can't rely upon the MARKER_TAG to be detected correctly + ifd.remove(MARKER_TAG); } } - if (neededAdjustment) { - ifd.putIFDValue(IFD.STRIP_OFFSETS, stripOffsets); - } - neededAdjustment = false; + // fix the offsets for > 4 GB files + if (use64Bit) { + try (RandomAccessInputStream stream = new RandomAccessInputStream(currentId)) { + stream.order(ifd.isLittleEndian()); + + // correct internal offsets within the IFD + + stream.seek(ifdOffsets[i]); + int keyCount = stream.readUnsignedShort(); + int[] tag = new int[keyCount]; + long[] highOrder = new long[keyCount]; + + for (int t=0; t 4) { + long offset = stream.readUnsignedInt(); + ifd.put(tag[t], new TiffIFDEntry(tag[t], type, valueCount, offset)); + } + else { + stream.skipBytes(4); + } + } + stream.skipBytes(8); + + // 4 high bytes for each IFD entry are stored at end of IFD + + for (int t=0; t 0) { + highOrder[t] <<= 32; + Object value = ifd.get(tag[t]); + if (value instanceof TiffIFDEntry) { + TiffIFDEntry entry = (TiffIFDEntry) value; + long newOffset = entry.getValueOffset() + highOrder[t]; + TiffIFDEntry newEntry = new TiffIFDEntry( + entry.getTag(), entry.getType(), + entry.getValueCount(), newOffset); + ifd.put(tag[t], newEntry); + } + else { + ifd.put(tag[t], ((Number) value).longValue() + highOrder[t]); + } + } + } - long[] stripByteCounts = ifd.getStripByteCounts(); - for (int j=0; j 1 && offsetHighBytes != null) { + for (int strip=0; strip in.length()) { - // can't rely upon the MARKER_TAG to be detected correctly - ifds.get(i).remove(MARKER_TAG); - } - else { - Object value = tiffParser.getIFDValue(markerTag); - ifds.get(i).putIFDValue(MARKER_TAG, value); - } - } - - tiffParser.fillInIFD(ifds.get(i)); + tiffParser.fillInIFD(ifd); int[] bpp = ifds.get(i).getBitsPerSample(); for (int q=0; q MAX_SIZE && ms.sizeY > MAX_SIZE; + ms.interleaved = !useTiffParser(ifd); ms.falseColor = false; ms.dimensionOrder = "XYCZT"; ms.thumbnail = index != 0; } } - Object source_lens_value = ifds.get(0).getIFDValue(SOURCE_LENS); + IFD first = ifds.get(0); + + Object source_lens_value = first.getIFDValue(SOURCE_LENS); if (source_lens_value != null) { magnification = Double.valueOf((float) source_lens_value); } - String metadataTag = ifds.get(0).getIFDStringValue(METADATA_TAG); + String metadataTag = first.getIFDStringValue(METADATA_TAG); if (metadataTag != null) { String[] entries = metadataTag.split("\n"); for (String entry : entries) { @@ -468,6 +530,55 @@ else if (key.equals("Product")) { } } } + + int captureMode = first.getIFDIntValue(CAPTURE_MODE); + if (captureMode > 6) { + // single channel data with more than 8 bits per pixel + // the pyramid IFDs may still indicate 3 channels x 8 bits + + for (int r=0; r Date: Wed, 11 Dec 2019 09:02:46 -0600 Subject: [PATCH 05/13] Fix magnification and add plane/channel metadata --- .../src/loci/formats/in/NDPIReader.java | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/components/formats-gpl/src/loci/formats/in/NDPIReader.java b/components/formats-gpl/src/loci/formats/in/NDPIReader.java index 64c5a29285a..1ee2c495377 100644 --- a/components/formats-gpl/src/loci/formats/in/NDPIReader.java +++ b/components/formats-gpl/src/loci/formats/in/NDPIReader.java @@ -46,7 +46,9 @@ import loci.formats.tiff.TiffIFDEntry; import loci.formats.tiff.TiffParser; +import ome.xml.model.primitives.NonNegativeInteger; import ome.xml.model.primitives.Timestamp; +import ome.units.UNITS; import ome.units.quantity.Length; /** @@ -522,7 +524,11 @@ else if (s < pyramidHeight) { addGlobalMeta(key, value); - if (key.equals("NDP.S/N")) { + // key name not a typo + if (key.equals("Objective.Lens.Magnificant") && magnification == null) { + magnification = new Double(value); + } + else if (key.equals("NDP.S/N")) { serialNumber = value; } else if (key.equals("Product")) { @@ -646,6 +652,40 @@ protected void initMetadataStore() throws FormatException { } else { store.setImageDescription(serialNumber, i); + + for (int p=0; p Date: Wed, 11 Dec 2019 11:08:37 -0600 Subject: [PATCH 06/13] Use TiffParser to read pixels if no markers are present Fixes reading JPEG data that is larger than the MAX_SIZE threshold, but still does not have markers that can be used by JPEGTurboService. --- components/formats-gpl/src/loci/formats/in/NDPIReader.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/formats-gpl/src/loci/formats/in/NDPIReader.java b/components/formats-gpl/src/loci/formats/in/NDPIReader.java index 1ee2c495377..27fdcd9e4ed 100644 --- a/components/formats-gpl/src/loci/formats/in/NDPIReader.java +++ b/components/formats-gpl/src/loci/formats/in/NDPIReader.java @@ -707,7 +707,8 @@ private int getIFDIndex(int seriesIndex, int zIndex) { private boolean useTiffParser(IFD ifd) throws FormatException { return ifd.getImageWidth() <= MAX_SIZE || ifd.getImageLength() <= MAX_SIZE || - ifd.getCompression() != TiffCompression.JPEG; + ifd.getCompression() != TiffCompression.JPEG || + !ifd.containsKey(MARKER_TAG); } /** From e38e5e0e59cb87aeb9bfc9d11911b7af830c8dc7 Mon Sep 17 00:00:00 2001 From: Melissa Linkert Date: Tue, 21 Jan 2020 09:30:51 -0600 Subject: [PATCH 07/13] Fix handling of BGR-ordered JPEG-XR data See https://github.com/glencoesoftware/jxrlib/pull/18 --- .../src/loci/formats/codec/JPEGXRCodec.java | 31 ++++++++++++++----- .../loci/formats/services/JPEGXRService.java | 6 ++++ .../formats/services/JPEGXRServiceImpl.java | 11 +++++++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java b/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java index c0cb86b7c45..10a6f6535f8 100644 --- a/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java +++ b/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java @@ -88,12 +88,8 @@ public byte[] decompress(byte[] buf, CodecOptions options) { initialize(); + boolean bgr = service.isBGR(buf); byte[] uncompressed = service.decompress(buf); - - if (options.interleaved) { - return uncompressed; - } - int bpp = options.bitsPerSample / 8; int pixels = options.width * options.height; int channels = uncompressed.length / (pixels * bpp); @@ -102,14 +98,35 @@ public byte[] decompress(byte[] buf, CodecOptions options) return uncompressed; } + if (options.interleaved) { + if (bgr && channels >= 3) { + for (int p=0; p Date: Wed, 22 Jan 2020 11:23:37 -0600 Subject: [PATCH 08/13] Perform BGR => RGB correction in JPEG-XR service instead of codec --- .../src/loci/formats/codec/JPEGXRCodec.java | 26 ++------------- .../loci/formats/services/JPEGXRService.java | 6 ---- .../formats/services/JPEGXRServiceImpl.java | 33 +++++++++++-------- 3 files changed, 23 insertions(+), 42 deletions(-) diff --git a/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java b/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java index 10a6f6535f8..5d23c41f1cd 100644 --- a/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java +++ b/components/formats-bsd/src/loci/formats/codec/JPEGXRCodec.java @@ -88,27 +88,13 @@ public byte[] decompress(byte[] buf, CodecOptions options) { initialize(); - boolean bgr = service.isBGR(buf); - byte[] uncompressed = service.decompress(buf); int bpp = options.bitsPerSample / 8; int pixels = options.width * options.height; - int channels = uncompressed.length / (pixels * bpp); - if (channels == 1) { - return uncompressed; - } + byte[] uncompressed = service.decompress(buf); + int channels = uncompressed.length / (pixels * bpp); - if (options.interleaved) { - if (bgr && channels >= 3) { - for (int p=0; p Date: Tue, 28 Jan 2020 11:16:04 -0600 Subject: [PATCH 09/13] Bump jxrlib version to 0.2.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ef664d4ef63..22c2ea11e15 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 5.3.2 0.1.3 0.2.5 - 0.2.1 + 0.2.2 2.7.2 From 3b70e18f3a4c659646ffae89d181d7041b26171c Mon Sep 17 00:00:00 2001 From: Melissa Linkert Date: Mon, 3 Feb 2020 10:31:22 -0600 Subject: [PATCH 10/13] CZI: fix JPEG-XR handling for new codec/service behavior CZI stores BGR JPEG-XR tiles, which the codec/service now internally reverses to RGB. --- .../formats-gpl/src/loci/formats/in/ZeissCZIReader.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/formats-gpl/src/loci/formats/in/ZeissCZIReader.java b/components/formats-gpl/src/loci/formats/in/ZeissCZIReader.java index c3f0775c66e..9d0bb80db7d 100644 --- a/components/formats-gpl/src/loci/formats/in/ZeissCZIReader.java +++ b/components/formats-gpl/src/loci/formats/in/ZeissCZIReader.java @@ -343,6 +343,7 @@ public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) Arrays.fill(buf, (byte) 0); boolean emptyTile = true; + int compression = -1; try { int minTileX = Integer.MAX_VALUE, minTileY = Integer.MAX_VALUE; int baseResolution = currentIndex; @@ -391,6 +392,7 @@ else if (prestitched != null && prestitched) { if (tile.intersects(image)) { emptyTile = false; + compression = plane.directoryEntry.compression; byte[] rawData = new SubBlock(plane).readPixelData(); Region intersection = tile.intersection(image); int intersectionX = 0; @@ -437,6 +439,7 @@ else if (rawData.length == (realX + 1) * (realY + 1) * pixel) { else { rawData = new SubBlock(plane).readPixelData(); } + compression = plane.directoryEntry.compression; if (rawData.length > buf.length || pixels.size() > 0) { RandomAccessInputStream s = new RandomAccessInputStream(rawData); try { @@ -457,8 +460,9 @@ else if (rawData.length == (realX + 1) * (realY + 1) * pixel) { } finally { } - if (isRGB() && !emptyTile) { + if (isRGB() && !emptyTile && compression != JPEGXR) { // channels are stored in BGR order; red and blue channels need switching + // JPEG-XR data has already been reversed during decompression int redOffset = bpp * 2; for (int i=0; i Date: Thu, 27 Feb 2020 13:05:28 -0600 Subject: [PATCH 11/13] JPEGXRServiceImpl: synchronize on decompress to prevent race conditions jxrlib appears not to be thread safe when reading images via Decode objects. This caused intermittent repository test failures. Until a version of jxrlib with improved thread safety is available, this commit should restore tests to a passing state. --- .../src/loci/formats/services/JPEGXRServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java b/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java index 559a9e5e2b9..59caa653bd3 100644 --- a/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java +++ b/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java @@ -57,7 +57,7 @@ public JPEGXRServiceImpl() { /** * @see JPEGXRServiceImpl#decompress(byte[]) */ - public byte[] decompress(byte[] compressed) throws FormatException { + public synchronized byte[] decompress(byte[] compressed) throws FormatException { LOGGER.trace("begin tile decode; compressed size = {}", compressed.length); try { Decode decode = new Decode(compressed); From 199705a9ca793a9b60885595ce559cfc7169bc1b Mon Sep 17 00:00:00 2001 From: Melissa Linkert Date: Mon, 2 Mar 2020 21:41:36 -0600 Subject: [PATCH 12/13] Revert "JPEGXRServiceImpl: synchronize on decompress to prevent race conditions" This reverts commit 311e1c8f66b13cf9864b2a188e039037aa0b26f5. --- .../src/loci/formats/services/JPEGXRServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java b/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java index 59caa653bd3..559a9e5e2b9 100644 --- a/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java +++ b/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java @@ -57,7 +57,7 @@ public JPEGXRServiceImpl() { /** * @see JPEGXRServiceImpl#decompress(byte[]) */ - public synchronized byte[] decompress(byte[] compressed) throws FormatException { + public byte[] decompress(byte[] compressed) throws FormatException { LOGGER.trace("begin tile decode; compressed size = {}", compressed.length); try { Decode decode = new Decode(compressed); From 2381e5a9be04fc59b0c8b789e98ced8ab611b105 Mon Sep 17 00:00:00 2001 From: Melissa Linkert Date: Tue, 3 Mar 2020 11:58:28 -0600 Subject: [PATCH 13/13] JPEG-XR: rework BGR correction Revert to using Decode.decodeFirstFrame(...) instead of constructing a new Decode object to get the raw pixels and add metadata parsing to get the pixel type directly. --- .../formats/services/JPEGXRServiceImpl.java | 59 ++++++++++++++----- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java b/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java index 559a9e5e2b9..37d11da579c 100644 --- a/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java +++ b/components/formats-bsd/src/loci/formats/services/JPEGXRServiceImpl.java @@ -32,10 +32,13 @@ package loci.formats.services; -import java.nio.ByteBuffer; +import java.io.IOException; +import loci.common.RandomAccessInputStream; import loci.common.services.AbstractService; import loci.formats.FormatException; +import loci.formats.tiff.IFD; +import loci.formats.tiff.TiffParser; import ome.jxrlib.Decode; @@ -47,6 +50,15 @@ */ public class JPEGXRServiceImpl extends AbstractService implements JPEGXRService { + // see table A.4 in ITU-T T.832 + private static final int PIXEL_FORMAT_TAG = 0xbc01; + + // see table A.6 in ITU-T T.832 + private static final short BGR_24 = 0x0c; + private static final short BGR_32 = 0x0e; + private static final short BGRA_32 = 0x0f; + private static final short PBGRA_32 = 0x10; + private static final Logger LOGGER = LoggerFactory.getLogger(JPEGXRServiceImpl.class); @@ -60,24 +72,19 @@ public JPEGXRServiceImpl() { public byte[] decompress(byte[] compressed) throws FormatException { LOGGER.trace("begin tile decode; compressed size = {}", compressed.length); try { - Decode decode = new Decode(compressed); - boolean bgr = decode.isBGR(); - int bpp = (int) decode.getBytesPerPixel(); - int pixels = (int) (decode.getWidth() * decode.getHeight()); - ByteBuffer output = ByteBuffer.allocateDirect(pixels * bpp); - decode.toBytes(output); - - if (bgr) { + byte[] raw = Decode.decodeFirstFrame(compressed, 0, compressed.length); + short[] format = getPixelFormat(compressed); + + if (isBGR(format)) { + int bpp = getBGRComponents(format); // only happens with 8 bits per channel, // 3 (BGR) or 4 (BGRA) channel data - for (int p=0; p