RGB в CMYK и обратный алгоритм - PullRequest
10 голосов
/ 01 февраля 2011

Я пытаюсь реализовать решение для расчета конверсии между RGB и CMYK и наоборот.Вот что у меня пока так:

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

Ответы [ 7 ]

7 голосов
/ 01 февраля 2011

Как сказала Леа Веру, вам следует использовать информацию о цветовом пространстве, потому что нет алгоритма для преобразования из RGB в CMYK. Adobe имеет несколько цветовых профилей ICC, доступных для загрузки 1 , но я не уверен, как они лицензируются.

Если у вас есть цветовые профили, то будет работать что-то вроде следующего:

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();
        }
    }
}
6 голосов
/ 01 февраля 2011

Для точного преобразования значений из RGB в CMYK и наоборот, как это делает Photoshop, вам нужно использовать цветовой профиль ICC. Все простые алгоритмические решения, которые вы найдете во внутренних сетях (например, опубликованные выше), являются неточными и производят цвета, которые находятся за пределами цветовой гаммы CMYK (например, они преобразуют CMYK (100, 0, 0, 0) в rgb (0). , 255, 255), что явно неверно, поскольку rgb (0, 255, 255) не может быть воспроизведено с помощью CMYK). Просмотрите классы java.awt.color.ICC_ColorSpace и java.awt.color.ICC_Profile для преобразования цветов с использованием цветовых профилей ICC. Что касается самих файлов цветового профиля, Adobe распространяет их бесплатно.

3 голосов
/ 25 августа 2012

Лучший способ сделать это:

    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(); }
0 голосов
/ 16 августа 2018
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 голосов
/ 15 апреля 2015

Вот мой путь.Имейте в виду, что я переконвертировал цвета RGB из оригинального.

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 голосов
/ 03 октября 2011

Для правильного отображения изображения CMYK должны содержать информацию о цветовом пространстве в качестве профиля ICC. Поэтому лучше всего использовать этот профиль ICC, который можно легко извлечь с помощью Sanselan :

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

Если к изображению не прикреплен профиль ICC, я бы по умолчанию использовал Профили Adobe .

Теперь проблема в том, что вы не можете просто загрузить файл JPEG с пользовательским цветовым пространством, используя ImageIO, так как он не сможет выдать исключение, жалуясь на то, что он не поддерживает некоторое цветовое пространство или подобный стиль. Скорее всего, вам придется работать с растрами:

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

Затем вы можете использовать result там, где вам нужно, и он будет преобразовывать цвета.

На практике, однако, я сталкивался с некоторыми изображениями (снятыми камерой и обработанными с помощью Photoshop), которые каким-то образом инвертировали значения цвета, поэтому результирующее изображение всегда инвертировалось, и даже после инвертирования они были слишком яркими. Хотя я до сих пор не знаю, как узнать, когда именно его использовать (когда мне нужно инвертировать значения пикселей), у меня есть алгоритм, который корректирует эти значения и преобразует цвет пиксель за пикселем:

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

Я почти уверен, что RasterOp или ColorConvertOp могут быть использованы для повышения эффективности разговора, но этого мне было достаточно.

Серьезно, нет необходимости использовать эти упрощенные алгоритмы преобразования CMYK в RGB, так как вы можете использовать профиль ICC, который встроен в изображение или доступен бесплатно от Adobe. Результирующее изображение будет выглядеть лучше, если не идеально (со встроенным профилем).

0 голосов
/ 01 февраля 2011

Здесь - вопрос, идентичный вашему

Вот копия / макароны этой страницы:

/** 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;
...