Java RGB到CMYK和back算法

Java RGB到CMYK和back算法,java,rgb,cmyk,Java,Rgb,Cmyk,我试图实现一个解决方案,用于计算RGB和CMYK之间的转换,反之亦然。以下是我到目前为止的情况: public static int[] rgbToCmyk(int red, int green, int blue) { int black = Math.min(Math.min(255 - red, 255 - green), 255 - blue); if (black!=255) { int cyan = (255

我试图实现一个解决方案,用于计算RGB和CMYK之间的转换,反之亦然。以下是我到目前为止的情况:

  public static int[] rgbToCmyk(int red, int green, int blue)
    {
        int black = Math.min(Math.min(255 - red, 255 - green), 255 - blue);

        if (black!=255) {
            int cyan    = (255-red-black)/(255-black);
            int magenta = (255-green-black)/(255-black);
            int yellow  = (255-blue-black)/(255-black);
            return new int[] {cyan,magenta,yellow,black};
        } else {
            int cyan = 255 - red;
            int magenta = 255 - green;
            int yellow = 255 - blue;
            return new int[] {cyan,magenta,yellow,black};
        }
    }

    public static int[] cmykToRgb(int cyan, int magenta, int yellow, int black)
    {
        if (black!=255) {
            int R = ((255-cyan) * (255-black)) / 255; 
            int G = ((255-magenta) * (255-black)) / 255; 
            int B = ((255-yellow) * (255-black)) / 255;
            return new int[] {R,G,B};
        } else {
            int R = 255 - cyan;
            int G = 255 - magenta;
            int B = 255 - yellow;
            return new int[] {R,G,B};
        }
    }
这个问题和你的一样

这是该页的副本/面食:

/** CMYK to RGB conversion */
/* Adobe PhotoShop algorithm */
cyan = Math.min(255, cyan + black); //black is from K
magenta = Math.min(255, magenta + black);
yellow = Math.min(255, yellow + black);
rgb[0] = 255 - cyan;
rgb[1] = 255 - magenta;
rgb[2] = 255 - yellow;


/* GNU Ghostscript algorithm -- this is better*/
int colors = 255 - black;
rgb[0] = colors * (255 - cyan)/255;
rgb[1] = colors * (255 - magenta)/255;
rgb[2] = colors * (255 - yellow)/255;

要像Photoshop那样准确地将值从RGB转换为CMYK,反之亦然,您需要使用ICC颜色配置文件。你可以在互联网站中找到的所有简单算法解决方案(如上面发布的)都是不通用的,并且产生的颜色超出了CMYK色域(例如,它们将CMYK(100,0,0,0,0)转换为rgb(0,255,255),这显然是错误的,因为rgb(0,255,255)不能用CMYK复制)。 查看使用ICC颜色配置文件转换颜色的方法和类。
至于颜色配置文件本身,Adobe免费分发它们。

正如Lea Verou所说的,你应该利用颜色空间信息,因为没有从RGB映射到CMYK的算法。Adobe有一些ICC颜色配置文件可供下载,但我不确定它们是如何获得许可的

一旦您有了颜色配置文件,下面的内容就可以完成这项工作:

import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.io.IOException;
import java.util.Arrays;


public class ColorConv {
    final static String pathToCMYKProfile = "C:\\UncoatedFOGRA29.icc";

    public static float[] rgbToCmyk(float... rgb) throws IOException {
        if (rgb.length != 3) {
            throw new IllegalArgumentException();
        }
        ColorSpace instance = new ICC_ColorSpace(ICC_Profile.getInstance(pathToCMYKProfile));
        float[] fromRGB = instance.fromRGB(rgb);
        return fromRGB;
    }
    public static float[] cmykToRgb(float... cmyk) throws IOException {
        if (cmyk.length != 4) {
            throw new IllegalArgumentException();
        }
        ColorSpace instance = new ICC_ColorSpace(ICC_Profile.getInstance(pathToCMYKProfile));
        float[] fromRGB = instance.toRGB(cmyk);
        return fromRGB;
    }

    public static void main(String... args) {
        try {
            float[] rgbToCmyk = rgbToCmyk(1.0f, 1.0f, 1.0f);
            System.out.println(Arrays.toString(rgbToCmyk));
            System.out.println(Arrays.toString(cmykToRgb(rgbToCmyk[0], rgbToCmyk[1], rgbToCmyk[2], rgbToCmyk[3])));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

为了正确显示,CMYK图像应包含ICC配置文件。因此,最好的方法是使用ICC配置文件,该配置文件可以通过以下方式轻松提取:

如果没有ICC配置文件附加到图像,我将使用默认设置

现在的问题是,您不能仅仅使用ImageIO加载带有自定义颜色空间的JPEG文件,因为它将无法引发异常,抱怨它不支持某些颜色空间或类似的内容。Hense您必须使用光栅:

JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new ByteArrayInputStream(data));
Raster srcRaster = decoder.decodeAsRaster();

BufferedImage result = new BufferedImage(srcRaster.getWidth(), srcRaster.getHeight(), BufferedImage.TYPE_INT_RGB);
WritableRaster resultRaster = result.getRaster();

ColorConvertOp cmykToRgb = new ColorConvertOp(cs, result.getColorModel().getColorSpace(), null);
cmykToRgb.filter(srcRaster, resultRaster);
然后,您可以在任何需要的地方使用
result
,它将具有转换后的颜色

然而,在实践中,我遇到了一些图像(用相机拍摄并用Photoshop处理),它们的颜色值以某种方式反转,因此生成的图像总是反转的,即使再次反转,它们也太亮了。虽然我仍然不知道如何准确地确定何时使用它(当我需要反转像素值时),但我有一个算法可以纠正这些值并逐像素转换颜色:

JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new ByteArrayInputStream(data));
Raster srcRaster =  decoder.decodeAsRaster();

BufferedImage ret = new BufferedImage(srcRaster.getWidth(), srcRaster.getHeight(), BufferedImage.TYPE_INT_RGB);
WritableRaster resultRaster = ret.getRaster();

for (int x = srcRaster.getMinX(); x < srcRaster.getWidth(); ++x)
    for (int y = srcRaster.getMinY(); y < srcRaster.getHeight(); ++y) {

        float[] p = srcRaster.getPixel(x, y, (float[])null);

        for (int i = 0; i < p.length; ++i)
            p[i] = 1 - p[i] / 255f;

        p = cs.toRGB(p);

        for (int i = 0; i < p.length; ++i)
            p[i] = p[i] * 255f;

        resultRaster.setPixel(x, y, p);
    }
JPEGImageDecoder decoder=JPEGCodec.createJPEGDecoder(新的ByteArrayInputStream(数据));
Raster srcRaster=解码器。decodeAsRaster();
BuffereImage ret=新的BuffereImage(srcRaster.getWidth(),srcRaster.getHeight(),BuffereImage.TYPE_INT_RGB);
WritableRaster resultRaster=ret.getRaster();
对于(int x=srcRaster.getMinX();x
我很确定RasterOp或ColorConvertOp可以用来提高对话效率,但这对我来说已经足够了

说真的,不需要使用这些简化的CMYK到RGB转换算法,因为您可以使用嵌入到图像中或从Adobe免费获得的ICC配置文件。生成的图像如果不完美(使用嵌入式配置文件),效果会更好。

更好的方法:

    try {
        // The "from" CMYK colorspace
        ColorSpace cmykColorspace = new ICC_ColorSpace(ICC_Profile.getInstance("icc/CoatedFOGRA27.icc"));
        // The "to" RGB colorspace
        ColorSpace rgbColorspace = new ICC_ColorSpace(ICC_Profile.getInstance("icc/AdobeRGB1998.icc"));

        // Bring in to CIEXYZ colorspace (refer to Java documentation: http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/color/ColorSpace.html)
        float[] ciexyz = cmykColorspace.toCIEXYZ(cmyk);
        float[] thisColorspace = rgbColorspace.fromCIEXYZ(ciexyz);
        float[] rgb = thisColorspace;
        Color c = new Color(rgb[0], rgb[1], rgb[2]);

        // Format RGB as Hex and return
        return String.format("#%06x", c.getRGB() & 0xFFFFFF);
    } catch (IOException e) { e.printStackTrace(); }

每个人都希望得到快速的答案,但没有必要具体说明这个解决方案对你有何作用?我看到你试图在没有ICC_颜色空间的情况下进行这项工作,你能保持下去吗?这些ICC类来自JDK 7吗?我在用链接编辑的Java6doco.Answer中找不到对它们的引用。据我所知,它们既不是新的(我两年前就用过),也不是在某个外部库中内置的。-1:这些转换公式的结果太差了,结果几乎毫无用处。事实上,他们被张贴在网上并没有使它变得更好。请停止传播它们。这不就是把RGB转换成色彩空间调整过的RGB吗?当我使用
BogusColorSpace
运行这个示例时,我只返回相同的R、G、B值,而不是正确的C、M、Y、K数组。注意,我还针对
.icc
配置文件提供的驱动程序运行了这个示例,并生成了看起来是R、G、B的内容。如果我们真的想直接与C、M、Y、K墨盒/色带/碳粉通话,该怎么办?如何获取这4个值?回答我自己的问题。。。显然,并非所有的
.icc
配置文件都是平等创建的。我刚刚使用供应商提供的
.icc
配置文件在
C:\Windows\system32\spool\drivers\color
中进行了测试,它没有返回CMYK,而是返回RGB值。。。但是,此评论中提到的Adobe驱动程序确实返回CMYK:)
    try {
        // The "from" CMYK colorspace
        ColorSpace cmykColorspace = new ICC_ColorSpace(ICC_Profile.getInstance("icc/CoatedFOGRA27.icc"));
        // The "to" RGB colorspace
        ColorSpace rgbColorspace = new ICC_ColorSpace(ICC_Profile.getInstance("icc/AdobeRGB1998.icc"));

        // Bring in to CIEXYZ colorspace (refer to Java documentation: http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/color/ColorSpace.html)
        float[] ciexyz = cmykColorspace.toCIEXYZ(cmyk);
        float[] thisColorspace = rgbColorspace.fromCIEXYZ(ciexyz);
        float[] rgb = thisColorspace;
        Color c = new Color(rgb[0], rgb[1], rgb[2]);

        // Format RGB as Hex and return
        return String.format("#%06x", c.getRGB() & 0xFFFFFF);
    } catch (IOException e) { e.printStackTrace(); }
public static String makeCMYKString(int color) {
    double red = Color.red(color);
    double green = Color.green(color);
    double blue = Color.blue(color);
    double red1 = red / 255;
    double green1 = green / 255;
    double blue1 = blue / 255;
    double max = (Math.max(Math.max(red1, green1), blue1));
    double K = 1 - max;
    double C = (1 - red1 - K) / (1 - K);
    double M = (1 - green1 - K) / (1 - K);
    double Y = (1 - blue1 - K) / (1 - K);
    double CMYK[] = {C, M, Y, K};
    String cmyk = "CMYK = (" + Math.round(C * 100) + " , " + Math.round(M * 100) + " , " + Math.round(Y * 100) + " , " + Math.round(K * 100) + ")";
    return cmyk;
}