圖形圖像處理-之-彩色轉化到灰度的速度優化

               圖形圖像處理-之-彩色轉化到灰度的速度優化
                   [email protected]  2009.02.08

tag:灰度算法,速度優化,定點數優化,MMX,SSE,SSE2,CPU緩存優化

摘要:
  彩色轉化到灰度的速度優化文章包括圖形圖像處理簡單Demo框架和灰度轉換的實
現及其速度優化,並演示其使用SIMD指令集的優化;
   本篇文章將第一次提供完整的可以編譯的圖像處理完整項目代碼;
   (以後會用這個框架逐步改寫以前的圖形圖像處理文章)

正文:  
  爲了便於討論,這裏只處理32bit的ARGB顏色;代碼使用C++;使用的編譯器爲vc2008;
(經過測試代碼也可以在DevC++和xcode下編譯通過) 測試使用的CPU爲AMD64x2 4200+(2.33G);

速度測試說明:
  只測試內存數據到內存數據的ARGB32顏色的灰度轉化;
  測試圖片是800*600; fps表示每秒鐘的幀數,值越大表示函數越快;

A: 圖形圖像處理簡單Demo框架
   
  我以前寫的圖形圖像處理方面的blog文章都沒有完整的可以編譯運行的代碼,
而僅僅列出了關鍵的核心代碼;經常有網友看了我的文章,但因爲不能實際運行看看,
從而對代碼的理解不深,也不能把代碼移植到自己的項目中使用; 所以決定爲我的圖形
圖像處理系列blog文章建立一個簡單的小型的框架;我把它命名爲hGraphic32,
它會盡量的小,演示爲主,僅支持ARGB32顏色,能夠加載和保存bmp圖片文件,能夠在
多個編譯器和平臺下編譯和運行;
   現在就下載完整項目源代碼吧:  完整項目源代碼


  <hGraphic32>文件夾裏的文件說明:
    "hColor32.h"  : 裏面定義了32bitARGB顏色類型Color32,它佔用4字節,代表一個顏色;
        TPixels32Ref是圖像數據區的描述信息,可以把它理解爲一個"指針",指向了Color32構成的像素區;
        IPixels32Buf是圖像數據區接口,用於描述一個圖像的緩衝區;
    "hPixels32.h" : 裏面定義了TPixels32類,它實現了IPixels32Buf接口,用於申請和管理一塊內存像素;
    "hStream.h"   : 裏面定義了IInputStream輸入流接口;
        IBufInputStream數據區輸入流接口,繼承自IInputStream;
        TFileInputStream文件輸入流類,它實現了IBufInputStream接口;
        IOutputStream輸出流接口;
        TFileOutputStream文件輸出流類,它實現了IOutputStream接口;
     "hBmpFile.h" : 裏面定義了TBmpFile類,它負責加載bmp和保存bmp;
     "hGraphic32.h" 文件include了上面的*.h頭文件,所以使用的時候,只要#include "hGraphic32.h"就可以了

B: 灰度轉化項目
  所有的轉換和測試代碼都在"ColorToGray/ColorToGray.cpp"文件中(帶有main函數的命令行程序);
  "ColorToGray/win_vc/ColorToGray.sln"是windows系統下的vc2008項目文件(測試的時請設定調試運行目錄爲"..");
  "ColorToGray/win_DevC++/ColorToGray.dev"是windows系統下的DevC++項目文件;
  "ColorToGray/macosx_xcode/ColorToGray.xcodeproj"是macosx系統下的xcode項目文件;
  你也可以自己建立項目,包含ColorToGray.cpp文件和<hGraphic32>文件夾下的所有文件,就可以編譯了;

C: 灰度轉化公式和代碼實現
  文章中用的灰度公式: Gray = R*0.299 + G*0.587  + B*0.114;
 
代碼實現:


////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_float           145.49 FPS
////////////////////////////////////////////////////////////////////////////////

D: 將浮點運算轉化爲定點數(整數)運算
    


////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_int16           355.33 FPS
////////////////////////////////////////////////////////////////////////////////

E: 做一個簡單的循環代碼展開


////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_int16_expand4   413.22 FPS
////////////////////////////////////////////////////////////////////////////////

F: 一個特別的版本
   在高級語言範圍內進行單條指令多數據流計算,減少需要的乘法量;
在乘法運算代價比較高昂的cpu上應該效果不錯; (x86上速度可能慢)

////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_int8_opMul      387.97 FPS
////////////////////////////////////////////////////////////////////////////////

G: 內聯彙編的MMX實現版本
   注意:這裏的MMX代碼都只支持x86CPU(奔騰MMX以上CPU);
   在x64下不再有MMX寄存器,而應該使用SEE的XMM寄存器;
   而且在x64模式下vc2008編譯器還沒有提供內聯彙編的直接支持,而必須使用函數指令方式的實現;
   GCC編譯器也支持內聯彙編模式,但是彙編語法不同,請參考相應的說明;



////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_MMX             590.84 FPS
////////////////////////////////////////////////////////////////////////////////

H: 優化寫緩衝的內聯彙編的MMX實現版本
  該版本相應於上面的MMX版本只改寫了兩句:
   一是寫內存的movq [edx+ecx*4],mm0 改成了 movntq [edx+ecx*4],mm0 繞過緩存
   二是函數結束的時候調用sfence刷新寫入
  完整代碼如下:




////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_MMX2            679.50 FPS
////////////////////////////////////////////////////////////////////////////////

I: 使用MMX函數指令方式的實現
  MMX/SSE等特殊指令除了內聯彙編來使用外,也可以使用函數指令方式的實現,從而在多種
編譯器下都可以使用SIMD相關指令,可移植性也會好很多;
  但現在看來,vc對此的優化還不夠,還可能遇到編譯器的實現bug;
  (可以考慮使用intel的編譯器編譯這些代碼,感覺優化能力很不錯)



////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_MMX_mmh         508.69 FPS
////////////////////////////////////////////////////////////////////////////////


 優化寫緩衝的使用MMX函數指令方式的實現



////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_MMX2_mmh        540.78 FPS
////////////////////////////////////////////////////////////////////////////////

J:把測試成績放在一起:

////////////////////////////////////////////////////////////////////////////////
//CPU: AMD64x2 4200+(2.33G)  800*600 to 800*600
//==============================================================================
// colorToGray_float           145.49 FPS
// colorToGray_int16           355.33 FPS
// colorToGray_int16_expand4   413.22 FPS
// colorToGray_int8_opMul      387.97 FPS
// colorToGray_MMX             590.84 FPS
// colorToGray_MMX2            679.50 FPS
// colorToGray_MMX_mmh         508.69 FPS
// colorToGray_MMX2_mmh        540.78 FPS
////////////////////////////////////////////////////////////////////////////////

ps:用SSE的浮點指令的版本/用SSE2整數指令的版本/利用SSE3的水平加指令等的實現版本有機會時再補充
ps:SIMD特殊指令集的使用框架請參見我的<YUV視頻格式到RGB32格式轉換的速度優化 中篇>一文,從而
根據CPU對指令集的支持情況動態的調用最優的實現函數版本;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章