顏色空間系列1: RGB和CIEXYZ顏色空間的轉換及相關優化


      顏色空間系列代碼下載鏈接:http://files.cnblogs.com/Imageshop/ImageInfo.rar (同文章同步更新)

      在顏色感知的研究中,CIE 1931 XYZ 色彩空間(也叫做 CIE 1931 色彩空間)是其中一個最先采用數學方式來定義的色彩空間,它由國際照明委員會(CIE)於1931年創立。CIE XYZ 色彩空間是從 1920 年代后期 W. David Wright (Wright 1928) 和 John Guild (Guild 1931) 做的一系列實驗中得出的。他們的實驗結果合並到了 CIE RGB 色彩空間的規定中,CIE XYZ 色彩空間再從它得出。

       更過具體的關於XYZ空間的理論解釋可見:點擊打開鏈接

      本文的重點是如何優化這個RGB<->XYZ相互轉換的過程。

      從相關的文獻包括OpenCv的文檔中可找到兩者的理論轉換算式如下:

      [X]        [0.412453    0.357580    0.180423]    [R]

      [Y]    =  [0.212671    0.715160    0.072169]    [G]       (1)

      [Z]        [0.019334    0.119193    0.950227]    [B]

      [R]        [3.240479   -1.537150   -0.498535]  [X]

      [G]    =  [-0.969256  1.875992   0.041556]    [Y]        (2)

      [B]         [0.055648   -0.204043  1.057311]    [Z]

        仔細觀察式(1),其中 X = 0.412453 * R +  0.412453 *G+  0.412453B ;  各系數相加之和為0.950456,非常接近於1,我們知道R/G/B的取值范圍為[ 0,255 ],如果系數和等於1,則X的取值范圍也必然在[ 0,255 ]之間,因此我們可以考慮等比修改各系數,使其之和等於1,這樣就做到了XYZ和RGB在同等范圍的映射,因此第一行的系數應分別修改為 [0.412453    0.357580    0.180423]  / [0.950456] = [0.433953    0.376219    0.189828]。

       式(1)的第二行,三個系數之和恰為1,因此無需修正。

       式(1)的第三行,三個系數之和為1.088754,修正算式為  [0.019334    0.119193    0.950227]  /  [1.088754] = [0.017758    0.109477    0.872765]

       由於式(1)的變化,式(2)必須做相應的調整,考慮式(1)關於X的各分量都除以了 0.950456,因此,只需在式2的對應分量上乘以  0.950456即可,同理,關於Z的各分量由於都除以了1.088754,式(2)各分量必須對應乘以1.088754。得到最終的變換式(3)(4)。

      [X]        [0.433953    0.376219    0.189828]    [R]

      [Y]    =  [0.212671    0.715160    0.072169]    [G]       (3)

      [Z]        [0.017758    0.109477    0.872765]    [B]

      [R]        [3.0799327   -1.537150   -0.542782]  [X]

      [G]    =  [-0.921235   1.875992    0.0452442]  [Y]      (4)

      [B]         [0.0528909  -0.204043  1.1511515]   [Z]

        如果有朋友查閱過OpenCv的RGB到LAB空間的轉換,就可以發現Cv就是用的上述矩陣先將RGB轉到XYZ,再由XYZ轉為LAB的。

        由以上數式可以看出RGB和XYZ顏色空間的轉換時線性的,因此,兩個系數矩陣之間的成績必為一個E矩陣(對角線為1,其他元素都為0),讀者可以用matlab測試下。

        由於各小數的存在,理論上說,RGB顏色空間的顏色對應的XYZ分量的數值一般都為浮點數,之前說過經過調整系數矩陣后其有效范圍在[0,255]之間,這和RGB的范圍是一致的,因此我們更感興趣的可能是用整數表示XYZ的值,此時,如果先用上述計算式計算,最后在用(int) 強制取整,則效率很低下,因此,很有必要做點的優化。

        優化的原理基本就是用整數的乘除法來替代浮點運算,比如,對各系數乘以一個很大的數,計算出結果在整除這個數,則得到的數字和之前的浮點算式取整結果是一致的。

        如何取放大系數,也有着一定的講究,比如0.433953 ,很多朋友的第一反應應該是乘以1000000得到433953 ,不錯,這是個很好的優化,卻不是最好的,因為最后的整除1000000相對來說也是個慢的過程,如果我們能夠整除一個2的N次冪數,則可以用整數的移位來代替整除。眾所周知,移位的速度非常快。

        那這個N如何取呢,比方說取1可行嗎,分析下馬上得到的結果是絕對不行,因為很多系數乘以2再取整就變為0了。我對這個N的取值建議是在保證整個算式的每個部分的計算結果不超過int(對於64位CPU,則是long類型)類型的最大范圍時,N越大越好。像我們這種情況,由於RGB的取值范圍是[255],因此N的取值最大只能是23。

        假定我們取N的值為20,則RGB轉XYZ的算式可以寫為如下:

X = (Blue * 199049 + Green * 394494 + Red * 455033 + 524288) >> 20;          // 這些系數是按照RGBLAB類里的labXr_32f放大2^20后得到的  
Y = (Blue * 75675 + Green * 749900 + Red * 223002 + 524288) >> 20;  
Z = (Blue * 915161 + Green * 114795 + Red * 18621 + 524288) >> 20;            //  這里無需驗證結果是否在[0,255]之間,必然在。     

       注意算式中的524288,這個值等於(2^20)/2,加上他的作用是使整個算式能夠做到四舍五入。

       另外,還要注意各系數小數點后數字的累積,那X一行來說事,0.433953  * 2^20  =  455032.700928,我們取值455033 ,0.376219    * 2^20= 394494.214144 ,則取值394494 ,那么最后一個系數其實可以不用計算,直接拿 2^20-455033 -394494 =199049 。

       對應的XYZ轉RGB空間算式為:

Blue = (X * 55460 - Y * 213955 + Z * 1207070) >> 20;  
Green = (X * -965985 + Y * 1967119 + Z * 47442) >> 20;    // x * -965985 和 -x * 965985 在反匯編后是不一樣的,后者多了個neg指令  
Red = (X * 3229543 - Y * 1611819 - Z * 569148) >> 20;  
if (Red > 255) Red = 255; else if (Red < 0) Red = 0;      // 這里需要判斷,因為RGB空間所有的顏色轉換到XYZ后,並不是填充滿了0-255的范圍的,反轉過去就會存在一些溢出的點。
if (Green > 255) Green = 255; else if (Green < 0) Green = 0;  // 編譯后比三目運算符的效率高  
if (Blue > 255) Blue = 255; else if (Blue < 0) Blue = 0;  
 

        正如代碼中的注釋一樣,XYZ-RGB的轉換必須判斷轉換的顏色是否在有效范圍內。

        另外對上述算式提一點點優化方面的是事情:

Green = (X * -965985 + Y * 1967119 + Z * 47442) >> 20;       // x * -965985 和 -x * 965985 在反匯編后是不一樣的,后者多了個neg指令  
00000048  imul        ebx,edi,0FFF1429Fh   
0000004e  imul        eax,dword ptr [ebp-10h],1E040Fh   
00000055  add         ebx,eax   
00000057  imul        eax,dword ptr [ebp-14h],0B952h   
0000005e  add         ebx,eax   
00000060  sar         ebx,14h   
       另外一種寫法:
Green = (-X * 965985 + Y * 1967119 + Z * 47442) >> 20;       // x * -965985 和 -x * 965985 在反匯編后是不一樣的,后者多了個neg指令  
00000048  mov         ebx,edi   
0000004a  neg         ebx   
0000004c  imul        ebx,ebx,0EBD61h   
00000052  imul        eax,dword ptr [ebp-10h],1E040Fh   
00000059  add         ebx,eax   
0000005b  imul        eax,dword ptr [ebp-14h],0B952h   
00000062  add         ebx,eax   
00000064  sar         ebx,14h   
      可以看到多了一句neg語句,雖然這個語句基本對效率沒啥影響,但是作為一個速度控,我對這些還是很感興趣的。
      下面給出一些轉換后的視覺圖:
      原始圖像:
 
       
 
          XYZ綜合圖像:
 
      
       
      X通道圖像:
 
     
 
      Y通道圖像:
 
     
 
       Z通道圖像:
 
       
     
     最后說一句,由於上述取整操作的執行,實際上是執行了一個有損的過程,因此,即使不做任何對XYZ的改變,對一副圖片進行多次轉換,就可以看出圖像慢慢的變得不同了,   如下圖所示為轉換10次左后的結果:
 
       
 
  可以看到臉部有明顯的斑紋,因此,如果要進行高精度的計算,那還是請按照公式(3)(4)一步一步來吧。
 
 
'*********************************************************************

  轉載請保留以下信息:

  作者: laviewpbt

  時間:2013.1.31 7點於辦公室

  QQ:33184777

  E-Mail : laviewpbt@sina.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM