【问题标题】:Can I change the compression algorithm used by Java's ImageWriter when creating a JPEG?创建 JPEG 时,我可以更改 Java 的 ImageWriter 使用的压缩算法吗?
【发布时间】:2012-06-11 00:53:22
【问题描述】:

或者,是否有更好的库来处理压缩?

让我以我已经了解的内容作为开头:(1) JPEG 是有损的——它看起来与输入文件不同。 (2) 我可以将压缩质量设置调整到 0.0 到 1.0 之间,就像我在下面的代码中所做的那样。

我正在使用 BufferedImage 并将其转换为 JPEG,并注意到 Java 的 ImageWriter 的 .write() 方法对 JPEG 图像产生了低于标准的结果(例如,与 Photoshop“保存为 Web”相比)。

我的代码现在看起来有点像这样:

// 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();

使用压缩质量设置会产生不同的质量输出,但即使设置为“1.0”,它们看起来也不如我在创建 JPEG 时使用其他工具所能得到的那样好。

由于我是新用户,还不能发布图片...here's a webpage that demos the differences。希望我能在某个时候为可能有类似问题的未来用户永久提供它们。

显然,此特定图像不是 JPEG 压缩的最佳候选者(PNG 更小且无损),但它可以更容易地看到压缩伪影。实际图像将主要是摄影性质的。至少,这更多地质疑 Java 的 JPEG 压缩算法和质量,而不是其他产生看起来更接近原始图像且字节数更少的图像。

【问题讨论】:

  • 刚刚完成了示例链接,供任何立即看到此内容的人使用。
  • 理想的做法是将更好的 JPEG 压缩合并到标准 Java 中;采用开源洁净室实现(GNU/Linux libjpeg?/尤其是 GNU Classpath)并将其移植到 Java。也许在 JUG 的一些支持下,它可能是可行的。

标签: java jpeg image-compression


【解决方案1】:

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

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

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

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

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

查看thisJava图像库,它也可以写JPEG图像。

【讨论】:

  • 感谢您的详细解释。在这种情况下,色度二次采样比质量设置更重要是有道理的。我知道将内置系统与 Photoshop 进行比较并不完全公平,但我认为这将是一个很好的比较标准。再次感谢指点。
猜你喜欢
  • 2023-03-17
  • 1970-01-01
  • 2012-02-05
  • 1970-01-01
  • 1970-01-01
  • 2012-08-30
  • 2018-08-31
  • 1970-01-01
  • 2013-01-26
相关资源
最近更新 更多