2011-02-01 28 views
10

Estoy tratando de implementar una solución para calcular la conversión entre RGB y CMYK y viceversa. Aquí es lo que tengo hasta ahora:algoritmo RGB a CMYK y posterior

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

Todo el mundo siempre quiere una respuesta rápida, es inútil especificar – Eric

+0

¿Cómo funcionó esta solución para usted? Veo que trataste de hacerlo sin el ICC_Colorspace, ¿pudiste mantenerlo? – TacB0sS

Respuesta

6

Como Lea Verou dijo que debería hacer uso de la información del espacio de color porque no hay un algoritmo para mapear desde RGB a CMYK. Adobe tiene algunos perfiles de color ICC disponibles para descargar 1, pero no estoy seguro de cómo se otorgan.

Una vez que tenga los perfiles de color haría el trabajo algo como lo siguiente:

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

Here es una cuestión idéntica a la suya

Aquí está una copia/pastas de esa página:

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

-1: El resultado de estas fórmulas de conversión es muy pobre, el resultado es casi inútil. El hecho de que estén publicados en toda la red no lo hace mejor. Por favor, deja de difundirlos aún más. – Codo

4

Para convertir con precisión los valores de RGB a CMYK y viceversa, como lo hace Photoshop, necesita usar un perfil de color ICC. Todas las soluciones algorítmicas simples que encontrará en las interwebs (como la publicada arriba) son ininteligibles y producen colores que están fuera de la gama de colores CMYK (por ejemplo, convierten CMYK (100, 0, 0, 0) en rgb (0 , 255, 255) que obviamente está mal, ya que rgb (0, 255, 255) no se puede reproducir con CMYK. Consulte las clases java.awt.color.ICC_ColorSpace y java.awt.color.ICC_Profile para convertir colores utilizando perfiles de color ICC. En cuanto a los archivos de perfil de color, Adobe los distribuye de forma gratuita.

+0

¿Estas clases de ICC provienen de JDK 7? No encuentro ninguna referencia a ellos en el doco de Java 6. –

+0

Respuesta editada con enlaces. Hasta donde yo sé, no son ni nuevos (los había usado hace 2 años), ni en alguna biblioteca externa, están integrados. –

0

Para que se visualice correctamente las imágenes CMYK deben contener color space information como perfil ICC. Así que la mejor manera es utilizar ese perfil ICC que puede ser fácilmente extraída con Sanselan:

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

En caso de que no existe un perfil ICC adjunto a la imagen, usaría Adobe profiles como predeterminado.

Ahora el problema es que no se puede simplemente cargar un archivo JPEG con espacio de color personalizado usando ImageIO, ya que no generará una excepción quejándose de que no admite espacio de color o algo así. Hense tendrá que trabajar con rasters:

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

continuación, puede utilizar result siempre que se necesite y tendrá colores convertidos.

En la práctica, he encontrado algunas imágenes (tomadas con cámara y procesadas con Photoshop) que de alguna manera habían invertido los valores de color, por lo que la imagen resultante siempre se invertía e incluso después de invertirlas eran demasiado brillantes. Aunque todavía no tengo ni idea de cómo averiguar en qué momento de usarlo (cuando necesito para invertir los valores de píxeles), tengo un algoritmo que corrige estos valores y convertir píxel de color por píxel:

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

estoy Bastante seguro de que RasterOp o ColorConvertOp podrían usarse para hacer la conversación más eficiente, pero esto fue suficiente para mí.

En serio, no hay necesidad de utilizar estos algoritmos de conversión de CMYK a RGB simplificados, ya que puede utilizar el Perfil ICC que está incrustado en la imagen o está disponible de forma gratuita desde Adobe. La imagen resultante se verá mejor si no perfecta (con perfil incrustado).

3

Una mejor manera de hacerlo:

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

Aquí es mi camino. Tenga en cuenta que reconvirtí los colores RGB del color original.

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

Sigue siendo la misma conversión de color "incorrecta". Sin saber de qué color son los valores RGB y CMYK, no puede convertir. Debe trabajar con perfiles ICC, como se describe en las respuestas aceptadas 4 años antes de su publicación. –