#417: TIFF 16 bit FP

This commit is contained in:
Harald Kuhr 2021-03-29 10:26:47 +02:00
parent 9c8977062d
commit 0011b9a480
3 changed files with 59 additions and 4 deletions

View File

@ -781,11 +781,11 @@ public final class TIFFImageReader extends ImageReaderBase {
throw new IIOException("Unsupported BitsPerSample for SampleFormat 2/Signed Integer (expected 8/16/32): " + bitsPerSample);
case TIFFExtension.SAMPLEFORMAT_FP:
if (bitsPerSample == 32) {
if (bitsPerSample == 16 || bitsPerSample == 32) {
return DataBuffer.TYPE_FLOAT;
}
throw new IIOException("Unsupported BitsPerSample for SampleFormat 3/Floating Point (expected 32): " + bitsPerSample);
throw new IIOException("Unsupported BitsPerSample for SampleFormat 3/Floating Point (expected 16/32): " + bitsPerSample);
default:
throw new IIOException("Unknown TIFF SampleFormat (expected 1, 2, 3 or 4): " + sampleFormat);
}
@ -1969,7 +1969,9 @@ public final class TIFFImageReader extends ImageReaderBase {
case DataBuffer.TYPE_FLOAT:
/*for (int band = 0; band < bands; band++)*/ {
boolean needsWidening = getBitsPerSample() == 16;
float[] rowDataFloat = ((DataBufferFloat) tileRowRaster.getDataBuffer()).getData(band);
short[] rowDataShort = needsWidening ? new short[rowDataFloat.length] : null;
WritableRaster destChannel = banded
? raster.createWritableChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), 0, 0, new int[] {band})
@ -1978,12 +1980,19 @@ public final class TIFFImageReader extends ImageReaderBase {
? tileRowRaster.createChild(tileRowRaster.getMinX(), 0, tileRowRaster.getWidth(), 1, 0, 0, new int[] {band})
: tileRowRaster;
for (int row = startRow; row < startRow + rowsInTile; row++) {
if (row >= srcRegion.y + srcRegion.height) {
break; // We're done with this tile
}
readFully(input, rowDataFloat);
if (needsWidening) {
readFully(input, rowDataShort);
toFloat(rowDataFloat, rowDataShort);
}
else {
readFully(input, rowDataFloat);
}
if (row >= srcRegion.y) {
normalizeColor(interpretation, rowDataFloat);
@ -2008,7 +2017,52 @@ public final class TIFFImageReader extends ImageReaderBase {
}
}
private void clamp(float[] rowDataFloat) {
private void toFloat(final float[] rowDataFloat, final short[] rowDataShort) {
for (int i = 0; i < rowDataFloat.length; i++) {
rowDataFloat[i] = toFloat(rowDataShort[i]);
}
}
/**
* Converts an IEEE 754 half-precision data type to single-precision.
*
* @param shortValue a 16 bit half precision value
* @return an IEE 754 single precision float
*
* @see <a href="https://stackoverflow.com/a/6162687/259991">Stack Overflow answer by x4u</a>
* @see <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format>Wikipedia</a>
*/
private float toFloat(final short shortValue) {
int mantissa = shortValue & 0x03ff; // 10 bits mantissa
int exponent = shortValue & 0x7c00; // 5 bits exponent
if (exponent == 0x7c00) { // NaN/Inf
exponent = 0x3fc00; // -> NaN/Inf
}
else if (exponent != 0) { // Normalized value
exponent += 0x1c000; // exp - 15 + 127
// Smooth transition
if (mantissa == 0 && exponent > 0x1c400) {
return Float.intBitsToFloat((shortValue & 0x8000) << 16 | exponent << 13 | 0x3ff);
}
}
else if (mantissa != 0) { // && exp == 0 -> subnormal
exponent = 0x1c400; // Make it normal
do {
mantissa <<= 1; // mantissa * 2
exponent -= 0x400; // Decrease exp by 1
} while ((mantissa & 0x400) == 0); // while not normal
mantissa &= 0x3ff; // Discard subnormal bit
} // else +/-0 -> +/-0
// Combine all parts, sign << (31 - 15), value << (23 - 10)
return Float.intBitsToFloat((shortValue & 0x8000) << 16 | (exponent | mantissa) << 13);
}
private void clamp(final float[] rowDataFloat) {
for (int i = 0; i < rowDataFloat.length; i++) {
if (rowDataFloat[i] > 1f) {
rowDataFloat[i] = 1f;

View File

@ -102,6 +102,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
new TestData(getClassLoaderResource("/tiff/cmyk_jpeg.tif"), new Dimension(100, 100)), // CMYK, JPEG compressed, with ICC profile
new TestData(getClassLoaderResource("/tiff/grayscale-alpha.tiff"), new Dimension(248, 351)), // Gray + unassociated alpha
new TestData(getClassLoaderResource("/tiff/signed-integral-8bit.tif"), new Dimension(439, 167)), // Gray, 8 bit *signed* integral
new TestData(getClassLoaderResource("/tiff/floatingpoint-16bit.tif"), new Dimension(151, 151)), // RGB, 16 bit floating point
new TestData(getClassLoaderResource("/tiff/floatingpoint-32bit.tif"), new Dimension(300, 100)), // RGB, 32 bit floating point
new TestData(getClassLoaderResource("/tiff/general-cmm-error.tif"), new Dimension(1181, 860)), // RGB, LZW compression with broken/incompatible ICC profile
new TestData(getClassLoaderResource("/tiff/lzw-rgba-padded-icc.tif"), new Dimension(19, 11)), // RGBA, LZW compression with padded ICC profile