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

Add reader for Ventana .bif files #3336

Merged
merged 27 commits into from Jul 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
920eaf1
Initial Ventana .bif reader
melissalinkert Sep 26, 2018
98b43cd
Progress on tile stitching for the full resolution image
melissalinkert Oct 12, 2018
99d12a2
Use full resolution tile positions to assemble lower resolution images
melissalinkert Nov 15, 2018
21520c1
Average a subset of recorded overlaps instead of using overlap list
melissalinkert Nov 15, 2018
7facfbe
Add "ventana.split_tiles" boolean option
melissalinkert Nov 15, 2018
5a2e134
Populate physical pixel sizes
melissalinkert Nov 16, 2018
fb93dd7
Speed up subresolution reading by reducing the number of tile decodes
melissalinkert Nov 16, 2018
cf53b8e
Reduce image width and height by the total number of overlap pixels
melissalinkert Nov 17, 2018
5017ee1
Fix overlap removal for images with multiple AOIs
melissalinkert Nov 17, 2018
4d65cde
Always adjust the Y coordinate of every other column in an AOI
melissalinkert Nov 17, 2018
6a70853
Fix image names when resolutions are not flattened
melissalinkert Nov 17, 2018
72f10bf
Clean up line lengths and add a few more comments
melissalinkert Nov 17, 2018
e4f3dc6
Use the same units for physical sizes and positions
melissalinkert Nov 19, 2018
c30bc2c
Fix compile errors
melissalinkert Feb 27, 2019
01a8116
Ventana: fix magnification
melissalinkert Feb 20, 2019
17eb6fc
Ventana: adjust XY size to match AOI bounds
melissalinkert Feb 21, 2019
1579fb1
Adjust XY coordinate calculation for low resolution tiles
melissalinkert Feb 25, 2019
6da415d
Define a bounding box for each AOI so that column offsetting is trimmed
melissalinkert Feb 28, 2019
6c31571
Ventana: treat as normal TIFF if no scanned areas defined
melissalinkert Mar 25, 2019
d72e180
Ventana: turn off series splitting in underlying TIFF reader
melissalinkert Mar 29, 2019
8f2f8cd
Ventana: override getThumbSize* to match openThumbBytes
melissalinkert Mar 29, 2019
99540c7
Ventana: fall back to AOI position if the index field is missing
melissalinkert Mar 29, 2019
e5dfe3f
Fix warnings
melissalinkert Mar 30, 2019
ffc72c7
Check for "iScan" in the XML tag during isThisType
melissalinkert Apr 1, 2019
a054b17
Ventana: don't allow the same tile to be decompressed twice in a sing…
melissalinkert Apr 9, 2019
1d0bc44
TIFF: reduce array allocations and unpacking method calls
melissalinkert Apr 9, 2019
a875a23
Fix typo in setting up pixel unpacking for <8 bit data
melissalinkert Apr 25, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions components/formats-api/src/loci/formats/readers.txt
Expand Up @@ -148,6 +148,7 @@ loci.formats.in.LEOReader # sxm
loci.formats.in.JPKReader # jpk
loci.formats.in.NDPIReader # ndpi
loci.formats.in.PCORAWReader # pcoraw
loci.formats.in.VentanaReader # bif

# TIFF-based readers with slow isThisType
loci.formats.in.OMETiffReader # tif
Expand Down
77 changes: 51 additions & 26 deletions components/formats-bsd/src/loci/formats/tiff/TiffParser.java
Expand Up @@ -775,11 +775,23 @@ else if (stripByteCounts[countIndex] < 0 && countIndex > 0) {
Arrays.fill(buf, (byte) 0);
return buf;
}
byte[] tile = new byte[(int) stripByteCounts[countIndex]];
int tileSize = (int) stripByteCounts[countIndex];
if (jpegTable != null) {
tileSize += jpegTable.length - 2;
}
byte[] tile = new byte[tileSize];

LOGGER.debug("Reading tile Length {} Offset {}", tile.length, stripOffset);
in.seek(stripOffset);
in.read(tile);

if (jpegTable != null) {
System.arraycopy(jpegTable, 0, tile, 0, jpegTable.length - 2);
in.seek(stripOffset + 2);
in.read(tile, jpegTable.length - 2, tile.length - (jpegTable.length - 2));
}
else {
in.seek(stripOffset);
in.read(tile);
}

// reverse bits in each byte if FillOrder == 2

Expand All @@ -796,13 +808,7 @@ else if (stripByteCounts[countIndex] < 0 && countIndex > 0) {
ifd.getPhotometricInterpretation() == PhotoInterp.Y_CB_CR &&
ifd.getIFDIntValue(IFD.Y_CB_CR_SUB_SAMPLING) == 1 && ycbcrCorrection;

if (jpegTable != null) {
byte[] q = new byte[jpegTable.length + tile.length - 4];
System.arraycopy(jpegTable, 0, q, 0, jpegTable.length - 2);
System.arraycopy(tile, 2, q, jpegTable.length - 2, tile.length - 2);
tile = compression.decompress(q, codecOptions);
}
else tile = compression.decompress(tile, codecOptions);
tile = compression.decompress(tile, codecOptions);
TiffCompression.undifference(tile, ifd);
unpackBytes(buf, 0, tile, ifd);

Expand Down Expand Up @@ -1139,24 +1145,18 @@ public static void unpackBytes(byte[] samples, int startIndex, byte[] bytes,
sampleCount /= nChannels;
}

LOGGER.trace(
"unpacking {} samples (startIndex={}; totalBits={}; numBytes={})",
new Object[] {sampleCount, startIndex, nChannels * bitsPerSample[0],
bytes.length});

long imageWidth = ifd.getImageWidth();
long imageHeight = ifd.getImageLength();
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(
"unpacking {} samples (startIndex={}; totalBits={}; numBytes={})",
new Object[] {sampleCount, startIndex, nChannels * bitsPerSample[0],
bytes.length});
}

int bps0 = bitsPerSample[0];
int numBytes = ifd.getBytesPerSample()[0];
int nSamples = samples.length / (nChannels * numBytes);

boolean noDiv8 = bps0 % 8 != 0;
boolean bps8 = bps0 == 8;
boolean bps16 = bps0 == 16;

boolean littleEndian = ifd.isLittleEndian();

// Hyper optimisation that takes any 8-bit or 16-bit data, where there is
// only one channel, the source byte buffer's size is less than or equal to
// that of the destination buffer and for which no special unpacking is
Expand All @@ -1172,6 +1172,15 @@ public static void unpackBytes(byte[] samples, int startIndex, byte[] bytes,
return;
}

long imageWidth = ifd.getImageWidth();
long imageHeight = ifd.getImageLength();

int numBytes = ifd.getBytesPerSample()[0];
int nSamples = samples.length / (nChannels * numBytes);

boolean noDiv8 = bps0 % 8 != 0;
boolean littleEndian = ifd.isLittleEndian();

long maxValue = (long) Math.pow(2, bps0) - 1;
if (photoInterp == PhotoInterp.CMYK) maxValue = Integer.MAX_VALUE;

Expand Down Expand Up @@ -1206,7 +1215,9 @@ public static void unpackBytes(byte[] samples, int startIndex, byte[] bytes,

RandomAccessInputStream bb = null;
try {
bb = new RandomAccessInputStream(new ByteArrayHandle(bytes));
if (noDiv8) {
bb = new RandomAccessInputStream(new ByteArrayHandle(bytes));
}
// unpack pixels
for (int sample=0; sample<sampleCount; sample++) {
int ndx = startIndex + sample;
Expand Down Expand Up @@ -1240,7 +1251,14 @@ public static void unpackBytes(byte[] samples, int startIndex, byte[] bytes,
}
}
else {
value = DataTools.bytesToLong(bytes, index, numBytes, littleEndian);
// DataTools.bytesToLong can handle the numBytes == 1 case,
// but direct assignment is faster than potentially thousands of method calls
if (numBytes == 1){
value = bytes[index] & 0xff;
}
else {
value = DataTools.bytesToLong(bytes, index, numBytes, littleEndian);
}
}

if (photoInterp == PhotoInterp.WHITE_IS_ZERO ||
Expand All @@ -1250,8 +1268,15 @@ public static void unpackBytes(byte[] samples, int startIndex, byte[] bytes,
}

if (outputIndex + numBytes <= samples.length) {
DataTools.unpackBytes(value, samples, outputIndex, numBytes,
littleEndian);
// DataTools.unpackBytes can handle the numBytes == 1 case,
// but direct assignment is faster than potentially thousands of method calls
if (numBytes == 1) {
samples[outputIndex] = (byte) value;
}
else {
DataTools.unpackBytes(value, samples, outputIndex, numBytes,
littleEndian);
}
}
}
else {
Expand Down