Search code examples
javajpegtiffjai

JAI Tiff to JPEG convertion issue


I have an issue converting Tiff-Files to JPEGs with JAI. This is my Code:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
        
TIFFDecodeParam param = null;
ImageDecoder dec = ImageCodec.createImageDecoder("tiff", new FileSeekableStream(inPath), param);
RenderedImage op = dec.decodeAsRenderedImage(0);
        
JPEGEncodeParam jpgparam = new JPEGEncodeParam();
jpgparam.setQuality(67);
ImageEncoder en = ImageCodec.createImageEncoder("jpeg", baos, jpgparam);
en.encode(op);

Mostly this code works fine, but with some Images, I got the following error:

java.lang.RuntimeException: Only 1, or 3-band byte data may be written.
at com.sun.media.jai.codecimpl.JPEGImageEncoder.encode(JPEGImageEncoder.java:142)

I cant find any related Problems over here and i have no idea how to fix it. The Images who throw this error have a high Resolution (9000 x 7000 or more) and are mostly scans of old pictures.

Image with this ColorModel works:

ColorModel: 
#pixelBits = 24 
numComponents = 3 
color space = java.awt.color.ICC_ColorSpace@21981a50 
transparency = 1 has alpha = false 
isAlphaPre = false

This not:

ColorModel: 
#pixelBits = 16 
numComponents = 1 
color space = java.awt.color.ICC_ColorSpace@88a30ad 
transparency = 1 has alpha = false 
isAlphaPre = false

Solution

  • I tried reading the JPEG standard, and it is not readily clear whether this is a limitation of the JPEG format or just the Encoder.

    The encoder provide with java only encodes 1 or 3 byte bands, so in your case there are 16bit gray scale images. One way to solve this, as it appears you have done, is to save the image using a PNG encoder. It would not support the compression quality parameter.

    The other way to handle this would be to save your image as an 8bit gray scale image.

    I made a simple example to test this w/out JAI.

    public static void main(String[] args) throws Exception{
            BufferedImage img = new BufferedImage(256, 256, BufferedImage.TYPE_USHORT_GRAY);
            Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix("jpg");
            while( writers.hasNext() ){
                ImageWriter writer = writers.next();
                ImageOutputStream ios = ImageIO.createImageOutputStream( new File("junk.jpg") );
                writer.setOutput(ios);
                writer.write(img);
            }     
        }
    

    The simplest way I can see to convert it is to create a new image and draw to it with a graphics.

    BufferedImage img2 = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
    Graphics g = img2.getGraphics();
    g.drawImage(img, 0, 0);
    g.dispose();
    

    Then img2 can be saved as JPG.