使用CYMK值绘制图像

时间:2019-04-22 15:32:45

标签: java rgb bufferedimage cmyk image-conversion

我想在不使用自动转换工具或库的情况下将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的值时,其中一些具有NaN的值,有人可以告诉我这是什么意思以及如何处理吗?

1 个答案:

答案 0 :(得分: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合成。我将其保留为练习。 ;-)