Skip to main content

Converting raw bitmap data to TIFF

9 replies [Last post]
jimorie
Offline
Joined: 2006-03-13

Hello! I am interested in finding out if the Java Advanced Imaging library can help me converting raw bitmap image data into a TIFF image. I rip image data out of PDF documents, and I know the colorspace (CMYK, RGB or Grayscale), the dimensions and the bit depth of the image data. I need to convert this to a TIFF image. Can JAI aid me in this? I was thinking it should, but a very quick glance through some examples didn't immediately tell how. So I thought I'd try this forum. Maybe someone could be so very nice and give me a pointer to a relevant example, or class descriptions within the JAI API.

All help is much appreciated.

Thanks,

Petter (md1pette mdstud.chalmers.se)

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Bob Deen

> You can use JAI and friends to *write* the image as TIFF,
> but you need to *create* it using java.awt.image, java.awt.color. and fill the raster with samples:
>
>
> BufferedImage result = createImage(width, height);
> WritableRaster raster = result.getRaster();
> ...
> raster.setSample(...);
> ...
>
>
> This is what I do to create an image (you will tweak
> some parameters, of course).
> If you find a simpler way, please share it :)

Yes but setSample is likely rather slow. If you have the image as an
array of bytes or whatever, you can create a DataBuffer from that, use a
Raster to attach an appropriate SampleModel to that (which describes the
layout of pixels in the data buffer), create an appropriate ColorModel,
and then create a BufferedImage or some derivative from those. You'll
have to look up the constructors and factory methods to get the
appropriate ones, but those are the superclasses you'll want to deal with.

The difference is, the setSample approach creates an in-memory image
using whatever default layout the underlying machine wants, so data
copying is necessary to fill up the buffer. Doing it the "hard" way as
I describe allows you to avoid that copy step, essentially wrapping an
image structure around an existing array of bytes, assuming they
represent uncompressed pixels in some manner. More work, but more
efficient.

-Bob

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
For additional commands, e-mail: interest-help@jai.dev.java.net

dub
Offline
Joined: 2003-06-11

> Yes but setSample is likely rather slow. If you have
> the image as an
> array of bytes or whatever, you can ...
>
> The difference is, the setSample approach creates an
> in-memory image
> using whatever default layout the underlying machine
> wants, so data
> copying is necessary to fill up the buffer. Doing it
> the "hard" way as
> I describe allows you to avoid that copy step,
> essentially wrapping an
> image structure around an existing array of bytes,
> assuming they
> represent uncompressed pixels in some manner. More
> work, but more
> efficient.
>
> -Bob

You are, of course, correct. In my case, there is no
existing data, so I used setSample() - thus the code
I posted. It does not seem to be too slow, so I am not planning to optimize it by gathering the data in a buffer first and creating a raster around it - yet.

Thanks for the hint.

Leonid

jimorie
Offline
Joined: 2006-03-13

Thanks for the feedback everyone! Your comments have got me on the track.

One thing on which I am a bit shaky and unsure over however, is which SampleModel to use. I read the API docs on the different SampleModels and I read Adobe's PDF Reference on the subject how images are stored. But I still feel uncertain. I will quote the relevant part from the PDF Reference and hope someone can give arguments for what SampleModel I should use.

From Adobe's PDF Reference:
[i]"Sample data is represented as a stream of bytes, interpreted as 8-bit unsigned integers in the range 0 to 255. The bytes constitute a continuous bit stream, with the high-order bit of each byte first. This bit stream, in turn, is divided into units of n bits each, where n is the number of bits per component. Each unit encodes a color component value, given with high-order bit first; units of 16 bits are given with the most significant byte first. Byte boundaries are ignored, except that each row of sample data must begin on a byte boundary. If the number of data bits per row is not a multiple of 8, the end of the row is padded with extra bits to fill out the last byte. A PDF consumer application ignores these padding bits.

Each n-bit unit within the bit stream is interpreted as an unsigned integer in the range 0 to 2n − 1, with the high-order bit first. The image dictionary’s Decode entry maps this integer to a color component value, equivalent to what could be used with color operators such as sc or g. Color components are interleaved sample by sample; for example, in a three-component RGB image, the red, green, and blue components for one sample are followed by the red, green, and blue components for the next."[/i]

Bob Deen

> One thing on which I am a bit shaky and unsure over however, is which SampleModel to use. I read the API docs on the different SampleModels and I read Adobe's PDF Reference on the subject how images are stored. But I still feel uncertain. I will quote the relevant part from the PDF Reference and hope someone can give arguments for what SampleModel I should use.

In the case where n==8, that sounds like a PixelInterleavedSampleModel.
In other cases I don't know; there are bit-packing SM's but I've never
used them and don't know anything about them.

Not that you'd necessarily WANT to, but you could write your own SM as
well, if necessary.

-Bob

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
For additional commands, e-mail: interest-help@jai.dev.java.net

mark.stephens@ukonline.co.uk

In message <23055798.1142246686398.JavaMail.tomcat@sdcst4.sfo.collab.n
et>
jai-interest@javadesktop.org wrote:

> Hello! I am interested in finding out if the Java Advanced Imaging
> library can help me converting raw bitmap image data into a TIFF
> image. I rip image data out of PDF documents, and I know the
> colorspace (CMYK, RGB or Grayscale), the dimensions and the bit depth
> of the image data. I need to convert this to a TIFF image. Can JAI aid
> me in this? I was thinking it should, but a very quick glance through
> some examples didn't immediately tell how. So I thought I'd try this
> forum. Maybe someone could be so very nice and give me a pointer to a
> relevant example, or class descriptions within the JAI API.
>
> All help is much appreciated.
>
> Thanks,
>
> Petter (md1pette mdstud.chalmers.se)
> [Message sent by forum member 'jimorie' (jimorie)]
>
> http://forums.java.net/jive/thread.jspa?messageID=91810
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
> For additional commands, e-mail: interest-help@jai.dev.java.net
>

We wrote a routine to add a header to the CCITT data for our PDF
library. Here is the code to make a TIFF from the raw data.

Other stream types are more complex.

Hope it helps.

MArk

/**
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.jpedal.org
* Project Lead: Mark Stephens (mark@idrsolutions.com)
*
* (C) Copyright 2005, IDRsolutions and Contributors.
*
* This file is part of JPedal
*
@LICENSE@
*
* ---------------
* TiffDecoder.java
* ---------------
* (C) Copyright 2005, by IDRsolutions and Contributors.
*
*
* --------------------------
*/
package org.jpedal.io;

import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;

import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;

import org.jpedal.utils.LogWriter;

import com.sun.media.jai.codec.ByteArraySeekableStream;

/**
* converts CCITT stream into either an image of bytestream
*
* Many thanks to Brian Burkhalter for all his help
*/
public class TiffDecoder {

byte[] bytes;

/**
* called with values from PDF
* Map contains values from PDF as stream pair
*/
public TiffDecoder(int w, int h,Map values,byte[] data){

//return value
bytes=null;

/**
* get values from stream
*/
//flag to show if default is black or white
boolean isBlack = false;
//int columns = 1728; //in PDF spec
int k = 0;
//boolean isByteAligned=false; //in PDF spec

//get k (type of encoding)
String value = (String) values.get("K");
if (value != null)
k = Integer.parseInt(value);

/**
//get flag for white/black as default
value = (String) values.get("EncodedByteAlign");
if (value != null)
isByteAligned =
Boolean.valueOf(value).booleanValue();*/

//get flag for white/black as default
value = (String) values.get("BlackIs1");
if (value != null){
isBlack = Boolean.valueOf(value).booleanValue();

}

/**not used but in Map from PDF
value = (String) values.get("Rows");
if (value != null)
rows = Integer.parseInt(value);

value = (String) values.get("Columns");
if (value != null)
columns= Integer.parseInt(value); */

/**
* build the image
*/
ByteArrayOutputStream bos=new ByteArrayOutputStream();

/**
* tiff header (id, version, offset)
* */
final String[] headerValues={"4d","4d","00","2a","00","00",
"00","08"};
for(int i=0;i bos.write(Integer.parseInt(headerValues[i],16));

int tagCount=9; //appears to be minimum needed
int stripCount=1; //1 strip with all CCITT data

/**
* write IFD image file directory
*/
writeWord(""+tagCount,bos); //num of directory entries
writeTag("256", "04", "01", ""+w, bos); /**image
width*/
writeTag("257", "04", "01", ""+h, bos); /**image
length*/
writeTag("258", "03", "01", "00010000h", bos);
/**BitsPerSample
258 - b&w 1 bit image*/

if (k == 0){
writeTag("259", "03", "01", "00030000h", bos);
/**compression
259 */
}else if (k > 0)
writeTag("259", "03", "01", "00020000h", bos);
/**compression 259 */
else if (k < 0)
writeTag("259", "03", "01", "00040000h", bos);
/**compression 259 */

if(!isBlack)
writeTag("262", "03", "01", "00000000h", bos); /
**photometricInterpretation 262 */
else
writeTag("262", "03", "01", "00010000h", bos); /
**photometricInterpretation 262 */

writeTag("273", "04", "1","122", bos); /**stripOffsets
273 -
start of data after tables */
writeTag("277", "03", "01", "00010000h", bos); /
**samplesPerPixel 277 */
writeTag("278", "04", "01", ""+h, bos); /**rowsPerStrip
278 -
uses height */
writeTag("279", "04", "1", ""+data.length,bos); /
**stripByteCount 279 - 1 strip so all data */
writeDWord("0",bos); /** write next IOD offset zero as
no other
table*/

/**
* write the CCITT image data at the end
*/
try{

bos.write(data);
bos.close();

} catch (IOException e) {
e.printStackTrace();
}

/**setup image */
try {

/**write out to debug*
java.io.FileOutputStream fos=new java.io.FileOutputStream
("macX2test.tiff");
fos.write(bos.toByteArray());
fos.close();
/***/

ByteArraySeekableStream fss=new ByteArraySeekableStream
(bos.toByteArray());//.wrapInputStream(bis,true);

RenderedOp op = (JAI.create("stream",fss));

Raster raster=op.getData();

//Raster raster = img2.getData();
DataBuffer db = raster.getDataBuffer();

DataBufferByte dbb = (DataBufferByte) db;

bytes=dbb.getData();

if(!isBlack){ //invert if needed
int bcount=bytes.length;
for(int i=0;i bytes[i]= (byte) (255-(bytes[i]));
}
}

} catch (Error err) {
LogWriter.writeLog("[PDF] Tiff error "+err);
//
// System.exit(1);
//
} catch (Exception e1) {
e1.printStackTrace();
//
// System.exit(1);
//
}
}

/**return raw bytes from image*/
public byte[] getRawBytes(){

return bytes;

}

/**write word (2 bytes to stream) */
private void writeWord(String i, ByteArrayOutputStream bos)
{

int value=0;

//allow decimal,octal or hex
if(i.endsWith("h"))
value=Integer.parseInt(i.substring(0,i.length()-1),16);
else if(i.endsWith("o"))
value=Integer.parseInt(i.substring(0,i.length()-1),8);
else
value=Integer.parseInt(i);

bos.write((value>>8)); //high byte
bos.write(value & 0xFF); //low byte

}

/**write Dword (4 bytes to stream) */
private void writeDWord(String i, ByteArrayOutputStream bos)
{

int value=0;

//allow decimal,octal or hex
if(i.endsWith("h"))
value=Integer.parseInt(i.substring(0,i.length()-1),16);
else if(i.endsWith("o"))
value=Integer.parseInt(i.substring(0,i.length()-1),8);
else
value=Integer.parseInt(i);

bos.write((value>>24) & 0xff); //high byte
bos.write((value>>16) & 0xff);
bos.write((value>>8) & 0xff);
bos.write(value & 0xFF); //low byte

}

/**write a tag to stream*/
private void writeTag(String TagId, String dataType, String

DataCount, String DataOffset, ByteArrayOutputStream bos) {

writeWord(TagId,bos);
writeWord(dataType,bos);
writeDWord(DataCount,bos);
writeDWord(DataOffset,bos);

}

}

>

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
For additional commands, e-mail: interest-help@jai.dev.java.net

dub
Offline
Joined: 2003-06-11

You can use JAI and friends to *write* the image as TIFF,
but you need to *create* it using java.awt.image, java.awt.color. and fill the raster with samples:

BufferedImage result = createImage(width, height);
WritableRaster raster = result.getRaster();
...
raster.setSample(...);
...

This is what I do to create an image (you will tweak
some parameters, of course).
If you find a simpler way, please share it :)

private static BufferedImage createImage(int width, int height) {
int colorSpace = ColorSpace.CS_sRGB;
ColorSpace cs = ColorSpace.getInstance(colorSpace);
boolean hasAlpha = false;
boolean isAlphaPremultiplied = true;
int transparency = java.awt.Transparency.OPAQUE;
int dataType = java.awt.image.DataBuffer.TYPE_USHORT;
java.awt.image.ColorModel cm = new java.awt.image.ComponentColorModel(
cs,
hasAlpha,
isAlphaPremultiplied,
transparency,
dataType
);

int numBands = 3;
java.awt.image.SampleModel sm = new java.awt.image.BandedSampleModel(
dataType,
width,
height,
numBands
);

java.awt.Point location = null;
java.awt.image.WritableRaster raster =
java.awt.image.Raster.createWritableRaster(sm, location);

java.util.Hashtable properties = null;
BufferedImage result = new BufferedImage(
cm,
raster,
isAlphaPremultiplied,
properties
);

return result;
}

Hope this helps.

Leonid

Fork Labs

Hi,

Another alternative is to find out if you can extract the image with
any of these PDF libraries :

http://schmidt.devlib.org/java/libraries-pdf.html

I know that Clover uses iText to write PDF reports (with images). If
the library can extract your image (exercice left yo you, alas) from
the PDF, use that in the first step of your pipeline.

Regards,

Daniel Léonard

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
For additional commands, e-mail: interest-help@jai.dev.java.net

rabbe
Offline
Joined: 2003-06-14

Rather than constructing an image from it's elements, can't you just extract the image to a temp file and use JAI to read in the image from the temp file, then save the image to TIFF format?

jimorie
Offline
Joined: 2006-03-13

Maybe... I'm not sure I get your point. The "image" as stored by the PDF is a stream of raw bitmap data, with given colorspace, dimension and depth. Right now I do write this data to a file and use ImageMagick (the only application I have found that can display the images) to convert the files to TIFF. What I am now exploring is to do away with ImageMagick and instead build the conversion mechanism directly into my Java application that extracts the images.

If JAI can read an image from file in the formats I am dealing with, that is of course a possibility. But why would it be easier than constructing the image "from its elements" directly in the code? But like I said, I haven't found any useful hints on how to accomplish anything in this direction with JAI. So any tidbits of information or pointers thereto would be great. Thanks!