2011-02-01 9 views
10

Próbuję zaimplementować rozwiązanie do obliczania konwersji między RGB i CMYK i na odwrót. Oto co mam do tej pory:RGB do CMYK i algorytm do tyłu

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

Wszyscy zawsze chcą szybką odpowiedź, jego sensu określić – Eric

+0

Jak to rozwiązanie pracował dla Ciebie? Widzę, że próbowałeś to zrobić bez ICC_Colorspace, czy byłeś w stanie to utrzymać? – TacB0sS

Odpowiedz

6

Lea Verou powiedziała, że ​​powinieneś użyć informacji o przestrzeni kolorów, ponieważ nie ma algorytmu mapowania z RGB na CMYK. Adobe ma niektóre profile kolorów ICC dostępne do pobrania 1, ale nie jestem pewien, w jaki sposób są one licencjonowane.

Gdy masz profilami kolorów coś jak poniżej by wykonać zadanie:

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 jest identyczne pytanie do Ciebie

Oto kopia/makaron z tej strony:

/** 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: Wynik tych formuł konwersji jest tak słaby, że wynik jest prawie bezużyteczny. Fakt, że są one publikowane w całej sieci, nie czyni ich lepszymi. Przestań je rozpowszechniać jeszcze bardziej. – Codo

4

Aby dokładnie przeliczyć wartości z RGB do CMYK i odwrotnie, tak jak robi to Photoshop, należy użyć profilu kolorów ICC. Wszystkie proste rozwiązania algorytmiczne, które znajdziesz w interwebs (takich jak ten zamieszczony powyżej) są niedokładne i tworzą kolory, które są poza gamą kolorów CMYK (na przykład konwertują CMYK (100, 0, 0, 0) na rgb (0 255, 255), co jest oczywiście błędne, ponieważ nie można odtworzyć rgb (0, 255, 255) przy użyciu CMYK). Sprawdź klasy java.awt.color.ICC_ColorSpace i java.awt.color.ICC_Profile, aby konwertować kolory za pomocą profili kolorów ICC. Jeśli chodzi o pliki profili kolorów, Adobe dystrybuuje je za darmo.

+0

Czy te klasy ICC pochodzą z JDK 7? Nie znalazłem w nich odnośników w doco Java 6. –

+0

Odpowiedź edytowana za pomocą linków. O ile mi wiadomo, nie są one nowe (użyłem ich 2 lata temu), ani w jakiejś zewnętrznej bibliotece, są one wbudowane. –

0

Aby być wyświetlany poprawnie CMYK obrazy powinny zawierać color space information jako ICC Profile. Tak więc najlepszym sposobem jest użycie tego profilu ICC, który może być łatwo wyodrębniony z Sanselan:

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

W przypadku nie ma profilu ICC dołączone do obrazu, użyłbym Adobe profiles jako domyślną.

Problem polega na tym, że nie można po prostu załadować pliku JPEG z niestandardową przestrzenią kolorów za pomocą ImageIO, ponieważ nie powiedzie się, rzucając wyjątek narzekając, że nie obsługuje on niektórych przestrzeni kolorów lub podobnych obiektów. Hense trzeba będzie pracować z rastrów:

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

Następnie można użyć result gdzie trzeba i będzie musiał przekonwertowane kolory.

W praktyce jednak natknąłem się na niektóre obrazy (zrobione aparatem i przetworzone przy użyciu Photoshopa), które miały w jakiś sposób odwrócone wartości kolorów, więc wynikowy obraz był zawsze odwrócony i nawet po odwróceniu ich ponownie były zbyt jasne. Chociaż nadal nie mają pojęcia, w jaki sposób dowiedzieć się, kiedy dokładnie go używać (gdy muszę odwrócić wartości pikseli), mam algorytm, który rozwiązuje te wartości i konwersja kolorów piksel po pikselu:

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

jestem dość pewny, że RasterOp lub ColorConvertOp mogą być wykorzystane do usprawnienia konwersacji, ale to mi wystarczyło.

Poważnie, nie ma potrzeby korzystania z tych uproszczonych algorytmów konwersji CMYK do RGB, ponieważ można użyć profilu ICC, który jest osadzony w obrazie lub dostępny bezpłatnie od Adobe. Wynikowy obraz będzie wyglądał lepiej, jeśli nie jest idealny (z profilem osadzonym).

3

Lepszym sposobem, aby to zrobić:

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

Oto mój sposób. Pamiętaj, że ponownie zmieniłem kolory RGB z oryginalnego koloru.

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

To wciąż ta sama "niewłaściwa" konwersja kolorów. Nie mając pojęcia, jaki kolor mają wartości RGB i CMYK, nie można dokonać konwersji. Musisz pracować z profilami ICC, zgodnie z opisem w zaakceptowanej odpowiedzi na 4 lata przed Twoim postem. –