简体   繁体   中英

How can I change the color profile of a BufferedImage without changing the pixel values?

I have a BufferedImage with an invalid color profile. I want to replace the color profile with an ICC_Profile without recomputing the pixel values in the image. How can I do that?

In particular the com.sun.imageio.plugins.png.PNGImageReader reads an image with a DCI-P3 ICC profile and produces a BufferedImage with a CS_sRGB ColorSpace but the pixel values correspond to the original DCI-P3 color space and the resulting image has the wrong colors.

If I could just swap the sRGB ColorSpace with a DCI-P3 ColorSpace from the ICC profile the colors would be correct. If I run a ColorConvertOp with a destination ICC_Profile on the image then the resulting colors are (differently) wrong because it interprets the source pixels using the wrong sRGB ColorSpace.

I haven't actually tried this, as there's no code in the question to verify against, but I believe this should work:

If you have the DCI-P3 profile available, you could just create a Java ColorSpace from that, then create a ColorModel using this color space. The new color model needs to have the same type and the same number of components as that in the original image. Finally, create a new BufferedImage using this color model and the Raster of the original image. Something like this:

BufferedImage original; // with incorrect color profile

ColorSpace dciP3 = new ICC_ColorSpace(ICC_Profile.getInstance(dciP3ProfilePath));
ColorModel cm = new ComponentColorModel(dciP3, new int[] {8, 8, 8}, 
                                        false, false, Transparency.OPAQUE,
                                        DataBuffer.TYPE_BYTE);

BufferedImage corrected = new BufferedImage(cm, original.getRaster(), cm.isAlphaPremultiplied(), null);

The above is assuming that your image is RGB (no alpha/transparency), 8 bits/sample and that the original image has a ComponentColorModel . If this is incorrect, you need to modify the color model accordingly. Some common examples:

ComponentColorModel with alpha, 16 bits/sample:

new ComponentColorModel(dciP3, new int[] {16, 16, 16, 16}, 
                        true, false, Transparency.TRANSLUCENT,
                        DataBuffer.TYPE_USHORT);

DirectColorModel with alpha, 8 bits/sample, ARGB order:

new DirectColorModel(dciP3, 32, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000,
                     false, DataBuffer.TYPE_INT)

As there is no duplication or conversion of the pixel data in this case, this operation should be really fast. But it will produce a TYPE_CUSTOM BufferedImage due to the non-standard ICC profile. It will look correct, but might be slow to paint on screen. If you need an image that is fast to paint, using ColorConvertOp might be a better option.

It should be possible to do an in-place conversion on the raster, directly from the DCI-P3 profile to sRGB like this:

BufferedImage original; // with incorrect sRGB profile

ICC_Profile dciP3 = ICC_Profile.getInstance(dciP3ProfilePath);
ICC_Profile sRGB = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
ColorConvertOp convert = new ColorConvertOp(new ICC_Profile[] {dciP3, sRGB}, null);

WritableRaster raster = original.getRaster();
if (raster.getNumNamds() > 3) { // or > profile.getNumComponents() 
    // Create raster that filters out the color components and leaves alpha untouched
    raster = raster.createWritableChild(0, 0, raster.getWidth(), raster.getHeight(), 
                                        0, 0, new int[] {0, 1, 2});
}

convert.filter(raster, raster); 

// original now have corrected pixels in sRGB profile

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM