使用 CYMK 值绘制图像

Draw Image using CYMK values

我想在不使用自动转换工具或库的情况下将缓冲图像从 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 值,有人可以告诉我这意味着什么以及如何处理它吗?

虽然可以,但在没有正确颜色配置文件的情况下将 RGB 转换为 CMYK 不会产生最佳效果。为了获得更好的性能和更高的颜色保真度,我真的建议使用 ICC 颜色配置文件(参见 ICC_ProfileICC_ColorSpace 类)和 ColorConvertOp。 :-)

无论如何,下面是如何使用您自己的转换来完成此操作。重要的部分是创建 CMYK 颜色 space,以及使用该颜色 space 的 ColorModelBufferedImage(您也可以从如上所述的 ICC 配置文件,但颜色可能看起来更差,因为它使用与您不同的计算)。

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 颜色 space 中的 alpha 合成。我会把它留作练习。 ;-)