Pregunta

¿Cómo puedo objeto presente del color en el modelo de color CIE Lab.

Color c = ...
float[] lab = {0,0,0};
...
c.getColorComponents(ColorSpace.getInstance(???), lab);

Pero yo no era capaz de forzar este trabajo con CIE Lab (a pesar del hecho de que TYPE_Lab se presenta en clase ColorSpace)

Thx por la atención.

¿Fue útil?

Solución

Aquí está mi aplicación:

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;

}

Otros consejos

He tenido algunos problemas utilizando el código en la respuesta de @ finw. Creo que eran sobre todo debido al hecho de que hay que hacer una conversión CIELab se debe especificar un iluminante:

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

Uno de los estándares populares es D50 , que es básicamente una luz estándar. Dado que el código de @ finw no tiene la corrección para la iluminación, los colores que se supone que son de color gris neutro salir ligeramente teñido. Una forma de comprobar esto es intentar:

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

Se debe conseguir aproximadamente el mismo número en los tres canales, pero que terminan con un perfil RGB que es notablemente (aunque ligeramente) azul. Estoy seguro de que es posible corregir esto en el código de @ finw, pero después de un poco de jugar con él y buscar alrededor, me encontré con un excelente código de conversión aquí:

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

Para completar, aquí está.

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

En mis pruebas, que produce valores de grises que son apropiadamente sin croma, y ??es mucho más rápido para arrancar.

Hay una TYPE_Lab, pero sin CS_Lab correspondiente. Va a necesitar extender ColorSpace y anular los métodos abstractos para convertir entre XYZ, RGB y Lab. Las conversiones necesarias se pueden encontrar en Lab espacio de color (Wikipedia) .

CIELAB parece estar apoyada solamente por su nombre en la biblioteca actual de Java - si nos fijamos en la fuente de java.awt.color.Colorspace, verá que sólo un puñado de los espacios de color son compatibles con nombre

utiliza este código y funcionó:

public double[] rgbToLab(int R, int G, int B) {

    double r, g, b, X, Y, Z, xr, yr, zr;

    // D65/2°
    double Xr = 95.047;  
    double Yr = 100.0;
    double Zr = 108.883;


    // --------- RGB to XYZ ---------//

    r = R/255.0;
    g = G/255.0;
    b = B/255.0;

    if (r > 0.04045)
        r = Math.pow((r+0.055)/1.055,2.4);
    else
        r = r/12.92;

    if (g > 0.04045)
        g = Math.pow((g+0.055)/1.055,2.4);
    else
        g = g/12.92;

    if (b > 0.04045)
        b = Math.pow((b+0.055)/1.055,2.4);
    else
        b = b/12.92 ;

    r*=100;
    g*=100;
    b*=100;

    X =  0.4124*r + 0.3576*g + 0.1805*b;
    Y =  0.2126*r + 0.7152*g + 0.0722*b;
    Z =  0.0193*r + 0.1192*g + 0.9505*b;


    // --------- XYZ to Lab --------- //

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

    if ( xr > 0.008856 )
        xr =  (float) Math.pow(xr, 1/3.);
    else
        xr = (float) ((7.787 * xr) + 16 / 116.0);

    if ( yr > 0.008856 )
        yr =  (float) Math.pow(yr, 1/3.);
    else
        yr = (float) ((7.787 * yr) + 16 / 116.0);

    if ( zr > 0.008856 )
        zr =  (float) Math.pow(zr, 1/3.);
    else
        zr = (float) ((7.787 * zr) + 16 / 116.0);


    double[] lab = new double[3];

    lab[0] = (116*yr)-16;
    lab[1] = 500*(xr-yr); 
    lab[2] = 200*(yr-zr); 

    return lab;

} 

En el código anterior I utiliza las fórmulas proporcionadas aquí el fin de convertir desde RGB a XYZ y después de XYZ a CIELab. Los resultados que obtengo son los mismos con este convertidor en línea .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top