一、RGB与Lab的区别
RGB的是由红色通道(R)、绿色通道(G)、蓝色通道(B)组成的,最亮的红色+最亮的绿色+最亮的蓝色=白色;最暗的红色+最暗的绿色+最暗的蓝色=黑色;而在最亮和最暗之间,相同明暗度的红色+相同明暗度的绿色+相同明暗度的蓝色=灰色。在RGB的任意一个通道内,白和黑表示这个颜色的明暗度。所以,有白色或者灰白色的地方,R、G、B三个通道都不可能是黑色的,因为必须要有R、G、B三个通道来构成这些颜色。
而LAB不一样,LAB中的明度通道(L)专门负责整张图的明暗度,简单的说就是整幅图的黑白版。a通道和b通道只负责颜色的多少。a通道表示从洋红色(通道里的白色)至深绿色(通道里的黑色)的范围;b表示从焦黄色(通道里的白色)至袅蓝色(通道里的黑色)的范围;a、b通道里的50%中性灰色表示没有颜色,所以越接近灰色说明颜色越少,而且a通道和b通道的颜色没有亮度。这就说明了为什么在a、b通道中红色T恤的轮廓是那么的清晰!因为红色是洋红色+焦黄色组成的。
总的来说:
- 适合RGB通道抠的图大部分LAB模式能完成,反之不成立。
- 任何单一色调背景下,用通道抠有明显颜色区别的部分,用LAB模式很快能完成
- LAB模式下对明度(L)通道做任何操作(如锐化、模糊等)不会影响到色相。
二、RGB转Lab颜色空间
RGB颜色空间不能直接转换为Lab颜色空间,需要借助XYZ颜色空间,把RGB颜色空间转换到XYZ颜色空间,之后再把XYZ颜色空间转换到Lab颜色空间。
仔细观察式(1),其中 X = 0.412453 * R + 0.357580 * G+ 0.180423 * B ; 各系数相加之和为0.950456,非常接近于1,我们知道R/G/B的取值范围为[ 0,255 ],如果系数和等于1,则X的取值范围也必然在[ 0,255 ]之间,因此我们可以考虑等比修改各系数,使其之和等于1,这样就做到了XYZ和RGB在同等范围的映射。这也就是为什么代码里X,Y,Z会分别除以0.950456、1.0、1.088754。
RGB2Lab关键代码实现:
//RGB2Lab Lab2RGB
const float param_13 = 1.0f / 3.0f;
const float param_16116 = 16.0f / 116.0f;
const float Xn = 0.950456f;
const float Yn = 1.0f;
const float Zn = 1.088754f;
float gamma(float x)
{
return x>0.04045?powf((x+0.055f)/1.055f,2.4f):(x/12.92);
};
void RGB2XYZ(T_U8 R, T_U8 G, T_U8 B, float *X, float *Y, float *Z)
{
float RR = gamma(R/255.0);
float GG = gamma(G/255.0);
float BB = gamma(B/255.0);
*X = 0.4124564f * RR + 0.3575761f * GG + 0.1804375f * BB;
*Y = 0.2126729f * RR + 0.7151522f * GG + 0.0721750f * BB;
*Z = 0.0193339f * RR + 0.1191920f * GG + 0.9503041f * BB;
}
void XYZ2Lab(float X, float Y, float Z, float *L, float *a, float *b)
{
float fX, fY, fZ;
X /= (Xn);
Y /= (Yn);
Z /= (Zn);
if (Y > 0.008856f)
fY = pow(Y, param_13);
else
fY = 7.787f * Y + param_16116;
if (X > 0.008856f)
fX = pow(X, param_13);
else
fX = 7.787f * X + param_16116;
if (Z > 0.008856)
fZ = pow(Z, param_13);
else
fZ = 7.787f * Z + param_16116;
*L = 116.0f * fY - 16.0f;
*L = *L > 0.0f ? *L : 0.0f;
*a = 500.0f * (fX - fY);
*b = 200.0f * (fY - fZ);
}
int RGB2Lab(IMAGE_TYPE *bmp_img,float *lab_img)
{
DWORD width,height,index;
WORD biBitCount;
T_U8 *dst,*bmp,R,G,B;
float X,Y,Z,L,a,b;
T_U32 line_byte;
T_U16 i,j;
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
memset(&bf, 0, sizeof(bf));
memset(&bi, 0, sizeof(bi));
bmp = bmp_img;
memcpy(&bf,bmp,14);
memcpy(&bi,&bmp[14],40);
height = bi.biHeight;
width = bi.biWidth;
biBitCount = bi.biBitCount;//每一个像素由24 bits表示,即RGB分量每一个分量用8 bits表示
line_byte = WIDTHBYTES(width*bi.biBitCount);
dst = bmp_img+BMPHEADSIZE;
for (i = 0; i <height;i++)
{
for (j = 0;j < width;j++)
{
index = i*line_byte+3*j;
B = dst[index];
G = dst[index+1];
R = dst[index+2];
RGB2XYZ(R,G,B,&X,&Y,&Z);
XYZ2Lab(X,Y,Z,&L,&a,&b);
lab_img[index] = L;
lab_img[index+1] = a;
lab_img[index+2] = b;
}
}
return 0;
}
三、Lab转RGB颜色空间
Lab2RGB关键代码实现:
extern const float param_13;
extern const float param_16116;
extern const float Xn;
extern const float Yn;
extern const float Zn;
float gamma_XYZ2RGB(float x)
{
return x>0.0031308?(1.055f*powf(x,(1/2.4f))-0.055):(x*12.92);
};
void XYZ2RGB(float X, float Y, float Z, unsigned char*R, unsigned char*G, unsigned char*B)
{
float RR , GG, BB ;
RR = 3.2404542f * X - 1.5371385f * Y - 0.4985314f * Z;
GG = -0.9692660f * X + 1.8760108f * Y + 0.0415560f * Z;
BB = 0.0556434f * X - 0.2040259f * Y + 1.0572252f * Z;
RR = gamma_XYZ2RGB(RR);
GG = gamma_XYZ2RGB(GG);
BB = gamma_XYZ2RGB(BB);
RR = CLIP255(RR*255.0+0.5);
GG = CLIP255(GG*255.0+0.5);
BB = CLIP255(BB*255.0+0.5);
*R = (unsigned char)RR;
*G = (unsigned char)GG;
*B = (unsigned char)BB;
}
void Lab2XYZ(float L, float a, float b, float *X, float *Y, float *Z)
{
float fX, fY, fZ;
fY = (L + 16.0f) / 116.0;
fX = a / 500.0f + fY;
fZ = fY - b / 200.0f;
if(powf(fY,3.0)>0.008856)
*Y =powf(fY,3.0);
else
*Y = (fY-param_16116)/7.787f;
if (powf(fX,3) > 0.008856)
*X = fX * fX * fX;
else
*X = (fX - param_16116) / 7.787f;
if (powf(fZ,3.0) > 0.008856)
*Z = fZ * fZ * fZ;
else
*Z = (fZ - param_16116) / 7.787f;
(*X) *= (Xn);
(*Y) *= (Yn);
(*Z) *= (Zn);
}
int Lab2RGB(IMAGE_TYPE *bmp_img,float *lab_img)
{
DWORD width,height,index;
WORD biBitCount;
T_U8 *bmp,R,G,B,*Lab2BMP;
float X,Y,Z,L,a,b;
T_U32 line_byte;
T_U16 i,j;
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
FILE *Lab2BMP_fp = fopen("Lab2BMP.bmp","wb");
if(NULL == Lab2BMP_fp)
{
printf("Can't open Lab2BMP.bmp\n");
return -1;
}
memset(&bf, 0, sizeof(bf));
memset(&bi, 0, sizeof(bi));
bmp = bmp_img;
memcpy(&bf,bmp,14);
memcpy(&bi,&bmp[14],40);
height = bi.biHeight;
width = bi.biWidth;
biBitCount = bi.biBitCount;//每一个像素由24 bits表示,即RGB分量每一个分量用8 bits表示
line_byte = WIDTHBYTES(width*bi.biBitCount);
fwrite(&bf,sizeof(BITMAPFILEHEADER),1,Lab2BMP_fp);
fwrite(&bi,sizeof(BITMAPINFOHEADER),1,Lab2BMP_fp);
Lab2BMP = (T_U8*)malloc(height*line_byte);
if (Lab2BMP == NULL)
{
printf("Can't malloc LabBMP image.\n");
return 0;
}
memset(Lab2BMP,0,height*line_byte);
for (i = 0; i <height;i++)
{
for (j = 0;j < width;j++)
{
index = i*line_byte+3*j;
L = lab_img[index];
a = lab_img[index+1];
b = lab_img[index+2];
Lab2XYZ(L,a,b,&X,&Y,&Z);
XYZ2RGB(X,Y,Z,&R,&G,&B);
Lab2BMP[index] = B;
Lab2BMP[index+1] = G;
Lab2BMP[index+2] = R;
}
}
fwrite(Lab2BMP, line_byte*height, 1, Lab2BMP_fp);
fclose(Lab2BMP_fp);
free(Lab2BMP);
return 0;
}
四、效果图
左侧图像是原始图像,右侧图像经过RGB->XYZ->LAB->XYZ->RGB的转换结果图。