使用CMYK值绘制图像

问题描述 投票:0回答:1

我想将RGBA格式的缓冲图像转换为CYMK格式,而不使用自动转换工具或库,所以我试图从使用BufferedImage.getRGB()的各个像素中提取RGBA值,这里到目前为止我做了什么:

BufferedImage img = new BufferedImage("image path")
int R,G,B,pixel,A;
float Rc,Gc,Bc,K,C,M,Y;
int height = img.getHeight();
int width = img.getWidth();
    for(int y = 0 ; y < height ; y++){
        for(int x = 0 ; x < width ; x++){
            pixel = img.getRGB(x, y);

            //I shifted the int bytes to get RGBA values
            A = (pixel>>24)&0xff;
            R = (pixel>>16)&0xff;
            G = (pixel>>8)&0xff;
            B = (pixel)&0xff;
            Rc = (float) ((float)R/255.0);
            Gc = (float) ((float)G/255.0);
            Bc = (float) ((float)B/255.0);

            // Equations i found on the internet to get CYMK values
            K = 1 - Math.max(Bc, Math.max(Rc, Gc));
            C = (1- Rc - K)/(1-K);
            Y = (1- Bc - K)/(1-K);
            M = (1- Gc - K)/(1-K);                                
        }
    }

现在我提取它之后,我想用这些值绘制或构建一个图像,你能告诉我一个方法或一种方法来做这个,因为我不认为BufferedImage.setRGB()会起作用,而且当我打印出的值时C,Y,M其中一些hadNaN值可以有人告诉我这意味着什么以及如何处理它?

java rgb bufferedimage cmyk image-conversion
1个回答
1
投票

虽然有可能,但如果没有合适的颜色配置文件将RGB转换为CMYK将无法产生最佳效果。为了获得更好的性能和更高的色彩保真度,我建议使用ICC颜色配置文件(请参阅ICC_ProfileICC_ColorSpace类)和ColorConvertOp。 :-)

无论如何,这是使用您自己的转换如何做到这一点。重要的部分是创建一个CMYK颜色空间,以及使用该颜色空间的ColorModelBufferedImage(您也可以从ICC配置文件加载CMYK颜色空间,如上所述,但颜色可能看起来更多,因为它使用不同的计算比你做的那样)。

public static void main(String[] args) throws IOException {
    BufferedImage img = ImageIO.read(new File(args[0]));
    int height = img.getHeight();
    int width = img.getWidth();

    // Create a color model and image in CMYK color space (see custom class below)
    ComponentColorModel cmykModel = new ComponentColorModel(CMYKColorSpace.INSTANCE, false, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
    BufferedImage cmykImg = new BufferedImage(cmykModel, cmykModel.createCompatibleWritableRaster(width, height), cmykModel.isAlphaPremultiplied(), null);
    WritableRaster cmykRaster = cmykImg.getRaster();

    int R,G,B,pixel;
    float Rc,Gc,Bc,K,C,M,Y;

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            pixel = img.getRGB(x, y);

            // Now, as cmykImg already is in CMYK color space, you could actually just invoke
            //cmykImg.setRGB(x, y, pixel);
            // and the method would perform automatic conversion to the dest color space (CMYK)

            // But, here you go... (I just cleaned up your code a little bit):
            R = (pixel >> 16) & 0xff;
            G = (pixel >> 8) & 0xff;
            B = (pixel) & 0xff;

            Rc = R / 255f;
            Gc = G / 255f;
            Bc = B / 255f;

            // Equations I found on the internet to get CMYK values
            K = 1 - Math.max(Bc, Math.max(Rc, Gc));
            if (K == 1f) {
                // All black (this is where you would get NaN values I think)
                C = M = Y = 0;
            }
            else { 
                C = (1- Rc - K)/(1-K);
                M = (1- Gc - K)/(1-K);
                Y = (1- Bc - K)/(1-K);
            }

            // ...and store the CMYK values (as bytes in 0..255 range) in the raster
            cmykRaster.setDataElements(x, y, new byte[] {(byte) (C * 255), (byte) (M * 255), (byte) (Y * 255), (byte) (K * 255)});
        }
    }

    // You should now have a CMYK buffered image
    System.out.println("cmykImg: " + cmykImg);
}

// A simple and not very accurate CMYK color space
// Full source at https://github.com/haraldk/TwelveMonkeys/blob/master/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/color/CMYKColorSpace.java
final static class CMYKColorSpace extends ColorSpace {

    static final ColorSpace INSTANCE = new CMYKColorSpace();

    final ColorSpace sRGB = getInstance(CS_sRGB);

    private CMYKColorSpace() {
        super(ColorSpace.TYPE_CMYK, 4);
    }

    public static ColorSpace getInstance() {
        return INSTANCE;
    }

    public float[] toRGB(float[] colorvalue) {
        return new float[]{
                (1 - colorvalue[0]) * (1 - colorvalue[3]),
                (1 - colorvalue[1]) * (1 - colorvalue[3]),
                (1 - colorvalue[2]) * (1 - colorvalue[3])
        };
    }

    public float[] fromRGB(float[] rgbvalue) {
        // NOTE: This is essentially the same equation you use, except 
        // this is slightly optimized, and values are already in range [0..1]

        // Compute CMY
        float c = 1 - rgbvalue[0];
        float m = 1 - rgbvalue[1];
        float y = 1 - rgbvalue[2];

        // Find K
        float k = Math.min(c, Math.min(m, y));

        // Convert to CMYK values
        return new float[]{(c - k), (m - k), (y - k), k};
    }

    public float[] toCIEXYZ(float[] colorvalue) {
        return sRGB.toCIEXYZ(toRGB(colorvalue));
    }

    public float[] fromCIEXYZ(float[] colorvalue) {
        return sRGB.fromCIEXYZ(fromRGB(colorvalue));
    }
}

PS:你的问题谈到了RGBA和CMYK,但你的代码只是忽略了alpha值,所以我做了同样的事情。如果您真的想要,可以保持alpha值不变并具有CMYK + A图像,以允许在CMYK颜色空间中进行alpha合成。我会把它留作练习。 ;-)

© www.soinside.com 2019 - 2024. All rights reserved.