圖形圖像處理-之-彩色轉化到灰度的速度優化
[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對指令集的支持情況動態的調用最優的實現函數版本;