loulou8284
loulou8284

Reputation: 15

How to create a TIFF file from TIFF image data

I have got several files that contains a specific header follows by TIFF image data. How can I write those TIFF image data into a TIFF file ? Thanks for your help.

EDIT : Here's what I tested :

InputStream is = new FileInputStream(filePath);
is.skip(252);
BufferedImage bufferedImage = ImageIO.read(is);
File fileOut = new File(fileOutPath);
ImageIO.write(bufferedImage,"TIFF", fileOut);

I skip the specific header of the file (252Bytes) to get the Tiff Image data bytes. But the bufferedImage is null so I get a java.lang.IllegalArgumentException: im == null! exception.

In resume, I have a TIFF file without a TIFF header. The TIFF header has been replace by a specific one but the image bytes are exactly the same as in a TIFF file.

EDIT : Thanks to haraldK, I can finally create a TIFF header. But I can't open the image, maybe it is because of the compression : "M2 = Modified Read Code II (MRII), i.e., fax group 4".

Here is the header that I created :

SubFileType (1 Long): Zero
ImageWidth (1 Long): 210
ImageLength (1 Long): 297
BitsPerSample (3 Short): 8, 8, 8
Compression (1 Short): Group 4 Fax (aka CCITT FAX4)
Photometric (1 Short): RGB
StripOffsets (1 Long): 306
SamplesPerPixel (1 Short): 3
RowsPerStrip (1 Short): 297
StripByteCounts (1 Long): 187110
ResolutionUnit (1 Short): None
XResolution (72 Rational): 
YResolution (1 Rational): Unexpected numeric
DateTime (20 ASCII): 2014:07:12 10:51:51 
Software (28 ASCII): Put your software name here 
ImageDescription (30 ASCII): Put an image description here 

Should I uncompress the image data before merging the header and the image data ?

Upvotes: 1

Views: 3365

Answers (2)

Harald K
Harald K

Reputation: 27054

Disclaimer: This isn't a fully working example (I would need some sample files to verify in that case), but rather outlines the idea.

First, open a stream to the file, and skip the proprietary header (your code assumes that you can always skip as many bytes you like, but that's not always the case):

InputStream is = new FileInputStream(filePath);

int toSkip = 252;
int skipped = 0;

while (toSkip > 0 && (skipped = is.skip(toSkip)) >= 0) {
    toSkip -= skipped;
}

Then, re-create a valid, minimal TIFF header, according to the TIFF specification. This is actually not too hard:

ByteArrayOutputStream bytes = new ByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream(bytes);

dataOut.write('M');
dataOut.write('M');     // "Motorola" (network) byte order
dataOut.writeShort(42); // TIFF magic identifier (42)

dataOut.writeUnsignedInt(8); // Offset to 1st IFD

// ... write IFD, containing minimal info as per the spec

As your input data seems to be in bilevel or black/white fax format (ref. comments), see Section 3: Bilevel Images (page 17) in the spec for the required fields and allowed values. For normal RGB images, see Section 6: RGB Full Color Images (page 24).

Note that SHORT in the spec is (unsigned) short in Java, but LONG is (unsigned) int. Also note that fields must be written in increasing tag order. RATIONAL can be written as two LONGs (i.e., two unsigned ints). For XResolution and YResolution, just write 72/1 as this is default 72 DPI. StripOffsets will be the length of the IFD + 8 for the bytes written before the IFD. If you don't have strips, set RowsPerStrip equal to ImageLength and StripByteCounts equal to the length of the entire (compressed) image data.

When done encoding the header, merge the header and the image data, and read it:

ByteArrayInputStream header = new ByteArrayInputStream(bytes.toByteArray());
InputStream stream = new SequenceInputStream(header, is); // Merge header and image data

Now, you could read the image:

BufferedImage image = ImageIO.read(stream); // Read image

// TODO: Test that image is non-null before attempting to write

However, if you are only writing the TIFF back to a file, you could just copy the data from stream to a new file, and try to open it in an external tool. This approach will be much faster and require less memory than decoding the image in Java, and then encoding it back to TIFF (and doesn't need JAI or other TIFF plugin for ImageIO):

OutputStream os = new FileOutputStream(new File(...));

byte[] buffer = new byte[1024];
int read;

while ((read = stream.read(buffer) >= 0) {
   os.write(0, read, buffer);
}

Upvotes: 0

blackbishop
blackbishop

Reputation: 32640

You can try with the javax.imageio.ImageIO read/write operations like this :

ImageIO.write(img, "TIFF", new File('fileName'));

Or you can use the Java Advanced Imaging API.

Upvotes: 0

Related Questions