RGB到CMYK和后面的algorithm

我正在尝试实现一种计算RGB和CMYK之间转换的解决scheme,反之亦然。 这是我到目前为止:

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}; } } 

由于Lea Verou说您应该使用色彩空间信息,因为没有algorithm将RGB映射到CMYK。 Adobe有一些ICC颜色configuration文件可供下载1 ,但我不知道他们是如何获得许可的。

一旦你有了颜色configuration文件,就可以完成这项工作:

 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(); } } } 

要准确地将RGB值转换为CMYK值,反之亦然,就像Photoshop一样,您需要使用ICC颜色configuration文件。 所有简单的algorithm解决scheme,你会发现在interwebs(如上面张贴的)是不正确的,并产生颜色超出CMYK色域(例如,他们转换CMYK(100,0,0,0)rgb(0 ,255,255),这显然是错误的,因为rgb(0,255,255)不能用CMYK再现)。 查看java.awt.color.ICC_ColorSpace和java.awt.color.ICC_Profile类,使用ICC颜色configuration文件转换颜色。 至于颜色configuration文件本身,Adobe则免费发布它们。

更好的方法来做到这一点:

  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(); } 

为了正确显示,CMYK图像应该包含ICCconfiguration文件的颜色空间信息 。 所以最好的方法是使用可以用Sanselan轻松提取的ICCconfiguration文件:

 ICC_Profile iccProfile = Sanselan.getICCProfile(new File("filename.jpg")); ColorSpace cs = new ICC_ColorSpace(iccProfile); 

如果没有ICCconfiguration文件附加到图像,我会使用Adobeconfiguration文件作为默认。

现在的问题是,你不能只加载使用ImageIO的自定义色彩空间的JPEG文件,因为它会失败,抛出一个exception,抱怨它不支持一些颜色空间或像这样的Sthing。 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处理),这些图像颠倒了颜色值,所以产生的图像总是被倒转,甚至在反转之后,它们又变得太亮。 虽然我仍然不知道如何确切地使用它(当我需要反转像素值时),但我有一个algorithm来校正这些值并逐个像素地转换颜色:

 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); } 

我很确定RasterOp或ColorConvertOp可以用来使对话更有效率,但这对我来说已经足够了。

严重的是,没有必要使用这些简化的CMYK到RGB转换algorithm,因为您可以使用embedded到图像中或从Adobe免费获得的ICCconfiguration文件。 如果不是完美的(使用embedded的configuration文件),所生成的图像看起来会更好。

这是你的一个相同的问题

这是该页面的复制/面食:

 /** 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; 

这是我的方式。 请记住,我reconverted从原来的颜色的RGB颜色。

 public static String getCMYK(int c){ float computedC = 0; float computedM = 0; float computedY = 0; float computedK = 0; int r = (c >> 16) & 0xFF; int g = (c >> 8) & 0xFF; int b = (c >> 0) & 0xFF; // BLACK if (r==0 && g==0 && b==0) { computedK = 1; return "0 0 0 100"; } computedC = 1 - (r/255f); computedM = 1 - (g/255f); computedY = 1 - (b/255f); float minCMY = Math.min(computedC,Math.min(computedM,computedY)); if (1 - minCMY != 0){ computedC = (computedC - minCMY) / (1 - minCMY) ; computedM = (computedM - minCMY) / (1 - minCMY) ; computedY = (computedY - minCMY) / (1 - minCMY) ; } computedK = minCMY; return (int)(computedC*100f) + " " + (int)(computedM*100f) + " " + (int)(computedY*100f) + " " + (int)(computedK*100f); }