mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 04:25:29 -04:00
#948: TIFF 64 bit FP support
This commit is contained in:
parent
1e574ca429
commit
3e7ad05973
@ -40,7 +40,7 @@ import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.*;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.InputStream;
|
||||
@ -330,4 +330,21 @@ public final class IIOUtil {
|
||||
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
|
||||
}
|
||||
}
|
||||
|
||||
public static void subsampleRow(double[] srcRow, int srcPos, int srcWidth,
|
||||
double[] destRow, int destPos,
|
||||
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
|
||||
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 64 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||
"bitsPerSample must be > 0 and <= 64 and a power of 2");
|
||||
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||
Validate.isTrue(samplesPerPixel * bitsPerSample <= 64 || samplesPerPixel * bitsPerSample % 64 == 0,
|
||||
"samplesPerPixel * bitsPerSample must be < 64 or a multiple of 64");
|
||||
|
||||
int pixelStride = bitsPerSample * samplesPerPixel / 64;
|
||||
for (int x = 0; x < srcWidth * pixelStride; x += samplePeriod * pixelStride) {
|
||||
// System.arraycopy should be intrinsic, but consider using direct array access for pixelStride == 1
|
||||
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
|
||||
}
|
||||
}
|
||||
}
|
@ -815,8 +815,11 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
if (bitsPerSample == 16 || bitsPerSample == 32) {
|
||||
return DataBuffer.TYPE_FLOAT;
|
||||
}
|
||||
else if (bitsPerSample == 64) {
|
||||
return DataBuffer.TYPE_DOUBLE;
|
||||
}
|
||||
|
||||
throw new IIOException("Unsupported BitsPerSample for SampleFormat 3/Floating Point (expected 16/32): " + bitsPerSample);
|
||||
throw new IIOException("Unsupported BitsPerSample for SampleFormat 3/Floating Point (expected 16/32/64): " + bitsPerSample);
|
||||
default:
|
||||
throw new IIOException("Unknown TIFF SampleFormat (expected 1, 2, 3 or 4): " + sampleFormat);
|
||||
}
|
||||
@ -1153,11 +1156,9 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
// Need to do color normalization after reading all bands for planar
|
||||
if (planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR) {
|
||||
if (normalize) {
|
||||
if (normalize && planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR) {
|
||||
normalizeColorPlanar(interpretation, destRaster);
|
||||
}
|
||||
}
|
||||
|
||||
col += colsInTile;
|
||||
|
||||
@ -1252,20 +1253,18 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
// We'll have to use readAsRaster and later apply color space conversion ourselves
|
||||
Raster raster = jpegReader.readRaster(0, jpegParam);
|
||||
// TODO: Refactor + duplicate this for all JPEG-in-TIFF cases
|
||||
if (normalize) {
|
||||
switch (raster.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
if (normalize) {
|
||||
normalizeColor(interpretation, samplesInTile, ((DataBufferByte) raster.getDataBuffer()).getData());
|
||||
}
|
||||
break;
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
if (normalize) {
|
||||
normalizeColor(interpretation, samplesInTile, ((DataBufferUShort) raster.getDataBuffer()).getData());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported transfer type: " + raster.getTransferType());
|
||||
}
|
||||
}
|
||||
|
||||
destination.getRaster().setDataElements(offset.x, offset.y, raster);
|
||||
}
|
||||
@ -2080,6 +2079,36 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
break;
|
||||
|
||||
case DataBuffer.TYPE_DOUBLE:
|
||||
/*for (int band = 0; band < bands; band++)*/ {
|
||||
double[] rowDataDouble = ((DataBufferDouble) tileRowRaster.getDataBuffer()).getData(band);
|
||||
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
input.readFully(rowDataDouble, 0, rowDataDouble.length);
|
||||
|
||||
if (row >= srcRegion.y) {
|
||||
if (normalize) {
|
||||
normalizeColor(interpretation, numBands, rowDataDouble);
|
||||
}
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
subsampleRow(rowDataDouble, srcRegion.x * numBands, colsInTile,
|
||||
rowDataDouble, srcRegion.x * numBands / xSub, numBands, bitsPerSample, xSub);
|
||||
}
|
||||
|
||||
destChannel.setDataElements(startCol, row - srcRegion.y, srcChannel);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new AssertionError("Unsupported data type: " + tileRowRaster.getTransferType());
|
||||
}
|
||||
@ -2091,13 +2120,24 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private void clamp(final float[] rowDataFloat) {
|
||||
for (int i = 0; i < rowDataFloat.length; i++) {
|
||||
if (rowDataFloat[i] > 1f) {
|
||||
rowDataFloat[i] = 1f;
|
||||
private void clamp(final float[] data) {
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
if (data[i] > 1f) {
|
||||
data[i] = 1f;
|
||||
}
|
||||
else if (rowDataFloat[i] < 0f) {
|
||||
rowDataFloat[i] = 0f;
|
||||
else if (data[i] < 0f) {
|
||||
data[i] = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clamp(final double[] data) {
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
if (data[i] > 1d) {
|
||||
data[i] = 1d;
|
||||
}
|
||||
else if (data[i] < 0d) {
|
||||
data[i] = 0d;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2395,6 +2435,28 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeColor(int photometricInterpretation, @SuppressWarnings("unused") int numBands, double[] data) {
|
||||
// TODO: Allow param to decide tone mapping strategy, like in the HDRImageReader
|
||||
clamp(data);
|
||||
|
||||
switch (photometricInterpretation) {
|
||||
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
||||
// Inverse values
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = 1d - data[i];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TIFFExtension.PHOTOMETRIC_CIELAB:
|
||||
case TIFFExtension.PHOTOMETRIC_ICCLAB:
|
||||
case TIFFExtension.PHOTOMETRIC_ITULAB:
|
||||
case TIFFExtension.PHOTOMETRIC_YCBCR:
|
||||
// Not supported
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void convertYCbCr2RGB(final short[] yCbCr, final short[] rgb, final double[] coefficients, final double[] referenceBW, final int offset) {
|
||||
double y;
|
||||
double cb;
|
||||
|
@ -32,6 +32,7 @@ package com.twelvemonkeys.imageio.plugins.tiff;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
@ -43,7 +44,7 @@ import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
@ -55,7 +56,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.AdditionalMatchers.and;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ -100,6 +104,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
||||
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/floatingpoint-64bit.tif"), new Dimension(64, 46)), // Gray, 64 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
|
||||
new TestData(getClassLoaderResource("/tiff/lzw-rgba-4444.tif"), new Dimension(64, 64)), // RGBA, LZW compression with UINT 4/4/4/4 + gray 2/2
|
||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user