2011-02-01 16 views
10

Sto cercando di implementare una soluzione per calcolare la conversione tra RGB e CMYK e viceversa. Ecco quello che ho finora:Algoritmo da RGB a CMYK e ritorno

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

Tutti vogliono sempre una risposta veloce, è inutile specificare – Eric

+0

Come ha funzionato questa soluzione? Vedo che hai provato ad andare in giro senza ICC_Colorspace, sei riuscito a continuare così? – TacB0sS

risposta

6

Come ha detto Lea Verou, è necessario utilizzare le informazioni sullo spazio colore perché non esiste un algoritmo per mappare da RGB a CMYK. Adobe ha alcuni profili colore ICC disponibili per il download 1, ma non sono sicuro di come siano concessi in licenza.

Una volta che hai i profili colore simile al seguente sarebbe fare il lavoro:

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 è una domanda identica alla tua

Ecco una copia/pasta quella pagina:

/** 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: il risultato di queste formule di conversione è così scadente, il risultato è quasi inutile. Il fatto che siano pubblicati in rete non lo rende migliore. Per favore, smettila di diffonderli ancora di più. – Codo

4

Per convertire con precisione i valori da RGB a CMYK e viceversa, come fa Photoshop, è necessario utilizzare un profilo colore ICC. Tutte le soluzioni algoritmiche semplici che troverai nell'interwebs (come quella pubblicata sopra) sono inacurrate e producono colori che sono al di fuori della gamma cromatica CMYK (per esempio convertono CMYK (100, 0, 0, 0) in rgb (0 , 255, 255) che è ovviamente sbagliato poiché rgb (0, 255, 255) non può essere riprodotto con CMYK). Cerca nelle classi java.awt.color.ICC_ColorSpace e java.awt.color.ICC_Profile per convertire i colori usando i profili colore ICC. Per quanto riguarda i file dei profili colore, Adobe li distribuisce gratuitamente.

+0

Queste classi ICC sono di JDK 7? Non trovo alcun riferimento a loro nel documento Java 6. –

+0

Risposta modificata con collegamenti. Per quanto ne so, non sono né nuovi (li avevo usati 2 anni fa), né in alcune librerie esterne, sono integrati. –

0

Per essere visualizzate correttamente le immagini CMYK dovrebbero contenere color space information come profilo ICC. Quindi il modo migliore è quello di utilizzare tale profilo ICC che può essere facilmente estratto con Sanselan:

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

Nel caso in cui non v'è alcun profilo ICC allegato all'immagine, vorrei utilizzare Adobe profiles come predefinito.

Ora il problema è che non è possibile caricare file JPEG con spazio colore personalizzato utilizzando ImageIO in quanto potrebbe non generare un'eccezione lamentandosi del fatto che non supporta lo spazio colore o lo sthing del genere. Hense si dovrà lavorare con raster:

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

È quindi possibile utilizzare result ovunque ne abbiate bisogno e avrà i colori convertiti.

In pratica, tuttavia, mi sono imbattuto in alcune immagini (scattate con la fotocamera e elaborate con Photoshop) che avevano in qualche modo invertito i valori dei colori, quindi l'immagine risultante era sempre invertita e anche dopo averli invertiti ancora una volta erano troppo luminosi. Anche se non ho ancora idea di come per sapere quando esattamente per usarlo (quando ho bisogno di invertire i valori dei pixel), ho un algoritmo che consente di correggere questi valori e convertire pixel di colore per 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); 
    } 

Sono abbastanza sicuro che RasterOp o ColorConvertOp potessero essere usati per rendere la conversazione più efficiente, ma questo era abbastanza per me.

Seriamente, non è necessario utilizzare questi algoritmi di conversione CMYK-RGB semplificati poiché è possibile utilizzare il profilo ICC incorporato nell'immagine o disponibile gratuitamente da Adobe. L'immagine risultante sembrerà migliore se non perfetta (con profilo incorporato).

3

Un modo migliore per farlo:

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

Qui è il mio modo. Tieni presente che ho riconvertito i colori RGB dal colore 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); 
} 
+0

È sempre la stessa conversione di colore "sbagliata". Non sai quale colore siano i tuoi valori RGB e CMYK, non puoi convertire. Devi lavorare con i profili ICC, come descritto nella risposta accettata 4 anni prima del tuo post. –