【问题标题】:Draw Image using CYMK values使用 CMYK 值绘制图像
【发布时间】:2019-04-22 15:32:44
【问题描述】:

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

【问题讨论】:

  • 生成的图像与输入的图像应该相差多远? (含糊地说,我希望您必须将 CMYK 值转换回 RGB,因为 RGB 只是图像在内存中的存储方式。但这没有任何意义。否则,您可能 必须深入研究ColorSpace 和相关课程(如果您对该主题不熟悉,它们可能会很丑……)

标签: java rgb bufferedimage cmyk image-conversion


【解决方案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 合成。我会把它留作练习。 ;-)

【讨论】:

  • 谢谢你这个解释清楚的答案!!。
  • 我在cmykRaster中设置数据元素后,是否会自动影响cmykImg?还是我必须再写一条语句?
  • @Makdous 是的,对光栅的任何更改也会影响图像(粗略地说,光栅是图像的像素,没有颜色信息)。你不需要做任何其他事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-20
  • 2014-06-11
  • 2014-07-06
  • 2023-03-19
  • 2021-01-31
相关资源
最近更新 更多