Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I change the compression algorithm used by Java's ImageWriter when creating a JPEG?

Or, alternatively, is there a better library to use to handle compression?

Let me preface this with what I already understand: (1) JPEG is lossy--it won't look the same as the input file. (2) I can adjust the compression quality setting to something between 0.0 and 1.0, as I've done in the code below.

I am taking a BufferedImage and converting it to a JPEG and am noticing that Java's ImageWriter's .write() method produces sub-par results for JPEG images (as compared to Photoshop "Save for Web", as an example).

My code looks a bit like this right now:

// img is a BufferedImage, here
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(.75f);

IIOImage image = new IIOImage(img, null, null);
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(byteArrayOut));
writer.write(null, image, iwp);
writer.dispose();

Playing with the compression quality setting produces different quality outputs, but even at setting "1.0", they don't look as nice as what I can get with other tools when creating a JPEG.

Since I'm a new user and can't post images yet... here's a webpage that demos the differences. Hopefully I can get them in here permanently at some point for future users who may have a similar question.

Obviously, this particular image is not the best candidate for JPEG compression (the PNG is much smaller and lossless), but it allows the compression artifacts to be seen more easily. The actual images will be mostly photographic in nature. At the very least, this is more to question the algorithm and quality of Java's JPEG compression versus others out there that produce images that look closer to the original with fewer bytes.

like image 420
timehat Avatar asked Jun 06 '12 23:06

timehat


1 Answers

"Java's ImageWriter's .write() method produces sub-par results for JPEG images (as compared to Photoshop "Save for Web", as an example)."

There are more than one reasons why this happens and it is unfair to compare image quality produced by Java imageio with a professional image software as photoshop.

Anyway, let's see what the most probable cause is for the artifacts in your image: usually for a software to save images as JPEG, it will allow user to specify a parameter either as compression or quality, one is the inverse of the other. This parameter is used to scale the quantization tables used in the quantization process which is the most significant contributor to JPEG lossiness. Different encoder may use different quantization tables which partially accounts for the image quality difference.

But there could be other factors which affect the compression and image quality among which is chroma subsampling (or downsampling) which actually happens before the quantization process. Chroma subsampling is the process whereby the color information in the image is sampled at a lower resolution than the original. For better explanation read this article.

Calvin Hass provides an excellent JPEG damping tool called JPEGSnoop which can be downloaded from http://www.impulseadventure.com. Using this tool on the ps75.jpg image you provided, I found the following output which pertains to chroma subsampling:

 Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y)
 Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb)
 Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr)

which means there is no subsampling done on the color components. On the other hand, the subsamping part from both 100.jpg and 75.jpg are the same:

 Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y)
 Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb)
 Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr)

which means subsamping has been done on both horizontal and vertical directions for color components by taking the average of two consecutive pixels.

The effect of chroma subsampling on image quality will be most notable when the original images are composed of strip and/or squares as in your case and you've already noticed the artifacts are easier to be seen here.

So, IMO, for this special case, the problem is more from the chroma subsampling than from the quality factor setting. Maybe I haven't dug deep enough, but I could not find a way to set sampling factors for imageio or the ImageWriter behind it (which is most probably com.sun.imageio.plugins.jpeg.JPEGImageWriter) though it seems possible to set quantization and Huffman tables to be used by ImageWriter.

Therefore, it is unlikely that you could change the compression algorithm used by Java's ImageWriter unless you write your own ImageWriter plugin for imageio or as a standalone one. But both are non-trivial given the complexity of JEPG compression algorithm. There is one relatively easy to follow Java JpegEncoder implementation written by James R. Weeks which by default doesn't do chroma subsampling. It used to be free but you can find the original versions by searching the web.

Another interesting thing is: from the output of JPEGSnoop, the actual quality factor for the 75% JPEG image saved by photoshop is actually shown as around 92%. And the following quoted text from Calvin Hass's website will answer the question why photoshop using no subsampling in your case:

As an aside, note that Photoshop CS2 uses different chroma subsampling levels depending on the Save JPEG Quality settings:

Photoshop Save As Quality 0-6 - 2x2 Chroma Subsampling
Photoshop Save As Quality 7-12 - 1x1 No Chroma Subsampling
Photoshop Save For Web Quality 0-50 - 2x2 Chroma Subsampling
Photoshop Save For Web Quality 51-100 - 1x1 No Chroma Subsampling

Check out this Java image library which can write JPEG image as well.

like image 182
dragon66 Avatar answered Sep 30 '22 01:09

dragon66