Pregunta

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

  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};
        }
    }
¿Fue útil?

Solución

Como Lea Verou dijo que debe usar información del espacio de color porque no hay un algoritmo para mapear de RGB a CMYK. Adobe tiene algunos perfiles de color ICC disponibles para descargar1, pero no estoy seguro de cómo tienen licencia.

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

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

Otros consejos

Para convertir con precisión los valores de RGB a CMYK y viceversa, la forma en que Photoshop lo hace, debe usar un perfil de color ICC. Todas las soluciones algorítmicas simples que encontrará en las interwebs (como la que se publicó anteriormente) están inacurradas y producen colores que están fuera de la gama de colores CMYK (por ejemplo, convierten CMYK (100, 0, 0) a RGB (0 , 255, 255) que obviamente es incorrecto ya que RGB (0, 255, 255) no puede reproducirse con CMYK). Mira el java.awt.color.icc_colorspace y java.awt.color.icc_profile Clases para convertir colores con perfiles de color ICC. En cuanto a los archivos de perfil de color, Adobe los distribuye de forma gratuita.

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

Para que se muestren correctamente las imágenes CMYK deben contener Información del espacio de color como perfil ICC. Entonces, la mejor manera es usar ese perfil ICC que se puede extraer fácilmente con Sanselan:

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

En caso de que no haya un perfil ICC conectado a la imagen, usaría Perfiles de adobe por defecto.

Ahora el problema es que no puede simplemente cargar el archivo JPEG con espacio de color personalizado usando Imageio, ya que fallará lanzar una excepción quejándose de que no admite algo de espacio de color o Sthing así. Henso, tendrás que trabajar con los rásteres:

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

Entonces puedes usar result Donde quiera que necesite y tendrá colores convertidos.

En la práctica, sin embargo, me he encontrado con algunas imágenes (tomadas con cámara y procesado con Photoshop) que de alguna manera tenían valores de color invertidos de alguna manera, por lo que la imagen resultante siempre estaba invertida e incluso después de invertirlas una vez más eran demasiado brillantes. Aunque todavía no tengo idea de cómo averiguar cuándo usarlo exactamente (cuando necesito invertir valores de píxeles), tengo un algoritmo que corrige estos valores y convierte el 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 que la conversación sea más eficiente, pero esto fue suficiente para mí.

En serio, no es necesario usar estos algoritmos de conversión CMYK a RGB, ya que puede usar el perfil ICC que está incrustado en una imagen o disponible de forma gratuita de Adobe. La imagen resultante se verá mejor si no es perfecta (con perfil incrustado).

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

Aquí es una pregunta idéntica para la tuya

Aquí hay una copia/pasta 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;

Aquí está 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);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top