为什么Java ImageIO会压缩JPEG颜色

10

我读取某些JPG文件时,颜色会变得平淡。这里有一个简单的例子,它读取了一个jpg文件,并将相同的图像写入另一个文件。

import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;

public class JPegReadTest {
    public static void main(String[] args) {
        if (args.length == 2) {
            try {
                BufferedImage src = ImageIO.read(new File(args[0]));
                ImageIO.write(src, "jpg", new File(args[1]));
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.err.println("Usage: java JPegReadTest src dest");
        }
    }
}

如果你尝试使用例如http://www.flickr.com/photos/visualpanic/233508614/sizes/l/这样的图片,目标图片的颜色与源文件不同。为什么会这样?如何修复?

我也尝试将图像保存为png格式,但其中的颜色也很平淡(因此推测颜色信息未被正确读取)。


你解决了如何保留颜色配置文件吗?我也遇到了同样的问题。 - Thang Pham
5个回答

10

可能有几个原因。

  1. JPEG颜色数据通常以YCrCb而非RGB存储,尽管转换应该大多数情况下不会被注意到。
  2. JPEG通常具有嵌入式颜色配置文件,但许多应用程序不理解这一点,只是忽略它(在这种情况下,你的输出文件可能缺少颜色配置文件)。
  3. Java损坏gamma值后可能会重置或丢失。

我实际上没有尝试过你的示例... 你能否请发布之前和之后的文件?如果没有实际检查结果文件,很难确定是否存在额外数据。

编辑:是的,显然你的原始图像和转换后的图像具有不同的颜色配置文件。Java去掉了原来的颜色配置文件并使用了通用的sRGB。在Windows上使用Firefox和各种程序时,它们对我们看起来是相同的,因为这些程序在渲染时不使用颜色配置文件。但是,在你的Mac上,Mac实际上支持这些颜色配置文件(引发有关Mac用于图形等方面的辩论),因此它们呈现出不同的效果。我手头没有Mac,但我猜如果你在任何平台上使用Photoshop打开文件,你会看到差异。


好的,这是之前和之后的文件。 http://terokinnunen.jalbum.net/Colortest/orig.jpg http://terokinnunen.jalbum.net/Colortest/converted-jpg.jpg还尝试保存为PNG格式,但仍然很平淡。 http://terokinnunen.jalbum.net/Colortest/converted-png.png - ketorin
有趣的是,对于我来说(Ubuntu 9.04,Firefox 3.5/Gimp),原始图像和转换后的图像看起来几乎一样(除了JPEG伪影)。唯一我在GIMP中发现的区别是,原始图像指定了“sRGB IEC61966-2.1”作为颜色空间,而转换后的图像则是“内置sRGB”。 - Joachim Sauer
我的理论是,差异在于以下两点:1)所讨论的环境是否处理色彩配置文件?2)显示器/图像/输出设备的哪种色彩配置文件被配置了? - Joachim Sauer
是的,我认为我们找到了根本原因,就是丢失了颜色配置文件信息!这太出乎意料了,以至于我很高兴能够将其制定成一个好的stackoverflow问题-答案对。因此,在我看来,一个适当的回应应该包括如何保留所需信息的代码。我尝试按照这里的说明操作:http://forums.java.net/jive/message.jspa?messageID=205964,但至少目前还没有成功。 - ketorin
@ketorin:你问的是“为什么”,而不是“如何避免这个问题”。既然你觉得答案很好(我也同意),那么你应该接受这个答案。也许可以再问一个关于“如何”的问题... :-) - PhiLho
显示剩余2条评论

2
也许你的源图像具有比sRGB更宽的色域(如Adobe RGB)的分配的色彩配置文件,并且您的加载/保存循环没有保留颜色空间信息?如果没有颜色配置文件,您的查看器将假定为sRGB,并且压缩的色域将使所有内容看起来“平淡无奇”。如果您有exiftool工具,请使用它。
exiftool -ProfileDescription filename.jpg

这是一种快速验证源图像和输出图像色彩配置文件的方法。


是的,这可能是问题所在,文件输出中确实缺少该配置文件。这个线程看起来很有前途:http://forums.java.net/jive/thread.jspa?threadID=60631&tstart=0,我接下来会去看看它。 - ketorin
糟糕,那是错误的线程:http://forums.java.net/jive/message.jspa?messageID=205964 - ketorin

0

这个回答太笼统了。你试过用问题中的图片吗?基本上你是在告诉 OP 再次“查看文档”。而且被接受的答案表明,这个回答不能解决 OP 的问题。 - andr

0

JPEG是一种有损格式。当读入时,Java将其存储为原始格式,类似于BMP。然后再次写出时会导致数据丢失。此外,与使用GIMP等工具相比,对质量的控制不太多。

也许可以考虑使用其他API,如Image Magick,以便更好地控制质量。


2
不相关。OP指出了一个重要的颜色配置文件问题,超出了简单的重新压缩范畴。 - erjiang

0

JPEG 是一种有损压缩格式。

这意味着,如果您打开一个文件并再次保存它,除非您采取非常特定的步骤不丢失任何信息(在这种情况下,可能的操作非常受限),否则您将会丢失一些信息。

此外,ImageIO.write() 可能使用一些默认的质量设置来保存 JPEG 文件,这可能比原始质量低,导致额外的质量损失。

尝试将其保存为 PNG 文件,您会发现它看起来与源文件相同。


我尝试保存为png格式,但颜色看起来也很单调,与保存为jpg格式的图像相同。我有一种预感,颜色可能没有被正确读取。 - ketorin
在这种情况下,问题可能是图像中存储了一些Java无法读取/解释的颜色配置文件。 - Joachim Sauer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接