Step1:
XYZ to RGB (采用 D65 白点):
[ R ] [ 3.240479 -1.537150 -0.498535 ] [ X ]
[ G ] = [ -0.969256 1.875992 0.041556 ] * [ Y ]
[ B ] [ 0.055648 -0.204043 1.057311 ] [ Z ].
R, G, B 在 [0,1].
逆变换:
[ X ] [ 0.412453 0.357580 0.180423 ] [ R ]
[ Y ] = [ 0.212671 0.715160 0.072169 ] * [ G ]
[ Z ] [ 0.019334 0.119193 0.950227 ] [ B ]
Step2:
XYZ to CIE L*a*b* (CIELAB) & CIELAB to XYZ
L* = 116 * (Y/Yn)1/3 - 16 若 Y/Yn > 0.008856
L* = 903.3 * Y/Yn 其他
a* = 500 * ( f(X/Xn) - f(Y/Yn) )
b* = 200 * ( f(Y/Yn) - f(Z/Zn) )
其中 f(t) = t1/3 若 t > 0.008856
f(t) = 7.787 * t + 16/116 其他
其中Xn, Yn 和 Zn是参考白的三刺激值。
逆变换( Y/Yn > 0.008856) :
X = Xn * ( P + a* / 500 ) 3
Y = Yn * P 3
Z = Zn * ( P - b* / 200 ) 3
其中 P = (L* + 16) / 116
====================================================================
这是我的代码:按照CIE走的,出来的结果和大家常用的PhotoShop的是不一样的;
不过有一种算法的计算结果很逼近photoshop的,再整理出来吧;
double BLACK = 20;
double YELLOW = 70;
void RGB2Lab(double R, double G, double B, double &L, double &a, double &b)
{
double X, Y, Z, fX, fY, fZ;
X = 0.412453*R + 0.357580*G + 0.180423*B;
Y = 0.212671*R + 0.715160*G + 0.072169*B;
Z = 0.019334*R + 0.119193*G + 0.950227*B;
X /= (255 * 0.950456);
Y /= 255;
Z /= (255 * 1.088754);
if (Y > 0.008856)
{
fY = pow(Y, 1.0/3.0);
L = 116.0*fY - 16.0;
}
else
{
fY = 7.787*Y + 16.0/116.0;
L = 903.3*Y;
}
if (X > 0.008856)
fX = pow(X, 1.0/3.0);
else
fX = 7.787*X + 16.0/116.0;
if (Z > 0.008856)
fZ = pow(Z, 1.0/3.0);
else
fZ = 7.787*Z + 16.0/116.0;
a = 500.0*(fX - fY);
b = 200.0*(fY - fZ);
if (L < BLACK)
{
a *= exp((L - BLACK) / (BLACK / 4));
b *= exp((L - BLACK) / (BLACK / 4));
L = BLACK;
}
if (b > YELLOW)
b = YELLOW;
}
void Lab2RGB(double L, double a, double b, double &R, double &G, double &B)
{
double X, Y, Z, fX, fY, fZ;
double RR, GG, BB;
fY = pow((L + 16.0) / 116.0, 3.0);
if (fY < 0.008856)
fY = L / 903.3;
Y = fY;
if (fY > 0.008856)
fY = pow(fY, 1.0/3.0);
else
fY = 7.787 * fY + 16.0/116.0;
fX = a / 500.0 + fY;
if (fX > 0.206893)
X = pow(fX, 3.0);
else
X = (fX - 16.0/116.0) / 7.787;
fZ = fY - b /200.0;
if (fZ > 0.206893)
Z = pow(fZ, 3.0);
else
Z = (fZ - 16.0/116.0) / 7.787;
X *= (0.950456 * 255);
Y *= 255;
Z *= (1.088754 * 255);
RR = 3.240479*X - 1.537150*Y - 0.498535*Z;
GG = -0.969256*X + 1.875992*Y + 0.041556*Z;
BB = 0.055648*X - 0.204043*Y + 1.057311*Z;
R = (float)(RR < 0 ? 0 : RR > 255 ? 255 : RR);
G = (float)(GG < 0 ? 0 : GG > 255 ? 255 : GG);
B = (float)(BB < 0 ? 0 : BB > 255 ? 255 : BB);
}
======================================================
下面是我找到的一个代码,还没仔细看,是从一个读取Adobe PSD文件的类里面找到的;
原址在这里http://www.codeproject.com/bitmap/MyPSD.asp
我试了一下,与Adobe的还是不一样
void CPSD::LabToRGB(const int L, const int a, const int b, int &R, int &G, int &B )
{
// For the conversion we first convert values to XYZ and then to RGB
// Standards used Observer = 2, Illuminant = D65
// ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883
const double ref_X = 95.047;
const double ref_Y = 100.000;
const double ref_Z = 108.883;
double var_Y = ( (double)L + 16.0 ) / 116.0;
double var_X = (double)a / 500.0 + var_Y;
double var_Z = var_Y - (double)b / 200.0;
if ( pow(var_Y, 3) > 0.008856 )
var_Y = pow(var_Y, 3);
else
var_Y = ( var_Y - 16 / 116 ) / 7.787;
if ( pow(var_X, 3) > 0.008856 )
var_X = pow(var_X, 3);
else
var_X = ( var_X - 16 / 116 ) / 7.787;
if ( pow(var_Z, 3) > 0.008856 )
var_Z = pow(var_Z, 3);
else
var_Z = ( var_Z - 16 / 116 ) / 7.787;
double X = ref_X * var_X;
double Y = ref_Y * var_Y;
double Z = ref_Z * var_Z;
XYZToRGB(X, Y, Z, R, G, B);
};
void CPSD::XYZToRGB(const double X, const double Y, const double Z, int &R, int &G, int &B)
{
// Standards used Observer = 2, Illuminant = D65
// ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883
const double ref_X = 95.047;
const double ref_Y = 100.000;
const double ref_Z = 108.883;
double var_X = X / 100.0;
double var_Y = Y / 100.0;
double var_Z = Z / 100.0;
double var_R = var_X * 3.2406 + var_Y * (-1.5372) + var_Z * (-0.4986);
double var_G = var_X * (-0.9689) + var_Y * 1.8758 + var_Z * 0.0415;
double var_B = var_X * 0.0557 + var_Y * (-0.2040) + var_Z * 1.0570;
if ( var_R > 0.0031308 )
var_R = 1.055 * ( pow(var_R, 1/2.4) ) - 0.055;
else
var_R = 12.92 * var_R;
if ( var_G > 0.0031308 )
var_G = 1.055 * ( pow(var_G, 1/2.4) ) - 0.055;
else
var_G = 12.92 * var_G;
if ( var_B > 0.0031308 )
var_B = 1.055 * ( pow(var_B, 1/2.4) )- 0.055;
else
var_B = 12.92 * var_B;
R = (int)(var_R * 256.0);
G = (int)(var_G * 256.0);
B = (int)(var_B * 256.0);
};