lab颜色空间

  RGB颜色空间相比,Lab是一种不常用的色彩空间。它是在1931年国际照明委员会(CIE)制定的颜色度量国际标准的基础上建立起来的。1976年,经修改后被正式命名为CIELab。它是一种设备无关的颜色系统,也是一种基于生理特征的颜色系统。这也就意味着,它是用数字化的方法来描述人的视觉感应。Lab颜色空间中的L分量用于表示像素的亮度,取值范围是[0,100],表示从纯黑到纯白;a表示从红色到绿色的范围,取值范围是[127,-128]b表示从黄色到蓝色的范围,取值范围是[127,-128]。下图所示为Lab颜色空间的图示;


    需要提醒的是,Lab颜色空间比计算机显示器、打印机甚至比人类视觉的色域都要大,表示为 Lab 的位图比 RGB 或 CMYK 位图获得同样的精度要求更多的每像素数据。虽然我们在生活中使用RGB颜色空间更多一些,但也并非Lab颜色空间真的一无所有。例如,在 Adobe Photoshop图像处理软件中,TIFF格式文件中,PDF文档中,都可以见到Lab颜色空间的身影。而在计算机视觉中,尤其是颜色识别相关的算法设计中,rgb,hsv,lab颜色空间混用更是常用的方法。


1.原理

RGB无法直接转换成LAB,需要先转换成XYZ再转换成LAB,即:RGB——XYZ——LAB

因此转换公式分两部分:

(1)RGB转XYZ

假设r,g,b为像素三个通道,取值范围均为[0,255],转换公式如下:

    (1)     

gamma矫正函数:

 

    (2)

 

    (3)

 

M=

0.4124,0.3576,0.1805

0.2126,0.7152,0.0722

0.0193,0.1192,0.9505

等同于如下公式:

X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505

上面的gamma函数,是用来对图象进行非线性色调编辑的,目的是提高图像对比度。这个函数不是唯一的,但是我在网上查到的基本都使用上式。

 

(2)XYZ转LAB

    (5)

 

    (6)

上面两个公式中,L*,a*,b*是最终的LAB色彩空间三个通道的值。X,Y,Z是RGB转XYZ后计算出来的值,Xn,Yn,Zn一般默认是95.047,100.0,108.883。

 

2.代码实现

 (1)完全按照算法无优化实现(经@竹子小蜻蜓指正,原来代码有误,现已改正--206.04.12)

复制代码
inline float gamma(float x)
{return x>0.04045?pow((x+0.055f)/1.055f,2.4f):x/12.92;};

void RGBToLab(unsigned char*rgbImg,float*labImg)
{
    float B=gamma(rgbImg[0]/255.0f);
    float G=gamma(rgbImg[1]/255.0f);
    float R=gamma(rgbImg[2]/255.0f);
    float X=0.412453*R+0.357580*G+0.180423*B;
    float Y=0.212671*R+0.715160*G+0.072169*B;
    float Z=0.019334*R+0.119193*G+0.950227*B;

  float X/=0.95047;
  float Y/=1.0;
  float Z/=1.08883;

    float FX = X > 0.008856f ? pow(X,1.0f/3.0f) : (7.787f * X +0.137931f);
    float FY = Y > 0.008856f ? pow(Y,1.0f/3.0f) : (7.787f * Y +0.137931f);
    float FZ = Z > 0.008856f ? pow(Z,1.0f/3.0f) : (7.787f * Z +0.137931f);
    labImg[0] = Y > 0.008856f ? (116.0f * FY - 16.0f) : (903.3f * Y);
    labImg[1] = 500.f * (FX - FY);
    labImg[2] = 200.f * (FY - FZ);
}
复制代码

上面是完全按照转换算法做的无优化的实现,里面涉及了大量的浮点运算,在PC上可能没什么问题,但是如果是在Android操作系统的移动端上,即使利用JNI,把转换算法写成C++版本进行加速,速度也不理想,因为这个操作时逐像素的,每个像素做几十次浮点运算,耗时还是十分巨大的。

 (2)牺牲一些精度的快速实现

首先可以把gamma函数去掉,因为经过我的测试,这个函数带来的影响很小。

其次,M矩阵中的值都为小数,可以通过放大变成整数,接下来的计算就可以变成整数计算(在手机上浮点计算一般耗时是整形计算的几倍)。在这里,可以把M矩阵的值都扩大2^20倍变为整数,在计算得到X,Y,Z分量时再缩小回来。但是,XYZ的小数部分会消失,会引入误差,而在公式(5)(6)中(尤其是(6),涉及到指数运算和阈值运算,之前XYZ的误差在这里会被放大),这种误差还会继续传递给最终的L,a,b值。那怎么才能保证一定的准确率呢?

在这里我的方法是引入了一个查表机制。过程如下:

A.在计算XYZ的时候,参数扩大2^20倍,但最后缩小时只缩小2^18,这样计算出来的XYZ值范围是[0,1023]

B.建立一个table,用于将取值为[0,1023]的XYZ通过f(t)函数映射到中间结果,记为:LabTable(m)

C.将LabTable(m)代入公式(5)计算最终的Lab分量值

代码如下:

复制代码
const static int big_shift=18;
const static int HalfShiftValue=512;
const static int shift=10;
const static int offset=128<<shift;
const static int ScaleLC = (16 * (1 << shift));
const static int ScaleLT = 116;
const static int ScaleY= 903;
const static int para1=500;
const static int para2=200;
const static int ThPara=9;

void RGBToLab(unsigned char*rgbImg,int*labImg)
{
    long long X=(rgbImg[0] * 199049 + rgbImg[1] * 394494 + rgbImg[2] * 455033 + 524288)>> (big_shift);
    long long Y=(rgbImg[0] * 75675 + rgbImg[1] * 749900 + rgbImg[2] * 223002 + 524288) >> (big_shift);
    long long Z=(rgbImg[0] * 915161 + rgbImg[1] * 114795 + rgbImg[2] * 18621 + 524288) >> (big_shift);

    labImg[0] = Y>ThPara?((ScaleLT * LabTable[Y] - ScaleLC + HalfShiftValue)>>shift):((ScaleY* Y)>>shift);
    labImg[1] = (para1*(LabTable[X] - LabTable[Y])+HalfShiftValue+offset)>>shift;
    labImg[2] = (para2*(LabTable[Y] - LabTable[Z])+HalfShiftValue+offset)>>shift;
}
复制代码

建立LabTable的代码如下:

复制代码
int LabTable[1024];
for (int I = 0; I < 1024; I++)
{
    if (I > 9)
       LabTable[I] = (int)(pow((float)I / 1020, 1.0F / 3) * (1 << 10) + 0.5 );
    else
       LabTable[I] = (int)((29 * 29.0 * I / (6 * 6 * 3 * 1020) + 4.0 / 29) * (1 << 10) + 0.5 );
    printf("%d,",LabTable[I]);
}
参考:http://blog.csdn.net/carson2005/article/details/7200440

http://www.cnblogs.com/hrlnw/p/4126017.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值