Java:如何将RGB颜色转换为CIE Lab

use*_*527 15 java color-space

如何在CIE Lab颜色模型中显示对象颜色.

Color c = ...
float[] lab = {0,0,0};
...
c.getColorComponents(ColorSpace.getInstance(???), lab);
Run Code Online (Sandbox Code Playgroud)

但是我无法用CIE Lab强制这项工作(尽管TYPE_Lab是在ColorSpace类中呈现的)

请注意.

fin*_*nnw 20

这是我的实现:

import java.awt.color.ColorSpace;

public class CIELab extends ColorSpace {

    public static CIELab getInstance() {
        return Holder.INSTANCE;
    }

    @Override
    public float[] fromCIEXYZ(float[] colorvalue) {
        double l = f(colorvalue[1]);
        double L = 116.0 * l - 16.0;
        double a = 500.0 * (f(colorvalue[0]) - l);
        double b = 200.0 * (l - f(colorvalue[2]));
        return new float[] {(float) L, (float) a, (float) b};
    }

    @Override
    public float[] fromRGB(float[] rgbvalue) {
        float[] xyz = CIEXYZ.fromRGB(rgbvalue);
        return fromCIEXYZ(xyz);
    }

    @Override
    public float getMaxValue(int component) {
        return 128f;
    }

    @Override
    public float getMinValue(int component) {
        return (component == 0)? 0f: -128f;
    }    

    @Override
    public String getName(int idx) {
        return String.valueOf("Lab".charAt(idx));
    }

    @Override
    public float[] toCIEXYZ(float[] colorvalue) {
        double i = (colorvalue[0] + 16.0) * (1.0 / 116.0);
        double X = fInv(i + colorvalue[1] * (1.0 / 500.0));
        double Y = fInv(i);
        double Z = fInv(i - colorvalue[2] * (1.0 / 200.0));
        return new float[] {(float) X, (float) Y, (float) Z};
    }

    @Override
    public float[] toRGB(float[] colorvalue) {
        float[] xyz = toCIEXYZ(colorvalue);
        return CIEXYZ.toRGB(xyz);
    }

    CIELab() {
        super(ColorSpace.TYPE_Lab, 3);
    }

    private static double f(double x) {
        if (x > 216.0 / 24389.0) {
            return Math.cbrt(x);
        } else {
            return (841.0 / 108.0) * x + N;
        }
    }

    private static double fInv(double x) {
        if (x > 6.0 / 29.0) {
            return x*x*x;
        } else {
            return (108.0 / 841.0) * (x - N);
        }
    }

    private Object readResolve() {
        return getInstance();
    }

    private static class Holder {
        static final CIELab INSTANCE = new CIELab();
    }

    private static final long serialVersionUID = 5027741380892134289L;

    private static final ColorSpace CIEXYZ =
        ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);

    private static final double N = 4.0 / 29.0;

}
Run Code Online (Sandbox Code Playgroud)


cha*_*ker 9

我在使用@finw的答案中的代码时遇到了一些问题.我相信它们主要是因为要进行CIELab转换,你应该指定一个光源:

http://en.wikipedia.org/wiki/Standard_illuminant

其中一个流行的标准是D50,它基本上只是标准的日光.因为@ finw的代码没有照明校正,所以应该是中性灰色的颜色会略微着色.检查这种方法的一种方法是尝试:

 float[] g = { 50.0f, 0f, 0f };
 CIELab.getInstance().toRGB(g); 
 for (float f : g) System.out.println(f);
Run Code Online (Sandbox Code Playgroud)

您应该在所有三个通道上获得大致相同的数字,但最终会得到明显(尽管略微)蓝色的RGB配置文件.我确信有可能在@ finw的代码中纠正这个问题,但经过一段时间的游戏和搜索后,我发现了一些优秀的转换代码:

http://www.f4.fhtw-berlin.de/~barthel/ImageJ/ColorInspector//HTMLHelp/farbraumJava.htm

为了完整,这里是.

public void rgb2lab(int R, int G, int B, int[] lab) {
    //http://www.brucelindbloom.com

    float r, g, b, X, Y, Z, fx, fy, fz, xr, yr, zr;
    float Ls, as, bs;
    float eps = 216.f/24389.f;
    float k = 24389.f/27.f;

    float Xr = 0.964221f;  // reference white D50
    float Yr = 1.0f;
    float Zr = 0.825211f;

    // RGB to XYZ
    r = R/255.f; //R 0..1
    g = G/255.f; //G 0..1
    b = B/255.f; //B 0..1

    // assuming sRGB (D65)
    if (r <= 0.04045)
        r = r/12;
    else
        r = (float) Math.pow((r+0.055)/1.055,2.4);

    if (g <= 0.04045)
        g = g/12;
    else
        g = (float) Math.pow((g+0.055)/1.055,2.4);

    if (b <= 0.04045)
        b = b/12;
    else
        b = (float) Math.pow((b+0.055)/1.055,2.4);


    X =  0.436052025f*r     + 0.385081593f*g + 0.143087414f *b;
    Y =  0.222491598f*r     + 0.71688606f *g + 0.060621486f *b;
    Z =  0.013929122f*r     + 0.097097002f*g + 0.71418547f  *b;

    // XYZ to Lab
    xr = X/Xr;
    yr = Y/Yr;
    zr = Z/Zr;

    if ( xr > eps )
        fx =  (float) Math.pow(xr, 1/3.);
    else
        fx = (float) ((k * xr + 16.) / 116.);

    if ( yr > eps )
        fy =  (float) Math.pow(yr, 1/3.);
    else
    fy = (float) ((k * yr + 16.) / 116.);

    if ( zr > eps )
        fz =  (float) Math.pow(zr, 1/3.);
    else
        fz = (float) ((k * zr + 16.) / 116);

    Ls = ( 116 * fy ) - 16;
    as = 500*(fx-fy);
    bs = 200*(fy-fz);

    lab[0] = (int) (2.55*Ls + .5);
    lab[1] = (int) (as + .5); 
    lab[2] = (int) (bs + .5);       
} 
Run Code Online (Sandbox Code Playgroud)

在我的测试中,它产生适当无色度的灰度值,并且启动速度更快.

  • 看来 `Ls` 已扩大到填充 [0, 255] 范围。仅返回“Ls”就足够了。我还省略了`+ .5`。它们看起来是多余的,因为它们会导致值溢出。我可以对值进行四舍五入,这样更有意义。 (2认同)

Tha*_*101 7

我使用了这段代码并且它有效:

\n\n
public double[] rgbToLab(int R, int G, int B) {\n\n    double r, g, b, X, Y, Z, xr, yr, zr;\n\n    // D65/2\xc2\xb0\n    double Xr = 95.047;  \n    double Yr = 100.0;\n    double Zr = 108.883;\n\n\n    // --------- RGB to XYZ ---------//\n\n    r = R/255.0;\n    g = G/255.0;\n    b = B/255.0;\n\n    if (r > 0.04045)\n        r = Math.pow((r+0.055)/1.055,2.4);\n    else\n        r = r/12.92;\n\n    if (g > 0.04045)\n        g = Math.pow((g+0.055)/1.055,2.4);\n    else\n        g = g/12.92;\n\n    if (b > 0.04045)\n        b = Math.pow((b+0.055)/1.055,2.4);\n    else\n        b = b/12.92 ;\n\n    r*=100;\n    g*=100;\n    b*=100;\n\n    X =  0.4124*r + 0.3576*g + 0.1805*b;\n    Y =  0.2126*r + 0.7152*g + 0.0722*b;\n    Z =  0.0193*r + 0.1192*g + 0.9505*b;\n\n\n    // --------- XYZ to Lab --------- //\n\n    xr = X/Xr;\n    yr = Y/Yr;\n    zr = Z/Zr;\n\n    if ( xr > 0.008856 )\n        xr =  (float) Math.pow(xr, 1/3.);\n    else\n        xr = (float) ((7.787 * xr) + 16 / 116.0);\n\n    if ( yr > 0.008856 )\n        yr =  (float) Math.pow(yr, 1/3.);\n    else\n        yr = (float) ((7.787 * yr) + 16 / 116.0);\n\n    if ( zr > 0.008856 )\n        zr =  (float) Math.pow(zr, 1/3.);\n    else\n        zr = (float) ((7.787 * zr) + 16 / 116.0);\n\n\n    double[] lab = new double[3];\n\n    lab[0] = (116*yr)-16;\n    lab[1] = 500*(xr-yr); \n    lab[2] = 200*(yr-zr); \n\n    return lab;\n\n} \n
Run Code Online (Sandbox Code Playgroud)\n\n

对于上面的代码,我使用此处提供的公式从 rgb 转换为 XYZ,然后从 XYZ 转换为 CIELab。我用这个在线转换器得到的结果是一样的。

\n