Java:如何将RGB颜色转换为CIE Lab
2022-09-02 23:26:09
如何在CIE实验室颜色模型中呈现对象颜色。
Color c = ...
float[] lab = {0,0,0};
...
c.getColorComponents(ColorSpace.getInstance(???), lab);
但是我无法与CIE实验室一起强制进行这项工作(尽管TYPE_Lab在ColorSpace类中呈现)
谢谢你的注意力。
如何在CIE实验室颜色模型中呈现对象颜色。
Color c = ...
float[] lab = {0,0,0};
...
c.getColorComponents(ColorSpace.getInstance(???), lab);
但是我无法与CIE实验室一起强制进行这项工作(尽管TYPE_Lab在ColorSpace类中呈现)
谢谢你的注意力。
这是我的实现:
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;
}
我在@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);
您应该在所有三个通道上获得大致相同的数字,但最终得到的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);
}
在我的测试中,它生成的灰度值适当无色度,并且启动速度要快得多。