5

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.

4

1 回答 1

5

“Java 的 ImageWriter 的 .write() 方法为 JPEG 图像生成低于标准的结果(例如,与 Photoshop “Save for Web”相比)。”

发生这种情况的原因不止一个,将 Java imageio 生成的图像质量与 Photoshop 等专业图像软件进行比较是不公平的。

无论如何,让我们看看图像中伪影的最可能原因是什么:通常对于将图像保存为 JPEG 的软件,它允许用户指定一个参数作为压缩或质量,一个是另一个的倒数。此参数用于缩放量化过程中使用的量化表,这是 JPEG 损失的最重要贡献者。不同的编码器可能使用不同的量化表,这部分解释了图像质量差异。

但是可能还有其他因素会影响压缩和图像质量,其中包括在量化过程之前实际发生的色度二次采样(或下采样)。色度二次采样是图像中的颜色信息以比原始分辨率低的分辨率进行采样的过程。为了更好的解释阅读这篇文章。

Calvin Hass 提供了一个出色的 JPEG 阻尼工具,称为 JPEGSnoop,可以从http://www.impulseadventure.com下载。在您提供的 ps75.jpg 图像上使用此工具,我发现以下与色度子采样有关的输出:

 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)

这意味着没有对颜色分量进行二次采样。另一方面,来自 100.jpg 和 75.jpg 的子采样部分是相同的:

 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)

这意味着通过取两个连续像素的平均值,已经在水平和垂直方向上对颜色分量进行了二次采样。

当原始图像由条形和/或正方形组成时,色度二次采样对图像质量的影响将最为显着,就像您的情况一样,并且您已经注意到在这里更容易看到伪影。

因此,IMO,对于这种特殊情况,问题更多来自色度二次采样,而不是质量因子设置。也许我还没有挖得足够深,但是我找不到为 imageio 或它背后的 ImageWriter(很可能是 com.sun.imageio.plugins.jpeg.JPEGImageWriter)设置采样因子的方法,尽管它似乎可以设置ImageWriter 使用的量化和霍夫曼表。

因此,除非您为 imageio 编写自己的 ImageWriter 插件或作为独立插件,否则您不太可能更改 Java 的 ImageWriter 使用的压缩算法。但鉴于 JEPG 压缩算法的复杂性,两者都不是微不足道的。James R. Weeks 编写了一个相对容易遵循的 Java JpegEncoder 实现,默认情况下不进行色度二次采样。它曾经是免费的,但您可以通过搜索网络找到原始版本。

另一个有趣的事情是:从 JPEGSnoop 的输出来看,photoshop 保存的 75% JPEG 图像的实际质量因子实际上显示为 92% 左右。以下来自 Calvin Hass 网站的引用文本将回答为什么 Photoshop 在您的情况下不使用子采样的问题:

顺便说一句,请注意 Photoshop CS2 使用不同的色度子采样级别,具体取决于“保存 JPEG 质量”设置:

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

查看这个Java 图像库,它也可以编写 JPEG 图像。

于 2012-06-10T19:37:53.473 回答