Question

Comment présenter objet couleur dans le modèle de couleur CIE Lab.

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

Mais je ne pouvais pas forcer ce travail avec CIE Lab (en dépit du fait que TYPE_Lab est présenté en classe ColorSpace)

Thx pour l'attention.

Était-ce utile?

La solution

Voici mon implémentation:

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;

}

Autres conseils

J'ai eu quelques problèmes en utilisant le code dans la réponse de @ finw. Je crois qu'ils étaient la plupart du temps en raison du fait que, pour effectuer une conversion CIELab vous devez spécifier un illuminant:

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

L'une des normes populaires est D50 , qui est fondamentalement juste un jour standard. Parce que le code @ finw ne pas la correction pour l'éclairage, les couleurs qui sont censés être gris neutre sortent légèrement teinté. Une façon de vérifier est d'essayer:

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

Vous devriez obtenir à peu près le même nombre sur les trois canaux, mais vous vous retrouvez avec un profil RVB qui est sensiblement (quoique légèrement) bleu. Je suis sûr qu'il est possible de corriger cela dans le code @ finw, mais après un peu de jouer avec et autour de la recherche, j'ai trouvé un excellent code de conversion ici:

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

Pour être complet, il est ici.

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

Dans mes tests, il produit des valeurs de gris qui sont de façon appropriée sans chroma, et il est beaucoup plus rapide pour démarrer.

Il y a un TYPE_Lab, mais pas CS_Lab correspondant. Vous aurez besoin d'étendre ColorSpace et remplacer les méthodes abstraites pour convertir entre XYZ, RVB et Lab. Les conversions nécessaires sont disponibles sur espace colorimétrique Lab (Wikipedia) .

CIELAB semble être pris en charge que par nom dans la bibliothèque Java actuelle - si vous regardez la source de java.awt.color.Colorspace, vous verrez que seule une poignée des espaces de couleurs nommées sont pris en charge

J'ai utilisé ce code et cela a fonctionné:

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;

} 

Pour le code ci-dessus j'ai utilisé les formules fournies pour convertir les rgb à XYZ, puis de XYZ à CIELab. Les résultats que je reçois sont les mêmes ce convertisseur en ligne.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top