RGB颜色空间转LAB,和OpenCV结果不一致原因分析与解决

最近看了RGB转LAB的原理,自己实现了一版。

最初的版本

单个像素的源码

 float X, Y, Z, L, A, B;
 float T[3][3];
 //Shift = 0;
 float Xscale = 0.95046;
 float Zscale = 1.08875;
 T[0][0] =  0.412453 / Xscale; //R
 T[0][1] =  0.357580 / Xscale; //G
 T[0][2] =  0.180423 / Xscale; //B

 T[1][0] =  0.212671;
 T[1][1] =  0.715160;
 T[1][2] =  0.072169;

 T[2][0] =  0.019334 / Zscale;
 T[2][1] =  0.119193 / Zscale;
 T[2][2] =  0.950227 / Zscale;

 X = (Blue * T[0][2] + Green * T[0][1] + Red * T[0][0]);
 Y = (Blue * T[1][2] + Green * T[1][1] + Red * T[1][0]);
 Z = (Blue * T[2][2] + Green * T[2][1] + Red * T[2][0]);

 if (Y / 255.0 > 0.008856f)
 {
    
    
     L = 116 * pow(Y / 255.0, 1.0 / 3) - 16;
 }
 else
 {
    
    
     L = 903.3 * Y / 255.0;
 }

 L = L * 255.0 / 100.0;

 double fX, fY, fZ;

 if (X / 255.0 > 0.008856f)
 {
    
    
     fX = pow(X / 255.0, 1.0 / 3);
 }
 else
 {
    
    
     fX = 7.787 * X / 255.0 + 16.0 / 116.0;
 }

 if (Y / 255.0 > 0.008856f)
 {
    
    
     fY = pow(Y / 255.0, 1.0 / 3);
 }
 else
 {
    
    
     fY = 7.787 * Y / 255.0 + 16.0 / 116.0;
 }

 if (Z / 255.0 > 0.008856f)
 {
    
    
     fZ = pow(Z / 255.0, 1.0 / 3);
 }
 else
 {
    
    
     fZ = 7.787 * Z / 255.0 + 16.0 / 116.0;
 }

 A = 500 * (fX - fY) + 128;
 B = 200 * (fY - fZ) + 128;

根据的原理是OpenCV的文档,
在这里插入图片描述

发现问题

转完后发现和直接调用OpenCV的结果不一样。后来看OpenCV的源码,才发现有gamma校正这一步。在imgproc模块的color_lab.cpp中,

 static const softfloat intScale(255*(1 << gamma_shift));
 for(i = 0; i < 256; i++)
 {
    
    
     softfloat x = softfloat(i)/f255;
     sRGBGammaTab_b[i] = (ushort)(cvRound(intScale*applyGamma(x)));
     linearGammaTab_b[i] = (ushort)(i*(1 << gamma_shift));
 }
const ushort* tab = srgb ? sRGBGammaTab_b : linearGammaTab_b;
for(; i < n; i++, src += scn, dst += 3 )
{
    
    
    int R = tab[src[0]], G = tab[src[1]], B = tab[src[2]];
    int fX = LabCbrtTab_b[CV_DESCALE(R*C0 + G*C1 + B*C2, lab_shift)];
    int fY = LabCbrtTab_b[CV_DESCALE(R*C3 + G*C4 + B*C5, lab_shift)];
    int fZ = LabCbrtTab_b[CV_DESCALE(R*C6 + G*C7 + B*C8, lab_shift)];

    int L = CV_DESCALE( Lscale*fY + Lshift, lab_shift2 );
    int a = CV_DESCALE( 500*(fX - fY) + 128*(1 << lab_shift2), lab_shift2 );
    int b = CV_DESCALE( 200*(fY - fZ) + 128*(1 << lab_shift2), lab_shift2 );

    dst[0] = saturate_cast<uchar>(L);
    dst[1] = saturate_cast<uchar>(a);
    dst[2] = saturate_cast<uchar>(b);
}

问题解决与函数优化

看原理中有很多浮点的运算,每次都要对每个像素进行计算,算法耗时较长。学习OpenCV的源码,可以看到使用了查找表的方式,以空间换时间。所以对函数进行了优化。

查找表的建立

gamma查找表

//gamma校正表建立
static const double gammaThreshold = double(809) / double(20000);    //  0.04045
static const double gammaInvThreshold = double(7827) / double(2500000); //  0.0031308
static const double gammaLowScale = double(323) / double(25);       // 12.92
static const double gammaPower = double(12) / double(5);         //  2.4
static const double gammaXshift = double(11) / double(200);       //  0.055

const int gammaShift = 3;

static inline float applyGamma(float x)
{
    
    
    float xd = x;
    return (xd <= gammaThreshold ?
        xd / gammaLowScale :
        pow((xd + gammaXshift) / (1 + gammaXshift), gammaPower));
}

const float intScale(255 * (1 << 0));
int i;
uint16_t sRGBGammaTab[256];
uint16_t linearGammaTab[256];
ofstream a("1.txt");
for (i = 0; i < 256; i++)
{
    
    
    float x = float(i) / 255.0;
    sRGBGammaTab[i] = (uint16_t)(255 * applyGamma(x) * (1 << gammaShift) + 0.5);
    a << sRGBGammaTab[i] << ",";
    linearGammaTab[i] = (uint16_t)(i * (1 << gammaShift) + 0.5);
}
a << endl;
a.close();

LAB查找表

//LAB查找表建立
const int Threshold = 9;
int* LabTab = new int[1024];
ofstream b("2.txt");
for (int i = 0; i < 1024; i++)
{
    
    
    if (i > Threshold)
    {
    
    
    	LabTab[i] = int(pow((float)i / 1020.0, 1.0f / 3) * (1 << Shift) + 0.5);		}
    else
    {
    
    
    	LabTab[i] = int((7.787 * (float)i / 1020.0 + 16 / 116.0) * (1 << Shift) + 0.5);
    } 
    b << LabTab[i] << ",";
    if (i % 16 == 15)
        b << endl;
}
b << endl;
b.close();

建立的查找表

uint16_t GammaTab[256] =
{
    
    
    0, 1, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10,
    11, 11, 12, 13, 14, 15, 16, 17, 19, 20, 21, 22, 24, 25, 26, 28,
    29, 31, 33, 34, 36, 38, 40, 41, 43, 45, 47, 49, 51, 54, 56, 58,
    60, 63, 65, 68, 70, 73, 75, 78, 81, 83, 86, 89, 92, 95, 98, 101,
    105, 108, 111, 115, 118, 121, 125, 129, 132, 136, 140, 144, 147, 151, 155, 160,
    164, 168, 172, 176, 181, 185, 190, 194, 199, 204, 209, 213, 218, 223, 228, 233,
    239, 244, 249, 255, 260, 265, 271, 277, 282, 288, 294, 300, 306, 312, 318, 324,
    331, 337, 343, 350, 356, 363, 370, 376, 383, 390, 397, 404, 411, 418, 426, 433,
    440, 448, 455, 463, 471, 478, 486, 494, 502, 510, 518, 527, 535, 543, 552, 560,
    569, 578, 586, 595, 604, 613, 622, 631, 641, 650, 659, 669, 678, 688, 698, 707,
    717, 727, 737, 747, 757, 768, 778, 788, 799, 809, 820, 831, 842, 852, 863, 875,
    886, 897, 908, 920, 931, 943, 954, 966, 978, 990, 1002, 1014, 1026, 1038, 1050, 1063,
    1075, 1088, 1101, 1113, 1126, 1139, 1152, 1165, 1178, 1192, 1205, 1218, 1232, 1245, 1259, 1273,
    1287, 1301, 1315, 1329, 1343, 1357, 1372, 1386, 1401, 1415, 1430, 1445, 1460, 1475, 1490, 1505,
    1521, 1536, 1551, 1567, 1583, 1598, 1614, 1630, 1646, 1662, 1678, 1695, 1711, 1728, 1744, 1761,
    1778, 1794, 1811, 1828, 1846, 1863, 1880, 1897, 1915, 1933, 1950, 1968, 1986, 2004, 2022, 2040
};

const int LabTab[1024] =
{
    
    
	36158,38159,40160,42162,44163,46164,48166,50167,52168,54169,56106,57917,59621,61233,62765,64225,
    65622,66961,68249,69490,70689,71848,72971,74060,75118,76147,77149,78126,79079,80009,80918,81808,
    82678,83530,84366,85185,85989,86777,87552,88314,89062,89798,90522,91235,91937,92628,93309,93981,
    94643,95295,95939,96575,97202,97821,98432,99036,99633,100222,100805,101381,101951,102514,103071,103622,
    104168,104707,105242,105771,106294,106813,107326,107835,108339,108838,109333,109823,110309,110791,111268,111742,
    112211,112677,113139,113597,114051,114502,114949,115393,115833,116271,116704,117135,117563,117987,118408,118827,
    119242,119655,120065,120472,120876,121278,121676,122073,122467,122858,123247,123633,124017,124399,124778,125155,
    125530,125902,126272,126640,127006,127370,127732,128092,128450,128806,129160,129511,129862,130210,130556,130900,
    131243,131584,131923,132261,132596,132930,133263,133593,133922,134250,134576,134900,135223,135544,135863,136182,
    136498,136814,137127,137440,137751,138060,138368,138675,138981,139285,139588,139889,140189,140488,140786,141082,
    141377,141671,141964,142255,142546,142835,143123,143410,143695,143980,144263,144546,144827,145107,145386,145664,
    145941,146217,146492,146766,147038,147310,147581,147851,148120,148387,148654,148920,149185,149449,149712,149975,
    150236,150496,150756,151014,151272,151529,151785,152040,152294,152548,152800,153052,153303,153553,153802,154051,
    154298,154545,154791,155037,155281,155525,155768,156010,156252,156492,156732,156972,157210,157448,157685,157922,
    158157,158392,158627,158860,159093,159325,159557,159788,160018,160248,160477,160705,160933,161160,161386,161612,
    161837,162061,162285,162508,162731,162953,163174,163395,163615,163835,164054,164272,164490,164707,164924,165140,
    165356,165571,165785,165999,166213,166426,166638,166850,167061,167271,167482,167691,167900,168109,168317,168524,
    168731,168938,169144,169350,169555,169759,169963,170167,170370,170572,170774,170976,171177,171378,171578,171778,
    171977,172176,172374,172572,172770,172967,173163,173359,173555,173750,173945,174139,174333,174527,174720,174912,
    175105,175296,175488,175679,175869,176059,176249,176438,176627,176816,177004,177192,177379,177566,177752,177939,
    178124,178310,178495,178679,178863,179047,179231,179414,179596,179779,179961,180142,180324,180504,180685,180865,
    181045,181224,181403,181582,181760,181939,182116,182294,182471,182647,182823,182999,183175,183350,183525,183700,
    183874,184048,184222,184395,184568,184741,184913,185085,185257,185428,185599,185770,185940,186110,186280,186450,
    186619,186788,186956,187125,187293,187460,187628,187795,187962,188128,188294,188460,188626,188791,188956,189121,
    189285,189449,189613,189777,189940,190103,190266,190429,190591,190753,190914,191076,191237,191398,191558,191719,
    191879,192038,192198,192357,192516,192675,192833,192991,193149,193307,193464,193622,193779,193935,194092,194248,
    194404,194559,194715,194870,195025,195179,195334,195488,195642,195796,195949,196102,196255,196408,196560,196713,
    196865,197016,197168,197319,197470,197621,197772,197922,198072,198222,198372,198522,198671,198820,198969,199117,
    199266,199414,199562,199709,199857,200004,200151,200298,200445,200591,200737,200883,201029,201175,201320,201465,
    201610,201755,201899,202044,202188,202332,202476,202619,202762,202905,203048,203191,203333,203476,203618,203760,
    203901,204043,204184,204325,204466,204607,204748,204888,205028,205168,205308,205447,205587,205726,205865,206004,
    206142,206281,206419,206557,206695,206833,206970,207108,207245,207382,207518,207655,207791,207928,208064,208200,
    208335,208471,208606,208742,208877,209011,209146,209281,209415,209549,209683,209817,209951,210084,210217,210350,
    210483,210616,210749,210881,211014,211146,211278,211409,211541,211673,211804,211935,212066,212197,212328,212458,
    212588,212719,212849,212978,213108,213238,213367,213496,213625,213754,213883,214012,214140,214268,214397,214525,
    214652,214780,214908,215035,215162,215289,215416,215543,215670,215796,215923,216049,216175,216301,216427,216552,
    216678,216803,216928,217053,217178,217303,217427,217552,217676,217800,217924,218048,218172,218296,218419,218542,
    218666,218789,218912,219034,219157,219279,219402,219524,219646,219768,219890,220012,220133,220255,220376,220497,
    220618,220739,220860,220980,221101,221221,221341,221461,221581,221701,221821,221941,222060,222179,222299,222418,
    222537,222655,222774,222893,223011,223129,223248,223366,223484,223601,223719,223837,223954,224071,224189,224306,
    224423,224539,224656,224773,224889,225005,225122,225238,225354,225470,225585,225701,225817,225932,226047,226162,
    226277,226392,226507,226622,226736,226851,226965,227079,227193,227307,227421,227535,227649,227762,227876,227989,
    228102,228215,228328,228441,228554,228667,228779,228892,229004,229116,229228,229340,229452,229564,229675,229787,
    229898,230010,230121,230232,230343,230454,230565,230675,230786,230897,231007,231117,231227,231337,231447,231557,
    231667,231777,231886,231996,232105,232214,232323,232432,232541,232650,232759,232867,232976,233084,233193,233301,
    233409,233517,233625,233733,233840,233948,234055,234163,234270,234377,234485,234592,234698,234805,234912,235019,
    235125,235232,235338,235444,235550,235656,235762,235868,235974,236080,236185,236291,236396,236501,236607,236712,
    236817,236922,237027,237131,237236,237340,237445,237549,237654,237758,237862,237966,238070,238174,238277,238381,
    238485,238588,238691,238795,238898,239001,239104,239207,239310,239413,239515,239618,239720,239823,239925,240027,
    240129,240231,240333,240435,240537,240639,240740,240842,240943,241045,241146,241247,241348,241449,241550,241651,
    241752,241853,241953,242054,242154,242254,242355,242455,242555,242655,242755,242855,242955,243054,243154,243253,
    243353,243452,243552,243651,243750,243849,243948,244047,244146,244244,244343,244442,244540,244638,244737,244835,
    244933,245031,245129,245227,245325,245423,245521,245618,245716,245813,245911,246008,246105,246202,246299,246396,
    246493,246590,246687,246784,246880,246977,247073,247170,247266,247362,247458,247555,247651,247747,247842,247938,
    248034,248130,248225,248321,248416,248511,248607,248702,248797,248892,248987,249082,249177,249272,249366,249461,
    249556,249650,249745,249839,249933,250027,250122,250216,250310,250404,250497,250591,250685,250779,250872,250966,
    251059,251152,251246,251339,251432,251525,251618,251711,251804,251897,251990,252082,252175,252267,252360,252452,
    252545,252637,252729,252821,252913,253005,253097,253189,253281,253373,253464,253556,253647,253739,253830,253922,
    254013,254104,254195,254286,254377,254468,254559,254650,254741,254831,254922,255013,255103,255194,255284,255374,
    255464,255555,255645,255735,255825,255915,256005,256094,256184,256274,256363,256453,256542,256632,256721,256810,
    256900,256989,257078,257167,257256,257345,257434,257523,257611,257700,257789,257877,257966,258054,258143,258231,
    258319,258407,258495,258583,258671,258759,258847,258935,259023,259111,259198,259286,259373,259461,259548,259636,
    259723,259810,259897,259985,260072,260159,260246,260332,260419,260506,260593,260679,260766,260853,260939,261026,
    261112,261198,261285,261371,261457,261543,261629,261715,261801,261887,261973,262058,262144,262230,262315,262401
};

优化后的函数

const int Shift = 18;
const int ScaleLC = (int)(16 * 2.55 * (1 << Shift) + 0.5);
const int ScaleLT = (int)(116 * 2.55 + 0.5);
const int HalfShiftValue = (1 << (Shift - 1));

void ToLAB(uint8_t* Src, uint8_t* Dst, int Length)
{
    
    
    int X, Y, Z, L, A, B;
    int T[3][3] = {
    
     0 };
    T[0][0] = (1 << Shift) * 0.412453 / 0.950456; //R
    T[0][1] = (1 << Shift) * 0.357580 / 0.950456; //G
    T[0][2] = (1 << Shift) * 0.180423 / 0.950456; //B

    T[1][0] = (1 << Shift) * 0.212671;
    T[1][1] = (1 << Shift) * 0.715160;
    T[1][2] = (1 << Shift) * 0.072169;

    T[2][0] = (1 << Shift) * 0.019334 / 1.088754;
    T[2][1] = (1 << Shift) * 0.119193 / 1.088754;
    T[2][2] = (1 << Shift) * 0.950227 / 1.088754;

    uint8_t Red, Green, Blue;
    uint16_t RR, GG, BB;

    for (int i = 0; i < Length; i++)
    {
    
    
        Red = *(Src + 0);
        Green = *(Src + 1);
        Blue = *(Src + 2);
        RR = GammaTab[Red];
        GG = GammaTab[Green];
        BB = GammaTab[Blue];

        X = (BB * T[0][2] + GG * T[0][1] + RR * T[0][0] + HalfShiftValue) >> (Shift + gammaShift - 2);
        Y = (BB * T[1][2] + GG * T[1][1] + RR * T[1][0] + HalfShiftValue) >> (Shift + gammaShift - 2);
        Z = (BB * T[2][2] + GG * T[2][1] + RR * T[2][0] + HalfShiftValue) >> (Shift + gammaShift - 2);

        X = LabTab[X];
        Y = LabTab[Y];
        Z = LabTab[Z];

        L = (ScaleLT * Y - ScaleLC + HalfShiftValue) >> Shift;
        A = ((500 * (X - Y) + HalfShiftValue) >> Shift) + 128;
        B = ((200 * (Y - Z) + HalfShiftValue) >> Shift) + 128;

        *Dst = (uint8_t)L;
        *(Dst + 1) = (uint8_t)A;
        *(Dst + 2) = (uint8_t)B;

        Src += 3;
        Dst += 3;
    }
}

代码测试

int main()
{
    
    
    Mat imRGB = imread("4.bmp");
    Mat imLAB = imread("4.bmp");
    Mat imLAB2 = imread("4.bmp");

    int nW = imRGB.cols;
    int nH = imRGB.rows;


    double start, stop, duration;
    cvtColor(imRGB, imLAB, COLOR_RGB2Lab);
    start = static_cast<double>(getTickCount());
    for (int i = 0; i < 10; i++)
        cvtColor(imRGB, imLAB, COLOR_RGB2Lab);
    //cvtColor(imRGB, imLAB, COLOR_RGB2XYZ);
    stop = static_cast<double>(getTickCount());
    duration = ((double)(stop - start)) / getTickFrequency();
    cout << "opencv:" << duration << endl;


    ToLAB(imRGB.data, imLAB2.data, nW * nH);    
    start = static_cast<double>(getTickCount());
    for(int i = 0;i<10;i++)
        ToLAB(imRGB.data, imLAB2.data, nW * nH);
    //ToXYZ(imRGB.data, imLAB2.data, nW * nH);
    stop = static_cast<double>(getTickCount());
    duration = ((double)(stop - start)) / getTickFrequency();
    cout << "new:" << duration << endl;

    Mat imLABSub = imLAB - imLAB2;

    cout << imLAB.at<Vec3b>(100, 100) << endl;
    cout << imLAB2.at<Vec3b>(100, 100) << endl;

    imshow("imLAB", imLAB);
    imshow("imLAB2", imLAB2);

    imwrite("3LAB.bmp", imLAB);
    imwrite("3LAB2.bmp", imLAB2);
    imwrite("3LABimLABSub.bmp", imLABSub);
    
    waitKey();

    return 0;
}

转换效果
请添加图片描述
请添加图片描述

猜你喜欢

转载自blog.csdn.net/iamqianrenzhan/article/details/123976479