mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 20:15:28 -04:00
Better PFM support.
This commit is contained in:
parent
a7ebc1b52f
commit
623d13a517
@ -291,4 +291,21 @@ public final class IIOUtil {
|
|||||||
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
|
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void subsampleRow(float[] srcRow, int srcPos, int srcWidth,
|
||||||
|
float[] 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 <= 32 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||||
|
"bitsPerSample must be > 0 and <= 32 and a power of 2");
|
||||||
|
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||||
|
Validate.isTrue(samplesPerPixel * bitsPerSample <= 32 || samplesPerPixel * bitsPerSample % 32 == 0,
|
||||||
|
"samplesPerPixel * bitsPerSample must be < 32 or a multiple of 32 ");
|
||||||
|
|
||||||
|
int pixelStride = bitsPerSample * samplesPerPixel / 32;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -45,6 +45,7 @@ final class PNMHeader {
|
|||||||
private final TupleType tupleType;
|
private final TupleType tupleType;
|
||||||
private final int width;
|
private final int width;
|
||||||
private final int height;
|
private final int height;
|
||||||
|
private final float maxSampleFloat;
|
||||||
private final int maxSample;
|
private final int maxSample;
|
||||||
|
|
||||||
private final List<String> comments;
|
private final List<String> comments;
|
||||||
@ -57,6 +58,7 @@ final class PNMHeader {
|
|||||||
this.height = isTrue(height > 0, height, "height must be greater than: %d");
|
this.height = isTrue(height > 0, height, "height must be greater than: %d");
|
||||||
isTrue(depth == tupleType.getSamplesPerPixel(), depth, String.format("incorrect depth for %s, expected %d: %d", tupleType, tupleType.getSamplesPerPixel(), depth));
|
isTrue(depth == tupleType.getSamplesPerPixel(), depth, String.format("incorrect depth for %s, expected %d: %d", tupleType, tupleType.getSamplesPerPixel(), depth));
|
||||||
this.maxSample = isTrue(tupleType.isValidMaxSample(maxSample), maxSample, "maxSample out of range: %d");
|
this.maxSample = isTrue(tupleType.isValidMaxSample(maxSample), maxSample, "maxSample out of range: %d");
|
||||||
|
this.maxSampleFloat = this.maxSample;
|
||||||
|
|
||||||
this.comments = Collections.unmodifiableList(new ArrayList<String>(comments));
|
this.comments = Collections.unmodifiableList(new ArrayList<String>(comments));
|
||||||
|
|
||||||
@ -70,7 +72,8 @@ final class PNMHeader {
|
|||||||
this.height = isTrue(height > 0, height, "height must be greater than: %d");
|
this.height = isTrue(height > 0, height, "height must be greater than: %d");
|
||||||
isTrue(depth == tupleType.getSamplesPerPixel(), depth, String.format("incorrect depth for %s, expected %d: %d", tupleType, tupleType.getSamplesPerPixel(), depth));
|
isTrue(depth == tupleType.getSamplesPerPixel(), depth, String.format("incorrect depth for %s, expected %d: %d", tupleType, tupleType.getSamplesPerPixel(), depth));
|
||||||
|
|
||||||
this.maxSample = -1;
|
this.maxSample = 1;
|
||||||
|
this.maxSampleFloat = maxSample;
|
||||||
this.byteOrder = byteOrder;
|
this.byteOrder = byteOrder;
|
||||||
|
|
||||||
this.comments = Collections.unmodifiableList(new ArrayList<String>(comments));
|
this.comments = Collections.unmodifiableList(new ArrayList<String>(comments));
|
||||||
@ -118,11 +121,8 @@ final class PNMHeader {
|
|||||||
if (maxSample <= PNM.MAX_VAL_16BIT) {
|
if (maxSample <= PNM.MAX_VAL_16BIT) {
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
if ((maxSample & 0xffffffffL) <= PNM.MAX_VAL_32BIT) {
|
|
||||||
return 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new AssertionError("maxSample exceeds 32 bit");
|
return 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTransferType() {
|
public int getTransferType() {
|
||||||
@ -135,11 +135,8 @@ final class PNMHeader {
|
|||||||
if (maxSample <= PNM.MAX_VAL_16BIT) {
|
if (maxSample <= PNM.MAX_VAL_16BIT) {
|
||||||
return DataBuffer.TYPE_USHORT;
|
return DataBuffer.TYPE_USHORT;
|
||||||
}
|
}
|
||||||
if ((maxSample & 0xffffffffL) <= PNM.MAX_VAL_32BIT) {
|
|
||||||
return DataBuffer.TYPE_INT;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new AssertionError("maxSample exceeds 32 bit");
|
return DataBuffer.TYPE_INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getComments() {
|
public List<String> getComments() {
|
||||||
|
@ -49,11 +49,13 @@ import java.io.DataInput;
|
|||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.util.IIOUtil.subsampleRow;
|
||||||
|
|
||||||
public final class PNMImageReader extends ImageReaderBase {
|
public final class PNMImageReader extends ImageReaderBase {
|
||||||
// TODO: Allow reading unknown tuple types as Raster!
|
// TODO: Allow reading unknown tuple types as Raster!
|
||||||
// TODO: readAsRenderedImage?
|
// TODO: readAsRenderedImage?
|
||||||
@ -83,7 +85,7 @@ public final class PNMImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
static String asASCII(final short type) {
|
static String asASCII(final short type) {
|
||||||
byte[] asciiBytes = {(byte) ((type >> 8) & 0xff), (byte) (type & 0xff)};
|
byte[] asciiBytes = {(byte) ((type >> 8) & 0xff), (byte) (type & 0xff)};
|
||||||
return new String(asciiBytes, Charset.forName("ASCII"));
|
return new String(asciiBytes, StandardCharsets.US_ASCII);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -150,7 +152,6 @@ public final class PNMImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
// TODO: Allow reading unknown tuple types as Raster!
|
// TODO: Allow reading unknown tuple types as Raster!
|
||||||
|
|
||||||
throw new AssertionError("Unknown PNM tuple type: " + header.getTupleType());
|
throw new AssertionError("Unknown PNM tuple type: " + header.getTupleType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,7 +262,7 @@ public final class PNMImageReader extends ImageReaderBase {
|
|||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
switch (transferType) {
|
switch (transferType) {
|
||||||
case DataBuffer.TYPE_BYTE:
|
case DataBuffer.TYPE_BYTE:
|
||||||
readRowByte(destRaster, clippedRow, colorConvert, rowDataByte, samplesPerPixel, input, y, srcRegion, xSub, ySub);
|
readRowByte(destRaster, clippedRow, colorConvert, rowDataByte, header.getBitsPerSample(), samplesPerPixel, input, y, srcRegion, xSub, ySub);
|
||||||
break;
|
break;
|
||||||
case DataBuffer.TYPE_USHORT:
|
case DataBuffer.TYPE_USHORT:
|
||||||
readRowUShort(destRaster, clippedRow, rowDataUShort, samplesPerPixel, input, y, srcRegion, xSub, ySub);
|
readRowUShort(destRaster, clippedRow, rowDataUShort, samplesPerPixel, input, y, srcRegion, xSub, ySub);
|
||||||
@ -339,7 +340,7 @@ public final class PNMImageReader extends ImageReaderBase {
|
|||||||
Raster rowRaster,
|
Raster rowRaster,
|
||||||
final ColorConvertOp colorConvert,
|
final ColorConvertOp colorConvert,
|
||||||
final byte[] rowDataByte,
|
final byte[] rowDataByte,
|
||||||
final int samplesPerPixel,
|
final int bitsPerSample, final int samplesPerPixel,
|
||||||
final DataInput input, final int y,
|
final DataInput input, final int y,
|
||||||
final Rectangle srcRegion,
|
final Rectangle srcRegion,
|
||||||
final int xSub, final int ySub) throws IOException {
|
final int xSub, final int ySub) throws IOException {
|
||||||
@ -352,7 +353,9 @@ public final class PNMImageReader extends ImageReaderBase {
|
|||||||
input.readFully(rowDataByte);
|
input.readFully(rowDataByte);
|
||||||
|
|
||||||
// Subsample (horizontal)
|
// Subsample (horizontal)
|
||||||
subsampleHorizontal(rowDataByte, rowDataByte.length, samplesPerPixel, xSub);
|
if (xSub > 1) {
|
||||||
|
subsampleRow(rowDataByte, srcRegion.x, srcRegion.width, rowDataByte, 0, samplesPerPixel, bitsPerSample, xSub);
|
||||||
|
}
|
||||||
|
|
||||||
normalize(rowDataByte, 0, rowDataByte.length / xSub);
|
normalize(rowDataByte, 0, rowDataByte.length / xSub);
|
||||||
|
|
||||||
@ -379,7 +382,9 @@ public final class PNMImageReader extends ImageReaderBase {
|
|||||||
readFully(input, rowDataUShort);
|
readFully(input, rowDataUShort);
|
||||||
|
|
||||||
// Subsample (horizontal)
|
// Subsample (horizontal)
|
||||||
subsampleHorizontal(rowDataUShort, rowDataUShort.length, samplesPerPixel, xSub);
|
if (xSub > 1) {
|
||||||
|
subsampleRow(rowDataUShort, srcRegion.x, srcRegion.width, rowDataUShort, 0, samplesPerPixel, 16, xSub);
|
||||||
|
}
|
||||||
|
|
||||||
normalize(rowDataUShort);
|
normalize(rowDataUShort);
|
||||||
|
|
||||||
@ -402,11 +407,14 @@ public final class PNMImageReader extends ImageReaderBase {
|
|||||||
readFully(input, rowDataFloat);
|
readFully(input, rowDataFloat);
|
||||||
|
|
||||||
// Subsample (horizontal)
|
// Subsample (horizontal)
|
||||||
subsampleHorizontal(rowDataFloat, rowDataFloat.length, samplesPerPixel, xSub);
|
if (xSub > 1) {
|
||||||
|
subsampleRow(rowDataFloat, srcRegion.x, srcRegion.width, rowDataFloat, 0, samplesPerPixel, 32, xSub);
|
||||||
|
}
|
||||||
|
|
||||||
normalize(rowDataFloat);
|
normalize(rowDataFloat);
|
||||||
|
|
||||||
int destY = (y - srcRegion.y) / ySub;
|
// Note: PFM is stored bottom to top!
|
||||||
|
int destY = destRaster.getHeight() - 1 - (y - srcRegion.y) / ySub;
|
||||||
// TODO: ColorConvertOp if needed
|
// TODO: ColorConvertOp if needed
|
||||||
destRaster.setDataElements(0, destY, rowRaster);
|
destRaster.setDataElements(0, destY, rowRaster);
|
||||||
}
|
}
|
||||||
@ -437,19 +445,6 @@ public final class PNMImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("SuspiciousSystemArraycopy")
|
|
||||||
private void subsampleHorizontal(final Object data, final int length, final int samplesPerPixel, final int xSub) {
|
|
||||||
if (xSub == 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Super-special 1 bit subsampling handling for PBM
|
|
||||||
|
|
||||||
for (int x = 0; x < length / xSub; x += samplesPerPixel) {
|
|
||||||
System.arraycopy(data, x * xSub, data, x, samplesPerPixel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void normalize(final byte[] rowData, final int start, final int length) {
|
private void normalize(final byte[] rowData, final int start, final int length) {
|
||||||
switch (header.getTupleType()) {
|
switch (header.getTupleType()) {
|
||||||
case BLACKANDWHITE:
|
case BLACKANDWHITE:
|
||||||
@ -484,13 +479,9 @@ public final class PNMImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void normalize(final float[] rowData) {
|
private void normalize(final float[] rowData) {
|
||||||
// TODO: Do the real thing, find min/max and normalize to range 0...255? But only if not reading raster..? Only support reading as raster?
|
|
||||||
// Normalize
|
// Normalize
|
||||||
for (int i = 0; i < rowData.length; i++) {
|
for (int i = 0; i < rowData.length; i++) {
|
||||||
// if (rowData[i] > 275f /*header.getMaxSampleFloat()*/) {
|
rowData[i] = Math.min(1, rowData[i]); // Clamp
|
||||||
// System.out.println("rowData[" + i + "]: " + rowData[i]);
|
|
||||||
// }
|
|
||||||
// rowData[i] = rowData[i] / 275f /*header.getMaxSampleFloat()*/;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,10 @@ final class PNMMetadata extends AbstractMetadata {
|
|||||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||||
|
|
||||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||||
imageOrientation.setAttribute("value", "Normal");
|
imageOrientation.setAttribute("value",
|
||||||
|
header.getFileType() == PNM.PFM_GRAY || header.getFileType() == PNM.PFM_RGB
|
||||||
|
? "FlipH"
|
||||||
|
: "Normal");
|
||||||
dimension.appendChild(imageOrientation);
|
dimension.appendChild(imageOrientation);
|
||||||
|
|
||||||
return dimension;
|
return dimension;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user