zoukankan      html  css  js  c++  java
  • RGB转LAB色彩空间

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

    1.原理

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

    因此转换公式分两部分:

    (1)RGB转XYZ

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

        (1)      

        (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://www.easyrgb.com/index.php?X=MATH&H=07#text7

    http://vcsos.com/Article/pageSource/120314/20120314114422.shtml

    http://www.cnblogs.com/Imageshop/archive/2013/01/31/2888097.html

    http://www.cnblogs.com/Imageshop/archive/2013/02/02/2889897.html

  • 相关阅读:
    第二十九课 循环链表的实现
    第二十八课 再论智能指针(下)
    第二十七课 再论智能指针(上)
    第二十六课 典型问题分析(Bugfix)
    普通new和placement new的重载
    leetcode 581. Shortest Unsorted Continuous Subarray
    leetcode 605. Can Place Flowers
    leetcode 219. Contains Duplicate II
    leetcode 283. Move Zeroes
    leetcode 217. Contains Duplicate
  • 原文地址:https://www.cnblogs.com/jukan/p/7844302.html
Copyright © 2011-2022 走看看