【问题标题】:sRGB IEC61966-2.1 ColorModel for Raster image用于光栅图像的 sRGB IEC61966-2.1 ColorModel
【发布时间】:2017-08-11 16:53:06
【问题描述】:

我需要将客户提供的 JPG 图像转换为 sRGB 格式 (sRGB IEC61966-2.1) 以使它们为网络做好准备。

我可以用ImageIOBufferedImage成功,但是这个操作真的很慢:

val srgbSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB)
val colorConversionOperation = new ColorConvertOp(srgbSpace, null)
val converted = colorConversionOperation.filter(inputImage, null)

(已经尝试将RenderingHints 提供给ColorConvertOp - 它没有帮助)

据我了解,罪魁祸首是BufferedImage,我需要在Rasters 上工作以加快速度:

val iccProfile = ICC_Profile.getInstance(ColorSpace.CS_sRGB)
val iccColorSpace = new ICC_ColorSpace(iccProfile)
val sourceColorSpace = inputImage.getColorModel.getColorSpace

val colorConversionOperation = new ColorConvertOp(sourceColorSpace, iccColorSpace, null)
val converted = colorConversionOperation.filter(inputImage.getRaster, null)

这确实带来了巨大的性能提升,但我不知道如何保存Raster,以便最终图像包含适当的色彩空间和色彩配置文件信息。

目前我通过以下方式从Raster 创建BufferedImage

val outImage = new BufferedImage(ColorModel.getRGBdefault, converted, false, null);

当我使用BufferedImage 时,最终的 JPG 保存为:

Color space: RGB
Color profile: sRGB IEC61966-2.1

在这种情况下是正确的。

当我使用Rasters 时,最终的 JPG 保存为:

Color space: RGB

这意味着我丢失了Color profile 信息。我认为这是因为我在将Raster 转换为BufferedImage 时使用ColorModel.getRGBdefault,但我不知道如何为sRGB IEC61966-2.1 获取ColorModel 的实例。

【问题讨论】:

  • ColorModel.getRGBdefault 始终位于 sRGB 颜色空间中(如 API 文档中所述)。但我同意,奇怪的是输出 JPEG 不同...问题是,(假设您使用ImageIO 和它的JPEGImageWriter 来编写图像)ICC 颜色配置文件是 not如果颜色空间是 sRGB,则默认写入。出于某种原因,converted 图像中的颜色空间与 sRGB 相同,但isCS_sRGB() 方法返回false(不知道为什么),这就是它的编写原因。我认为您可以通过在元数据(IIOMetadata 类)中传递来强制写入 ICC 配置文件。
  • @haraldK 您是否知道将 ICC 配置文件传递给 IIOmedatada 的任何示例是否可用?我正在查看 IIOMetadata/JPEGMetadata,但我不知道如何在那里传递 ICC 配置文件。我的类路径上有十二个猴子 ImageIO,如果这可以提供任何帮助的话。

标签: java image-processing awt bufferedimage javax.imageio


【解决方案1】:

这是一个“简短”的示例代码,展示了如何“强制”JPEGImageWriter 在输出 JPEG 文件中输出 ICC 配置文件段,即使图像在 sRGB 色彩空间中(如果图像是't 在 sRGB 颜色空间中,正如您在测试中看到的那样)。

public class ICCTest {
    public static void main(String[] args) throws IOException {
        // Fist let's assume this isn't already in sRGB (it is...)
        BufferedImage inputImage = new BufferedImage(10, 10, BufferedImage.TYPE_3BYTE_BGR);

        // Conversion similar to yours
        ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
        WritableRaster raster = new ColorConvertOp(inputImage.getColorModel().getColorSpace(), sRGB, null)
            .filter(inputImage.getRaster(), null);

        ComponentColorModel cm = new ComponentColorModel(sRGB, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
        BufferedImage converted = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);

        // Just for comparison    
        ImageIO.write(converted, "JPEG", new File("converted.jpg"));

        // Write image with forced ICC profile
        ImageWriter writer = ImageIO.getImageWritersByFormatName("JPEG").next();
        try (ImageOutputStream out = ImageIO.createImageOutputStream(new File("converted_icc.jpg"))) {
            writer.setOutput(out);

            ImageWriteParam param = writer.getDefaultWriteParam();
            IIOMetadata metadata = writer.getDefaultImageMetadata(ImageTypeSpecifier.createFromRenderedImage(converted2), param);
            metadata.mergeTree("javax_imageio_jpeg_image_1.0", createICCTree((ICC_ColorSpace) sRGB));

            writer.write(null, new IIOImage(converted, null, metadata), param);
        }
        writer.dispose();
    }

    // Create a minimal IIOMetadata tree, containing an ICC profile
    private static IIOMetadataNode createICCTree(ColorSpace cs) {
        IIOMetadataNode root = new IIOMetadataNode("javax_imageio_jpeg_image_1.0");

        IIOMetadataNode jpegVariety = new IIOMetadataNode("JPEGvariety");
        root.appendChild(jpegVariety);
        root.appendChild(new IIOMetadataNode("markerSequence")); // Must be present, even if empty...

        IIOMetadataNode app0JFIF = new IIOMetadataNode("app0JFIF");
        jpegVariety.appendChild(app0JFIF);

        IIOMetadataNode icc = new IIOMetadataNode("app2ICC");
        app0JFIF.appendChild(icc);

        icc.setUserObject(cs.getProfile());

        return root;
    }
}

您可以在文档中阅读有关JPEG metadata format 的更多信息。

PS:我认为大多数现代网络浏览器会将没有配置文件的 JPEG 视为已经在 sRGB 中(尽管根据 JFIF 规范这是错误的)。因此,您可能不需要实际输出 ICC 配置文件,只要您进行色彩空间转换即可。

【讨论】:

  • 非常感谢。我不熟悉 Java 中的图像处理,如果没有您的帮助,我需要一个月的时间才能将 Color Profile 正确添加到元数据中。干杯!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-14
  • 2020-03-27
  • 1970-01-01
  • 1970-01-01
  • 2015-01-05
相关资源
最近更新 更多