RVB à CMJN et algorithme de retour

j'essaie d'implémenter une solution pour calculer la conversion entre RGB et CMYK et vice versa. Voici ce que j'ai jusqu'à présent:

  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};
        }
    }
10
demandé sur Juliet 2011-02-01 04:56:02

7 réponses

comme Lea Verou a dit que vous devriez utiliser l'information d'espace de couleur parce qu'il n'y a pas d'algorithme pour mapper de RVB à CMYK. Adobe a quelques profils de couleur ICC disponibles pour le téléchargement 1 , mais je ne suis pas sûr de la façon dont ils sont sous licence.

une fois que vous avez les profils de couleur quelque chose comme ce qui suit ferait le travail:

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();
        }
    }
}
7
répondu Richard Miskin 2011-02-01 11:19:49

pour convertir avec précision les valeurs de RVB En CMYK et vice versa, comme le fait Photoshop, vous devez utiliser un profil de couleur ICC. Toutes les solutions algorithmiques simples que vous trouverez dans les interwebs (comme celle ci-dessus) sont inacurrate et produisent des couleurs qui sont en dehors de la gamme de couleurs CMYK (par exemple ils convertissent CMYK(100, 0, 0, 0) en rgb(0, 255, 255) ce qui est évidemment faux puisque rgb(0, 255, 255) ne peut pas être reproduit avec CMYK). Regardez dans le Java.awt.couleur.ICC_ColorSpace et java.awt.couleur.ICC_Profile classes pour convertir les couleurs ICC en utilisant des profils de couleur. Comme pour les fichiers de profil de couleur eux-mêmes, Adobe les distribue gratuitement.

5
répondu Lea Verou 2011-02-01 09:01:22

Une meilleure façon de le faire:

    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(); }
3
répondu Varun Arora 2012-08-25 01:29:02

afin d'être affichées correctement, les images CMYK doivent contenir information sur l'espace de couleur comme Profil ICC. Donc la meilleure façon est d'utiliser ce profil ICC qui peut être facilement extrait avec Sanselan :

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

dans le cas où il n'y a pas de profil ICC attaché à l'image, j'utiliserais Adobe profiles par défaut.

maintenant le problème est que vous ne pouvez pas simplement charger JPEG fichier avec l'espace de couleur personnalisé en utilisant ImageIO comme il va échouer jeter une exception se plaindre qu'il ne supporte pas un certain espace de couleur ou la sensation comme cela. Hense vous aurez à travailler avec des rasoirs:

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

vous pouvez alors utiliser result où que vous ayez besoin et il aura des couleurs converties.

dans la pratique, cependant j'ai rencontré quelques images (prises avec la caméra et traitées avec Photoshop) qui avaient en quelque sorte inversé les valeurs de couleur de sorte que le l'image résultante était toujours inversée et même après les avoir inversées une fois de plus ils étaient trop lumineux. Bien que je ne sais toujours pas comment savoir quand exactement l'utiliser (quand j'ai besoin d'inverser les valeurs de pixel), j'ai un algorithme qui corrige ces valeurs et convertir pixel couleur par pixel:

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

Je suis presque sûr que RasterOp ou ColorConvertOp pourrait être utilisé pour rendre la conversation plus efficace, mais c'était suffisant pour moi.

sérieusement, il y a pas besoin d'utiliser ces algorithmes de conversion CMJN en RVB simplifiés car vous pouvez utiliser le profil ICC qui est intégré dans l'image ou disponible gratuitement sur Adobe. Image résultante mieux si pas parfait (avec profil incorporé).

0
répondu Saulius 2011-10-03 16:49:56
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;
}
0
répondu hardip 2018-08-16 10:53:55

ici est une question identique à la vôtre

Voici une copie / pasta de cette page:

/** 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;
-1
répondu Eric 2011-02-01 02:01:25

voici ma façon. Gardez à l'esprit que j'ai reconverti les couleurs RGB de la couleur originale.

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);
}
-1
répondu Michael Kern 2015-04-15 02:06:34