0
votes

I need to compress a tif file that has several gray 16bit images (multi-page). I have tried working with ImageIO as here: Tiff compression using Java ImageIO Initially, each image that will be in the tif file comes from another tiff file. When I want to use the compressors, I have the following options:

  1. CCITT RLE, CCITT T.4, CCITT T.6: They give me the error: "javax.imageio.IIOException: I/O error writing TIFF file!"
  2. LZW. I cannot use it. My images are 16bit and LZW increases the size of 16bit images
  3. JPEG. Not possible for 16bit images.
  4. ZLIB. It only reduces 10% even if I specify setCompressionQuality(0.0f);
  5. PackBits. Does not compress.
  6. Deflate. Like ZLIB.
  7. EXIF JPEG. It gives me the error: "javax.imageio.IIOException: Old JPEG compression not supported!"

Does any know any other alternative? I saw an apache imaging library but the tif compression only support the above or less options. Does anyone know about JPEG2000 compressor? Any other kind of alternative?

2

2 Answers

1
votes

PNG compresses 16-bit images losslessly. Libraries and utilities are widely available. JPEG2000 has a lossy 16-bit mode, but you'd have to find some software that supports it. Open JPEG might.

However I'd have to ask: what are your criteria for when you have acceptable image quality and when you do not? If it is visual, then you likely end up at normal JPEG anyway, with a good bit less than 8 bits per pixel effective.

0
votes

Reducing the image 16 bit to 8 bit. Consider that you have a byte[] variable plane16 where you have all the pixels of your image.

Note: My byte[] plane16 gets the data from a 16bit image but byte is 8bit=1byte. Therefore, 2 elements in row of this array are 2byte = 16bit. That is why I convert it to a short[] before operating. If you start from a short[], ommit "ByteBuffer.wrap(plane16).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);"

byte[] plane16; //Fill it with your image!!!

//Do stuff with imageIO. Set writer and compresion method
ImageIO.scanForPlugins();
TIFFImageWriterSpi tiffspi = new TIFFImageWriterSpi();
javax.imageio.ImageWriter writerIO = tiffspi.createWriterInstance();
ImageWriteParam param = writerIO.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionType("ZLib");
param.setCompressionQuality(0.5f);
File fOutputFile = new File(route+".tif");
ImageOutputStream ios = ImageIO.createImageOutputStream(fOutputFile);
writerIO.setOutput(ios);

//Reducing 16bit to 8bit
short[] shorts = new short[plane16.length/2];
ByteBuffer.wrap(plane16).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);

int max = 0;
int min = 999999;
for (int v = 0; v<shorts.length;v++){
    if (max<shorts[v]) max = shorts[v];
    if (min>shorts[v]) min = shorts[v];
}

double range = 255./(max-min);
byte[] plane8 = new byte[shorts.length];
for (int v = 0; v<plane8.length;v++){
    plane8[v] = (byte) ((shorts[v]+min)*range - 128);   
}                       
//16bit:    
/*BufferedImage convertedGrayscale = new BufferedImage(width,
                            heigth, BufferedImage.TYPE_USHORT_GRAY);    
  convertedGrayscale.getRaster().setDataElements(0, 0, width,
                            heigth, shorts);*/

//8bit:
BufferedImage convertedGrayscale = new BufferedImage(width,
                            heigth, BufferedImage.TYPE_BYTE_GRAY);  
convertedGrayscale.getRaster().setDataElements(0, 0, width,
                            heigth, plane8);

//Save image
//If you have a stack of images in tiff, do this trick. "image" is the image number you are setting inside the tiff. If you only have 1 image, remove the if and take the expression from the else.
if (image!=0){
    writerIO.writeInsert(image, new IIOImage(convertedGrayscale, null, null), param);                   
}else{
    writerIO.write(null, new IIOImage(convertedGrayscale, null, null), param);
}

//do the next only after the last image to be saved
writerIO.dispose();
ios.flush();
ios.close();