我需要将客户提供的JPG图像转换为sRGB格式(sRGB IEC61966-2.1),以使其可用于Web。

我可以使用ImageIOBufferedImage成功完成此操作,但是此操作确实很慢:

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


(已尝试向RenderingHints提供ColorConvertOp-这无济于事)

据我了解,罪魁祸首是BufferedImage,我需要研究Raster来加快速度:

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,以便最终图像包含适当的颜色空间和颜色配置文件信息。

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

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


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

Color space: RGB
Color profile: sRGB IEC61966-2.1


在这种情况下是正确的。

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

Color space: RGB


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

最佳答案

这是一个“简短”的示例代码,它显示如何“强制” JPEGImageWriter在输出JPEG文件中输出ICC配置文件段,即使图像处于sRGB色彩空间中(如果图像不在sRGB色彩空间中也会自动发生) 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:我认为大多数现代的Web浏览器会将没有配置文件的JPEG视为已经在sRGB中(尽管根据JFIF规范,这是错误的)。因此,只要您进行色彩空间转换,就可能不需要实际输出ICC配置文件。

关于java - sRGB IEC61966-2.1用于光栅图像的ColorModel,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42903133/

10-10 13:34