Java: comment convertir la couleur RGB en CIE Lab
Comment puis-je présenter la couleur de l'objet dans le modèle de couleur de la CIE Lab.
Color c = ...
float[] lab = {0,0,0};
...
c.getColorComponents(ColorSpace.getInstance(???), lab);
mais je n'ai pas pu forcer ce travail avec CIE Lab (malgré le fait que TYPE_Lab est présenté en classe ColorSpace)
Thx pour l'attention.
5 réponses
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;
}
j'ai eu quelques problèmes en utilisant le code dans la réponse de @finw. Je pense qu'elles étaient principalement dues au fait que pour faire une conversion CIELab vous devez spécifier un illuminant:
http://en.wikipedia.org/wiki/Standard_illuminant
l'Un des formats standard est D50, qui est essentiellement juste un jour standard. Parce que le code de @finw n'a pas la correction pour l'illumination, les couleurs qui sont supposées être du gris neutre sortent légèrement teinté. Une façon de vérifier cela 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 (bien que légèrement) bleu. Je suis sûr qu'il est possible de corriger cela dans le code de @finw, mais après un peu de jouer avec et de chercher autour, j'ai trouvé un code de conversion excellent ici:
http://www.f4.fhtw-berlin.de/~barthel/ImageJ/ColorInspector//HTMLHelp/farbraumJava.htm
pour être complet, Le voici.
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 grises qui sont correctement sans chroma, et il est beaucoup plus rapide à démarrer.
il y a un TYPE_Lab
, mais pas de correspondance CS_Lab
. Vous aurez besoin de prolonger ColorSpace
et outrepasser les méthodes abstraites pour convertir entre XYZ, RGB, et Lab. Les conversions nécessaires peuvent être trouvées à espace couleur Lab (Wikipedia).
CIELAB semble être supporté uniquement par le nom dans la bibliothèque Java actuelle-si vous regardez la source de java.awt.couleur.Colorspace, vous verrez que seulement une poignée des espaces de couleur nommés sont supportés.
j'ai utilisé ce code et il 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 ici afin de passer de rgb À XYZ puis De XYZ à CIELab. Les résultats que j'obtiens sont les mêmes avec convertisseur en ligne.