0

我正在使用带有 Java 11 的 Java Image I/O 来读取 JPEG 图像,通过绘制缩放版本对其进行缩放,然后将其写入。(我使用这种技术是因为它产生了很好的结果,而且我尝试的 JAI 子样本平均技术在图像的某些侧面留下了黑色边框。)

重要的是,我丢弃了所有元数据。(我稍后将一小部分元数据单独写回最终图像,但这与本次讨论无关。)

…
imageReader.setInput(imageInputStream, true, true); //ignore metadata
oldImage = imageReader.read(0, imageReadParam);
…
Image scaledImage = oldImage.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
int oldImageType = oldImage.getType();
int newImageType = oldImageType != BufferedImage.TYPE_CUSTOM ? oldImageType //use the existing image type if it isn't custom
    : (oldImage.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; //otherwise use RGB unless ARGB is needed for transparency
newImage = new BufferedImage(newWidth, newHeight, newImageType);
final Graphics2D graphics = newImage.createGraphics();
try {
  graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
  graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
  graphics.drawImage(scaledImage, null, null);
} finally {
  graphics.dispose();
}
scaledImage.flush();
…
imageWriter.setOutput(imageOutputStream);
IIOImage iioImage = new IIOImage(newImage, null, null); //write with no thumbnails and no metadata
imageWriter.write(null, iioImage, imageWriteParam);
…

一些输入图像具有 ICC 配置文件元数据部分。我担心丢弃 ICC 配置文件元数据是否会改变结果图像的显示方式。我从概念上了解图像颜色配置文件是什么,但我不知道它们如何在文件中工作以及它们如何与颜色空间交互。

我的问题总结:如果我在使用 Java Image I/O 处理图像时总是丢弃 ICC 配置文件和其他与配置文件相关的元数据部分,我如何确保生成的图像颜色正确?

通过与某人的讨论,我推断这些 ICC 配置文件可能适用于 sRGB 以外的颜色空间。(这是真的吗?)如果是这样,使用 Java Image I/O 将图像转换为 sRGB 是否会考虑 ICC 配置文件以便我可以丢弃它?如果是这样,我怎么知道我是否正在转换为 sRGB?

在上面的代码中,我尝试使用现有的图像类型(因为我认为最好不要更改任何内容),除非类型是“自定义”,在这种情况下,我选择一种简单的 RGB 类型。

int newImageType = oldImageType != BufferedImage.TYPE_CUSTOM ? oldImageType //use the existing image type if it isn't custom
    : (oldImage.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; //otherwise use RGB unless ARGB is needed for transparency

但是,如果我尽可能使用旧的图像类型,那可能会使用 RGB 以外的颜色空间,需要 ICC 配置文件吗?如果我丢弃 ICC 配置文件,图像颜色会不正确吗?我应该以某种方式强制 Java Image I/O 转换为 RGB 吗?

我的部分疑问无疑是由于我不了解颜色配置文件在文件中的工作原理,因此,如果您知道一个好的概述,也将不胜感激(不会因字节而迷失在泥泞中)级别详细信息)。

提前感谢您分享您的专业知识。

4

1 回答 1

1

简短回答:如果您从 JPEG 中丢弃 ICC 颜色配置文件,则无法确保颜色正确(除非您还完全控制这些文件的读取)。

然而,当前的代码不只是“丢弃”ICC 配置文件,相反,图像将始终以纯 RGB 色彩空间结束,使用 sRGB 配置文件,并在此过程中被 Java2D 正确转换。由于通常不会嵌入 sRGB 配置文件,因此您的图像将使用使用 sRGB 作为默认配置文件的软件按预期显示。


长答案:

  1. 传递oftrue方法只是向读者暗示它可能会忽略元数据。它不强制跳过元数据(很可能,ICC 配置文件无论如何都不应该被视为元数据)。来自 JavaDoc:ignoreMetadataImageReader.setInput

    ignoreMetadata参数如果设置为true,则允许阅读器忽略在阅读过程中遇到的任何元数据。[...] 读者可以选择忽略此设置并正常返回元数据。

  2. 在 Java 中处理BufferedImages 时,图像将始终使用颜色配置文件。您oldImage应该具有来自 JPEG 的原始颜色配置文件,或者从该配置文件转换为 sRGB。这究竟是如何工作的(如果有的话)完全取决于插件。但据我所知,标准 ImageIO JPEG 插件将读取嵌入的 ICC 配置文件,并将结果转换为 sRGB。

  3. 当你使用Image.getScaledImage我不确定时会发生什么,因为旧的 AWT ImageAPI 不是我使用的东西。最有可能的是,图像已经使用 sRGB,并且仍将使用 sRGB 配置文件。它不应该修改颜色。

  4. 在任何情况下,newImage在 sRGB 配置文件中始终是 RGB,因为没有配置文件信息传递给BufferedImage使用的构造函数。非 RGB 图像将始终为TYPE_CUSTOM,因此转换为TYPE_INT_(A)RGB. 请注意,具有 alpha 通道的图像通常不会被其他 JPEG 软件解释为预期的那样,并且我相信最近的 JDK 已删除了支持,因此我的建议是在编写为 JPEG 之前始终删除任何 alpha 通道。

  5. 最后,writer.write(没有任何元数据)对于标准 ImageIO JPEG 插件,只需将图像写入 JFIF 格式的 JPEG(如果可能)。如果要写入的图像在非标准配置文件中,它只会写入 ICC 配置文件。对于 sRGB,它不会写一个(我假设imageWriteParam都是默认值)。

PS:原始的 JFIF 格式没有指定默认颜色配置文件,但我相信后来的修订(如 IEC 61966-2-1)确实将 sRGB 指定为默认值。Exif 格式允许将 sRGB 指定为颜色配置文件,而无需实际嵌入它,方法是将 ExifColorSpace标记 (tag 40961) 设置为1. 标准 JPEG 插件不编写 Exif 文件(您没有重要的代码)。

您可以通过仔细研究 JDK 的源代码来验证上述大部分陈述。

于 2021-02-15T11:24:34.840 回答