圖形圖像處理-之-誤差擴散 中篇
[email protected] 2008.04.22
(2010.01.05 文章由2篇變成3篇,對誤差擴散的速度和品質作進一步探讨!
代碼也有一些更新,容納到我的圖像處理建議架構内,并提供源代碼下載下傳!
測試環境也有了變動;由AMD64x2 4200+(2.37G)DDR2 677(雙通道) 更新為i7-920 DDR3 1333(三通道) )
(2008.12.01 修正一處bug,顔色誤差多累加了一次; 該錯誤由QueQuan發現,表示感謝! )
tag: 誤差擴散,真彩色到高彩色轉換,色階,減色,半色調
摘要: 在圖像的顔色轉換過程中,由于顔色值域的不同,轉換過程中可能會産生誤差;
誤差擴散算法通過将誤差傳遞到周圍像素而減輕其造成的視覺誤差。
上篇:簡單實作; 中篇:簡單的速度優化; 下篇: 更快的速度或更好的效果.
(測試源代碼下載下傳: https://github.com/sisong/demoForHssBlog )
正文:
代碼使用C++,編譯器:VC2005
測試平台:(CPU:i7-920(3.44G); 記憶體:DDR3 1333(三通道); 編譯器:VC2005)
(請先參看文章的上篇)
E: 将誤差擴散的浮點實作改寫為一個整數的定點數實作:
struct TErrorColor{
long dR;
long dG;
long dB;
}; inline long getBestRGB16_555Color( const long wantColor){
const long rMax = ( 1 << 5 ) - 1 ;
if (wantColor <= 0 )
return 0 ;
else if (wantColor >= (rMax << 3 ))
return rMax;
else
return wantColor >> 3 ;
}
inline long getC8Color( const long rColor){
return rColor*(255*(1<<16)/((1<<5)-1)) >>16; // rColor*255/((1<<5)-1);
}
void CvsPic32To16_ErrorDiffuse_Line_1(UInt16 * pDst, const Color32 * pSrc, long width,TErrorColor * PHLineErr){
TErrorColor HErr;
HErr.dR = 0 ; HErr.dG = 0 ; HErr.dB = 0 ;
PHLineErr[ - 1 ].dB = 0 ; PHLineErr[ - 1 ].dG = 0 ; PHLineErr[ - 1 ].dR = 0 ;
for ( long x = 0 ;x < width; ++ x)
{
long cB = (pSrc[x].b + HErr.dB*2 + PHLineErr[x].dB );
long cG = (pSrc[x].g + HErr.dG*2 + PHLineErr[x].dG );
long cR = (pSrc[x].r + HErr.dR*2 + PHLineErr[x].dR );
long rB = getBestRGB16_555Color(cB);
long rG = getBestRGB16_555Color(cG);
long rR = getBestRGB16_555Color(cR);
pDst[x] = rB | (rG << 5 ) | (rR << 10 ); HErr.dB = (cB - getC8Color(rB)) >> 2 ;
HErr.dG = (cG - getC8Color(rG)) >> 2 ;
HErr.dR = (cR - getC8Color(rR)) >> 2 ; PHLineErr[x - 1 ].dB += HErr.dB;
PHLineErr[x - 1 ].dG += HErr.dG;
PHLineErr[x - 1 ].dR += HErr.dR; PHLineErr[x] = HErr;
}
} void CvsPic32To16_ErrorDiffuse_1( const TPicRegion_RGB16_555 & dst, const TPixels32Ref & src){
UInt16 * pDst = (UInt16 * )dst.pdata;
const Color32 * pSrc = src.pdata;
const long width = src.width; TErrorColor * _HLineErr = new TErrorColor[width + 2 ];
for ( long x = 0 ;x < width + 2 ; ++ x){
_HLineErr[x].dR = 0 ;
_HLineErr[x].dG = 0 ;
_HLineErr[x].dB = 0 ;
}
TErrorColor * HLineErr =& _HLineErr[ 1 ]; for ( long y = 0 ;y < src.height; ++ y){
CvsPic32To16_ErrorDiffuse_Line_1(pDst,pSrc,width,HLineErr);
(UInt8 *& )pDst += dst.byte_width;
(UInt8 *& )pSrc += src.byte_width;
} delete[]_HLineErr;
}
速度測試:
//
//CvsPic32To16_ErrorDiffuse_1 283.77 FPS
//
F:繼續優化
getBestRGB16_555Color函數有條件分支,建立一個查找表來代替,完整的實作如下:
static UInt8 _BestRGB16_555Color_Table[ 256 * 5 ];
const UInt8 * BestRGB16_555Color_Table =& _BestRGB16_555Color_Table[ 256 * 2 ];
struct _TAutoInit_BestRGB16_555Color_Table{
_TAutoInit_BestRGB16_555Color_Table(){
for ( long i = 0 ;i < 256 * 5 ; ++ i){
_BestRGB16_555Color_Table[i] = getBestRGB16_555Color(i - 256 * 2 );
}
}
};
static _TAutoInit_BestRGB16_555Color_Table _AutoInit_BestRGB16_555Color_Table;
//實際代碼中建議預先生成_BestRGB16_555Color_Table的資料,進而避免初始化順序依賴的問題 void CvsPic32To16_ErrorDiffuse_Line_2(UInt16 * pDst, const Color32 * pSrc, long width,TErrorColor * PHLineErr){
TErrorColor HErr;
HErr.dR = 0 ; HErr.dG = 0 ; HErr.dB = 0 ;
PHLineErr[ - 1 ].dB = 0 ; PHLineErr[ - 1 ].dG = 0 ; PHLineErr[ - 1 ].dR = 0 ;
for ( long x = 0 ;x < width; ++ x)
{
long cB = (pSrc[x].b + HErr.dB*2 + PHLineErr[x].dB );
long cG = (pSrc[x].g + HErr.dG*2 + PHLineErr[x].dG );
long cR = (pSrc[x].r + HErr.dR*2 + PHLineErr[x].dR );
long rB = BestRGB16_555Color_Table[cB];
long rG = BestRGB16_555Color_Table[cG];
long rR = BestRGB16_555Color_Table[cR];
pDst[x] = rB | (rG << 5 ) | (rR << 10 ); //做乘法比較慢的cpu體系下可以嘗試把getC8Color也做成一個數組表
HErr.dB = (cB - getC8Color(rB)) >> 2 ;
HErr.dG = (cG - getC8Color(rG)) >> 2 ;
HErr.dR = (cR - getC8Color(rR)) >> 2 ; PHLineErr[x - 1 ].dB += HErr.dB;
PHLineErr[x - 1 ].dG += HErr.dG;
PHLineErr[x - 1 ].dR += HErr.dR; PHLineErr[x] = HErr;
}
}
void CvsPic32To16_ErrorDiffuse_2( const TPicRegion_RGB16_555 & dst, const TPixels32Ref & src){
UInt16 * pDst = (UInt16 * )dst.pdata;
const Color32 * pSrc = src.pdata;
const long width = src.width; TErrorColor * _HLineErr = new TErrorColor[width + 2 ];
for ( long x = 0 ;x < width + 2 ; ++ x){
_HLineErr[x].dR = 0 ;
_HLineErr[x].dG = 0 ;
_HLineErr[x].dB = 0 ;
}
TErrorColor * HLineErr =& _HLineErr[ 1 ]; for ( long y = 0 ;y < src.height; ++ y){
CvsPic32To16_ErrorDiffuse_Line_2(pDst,pSrc,width,HLineErr);
(UInt8 *& )pDst += dst.byte_width;
(UInt8 *& )pSrc += src.byte_width;
} delete[]_HLineErr;
}
速度測試:
//
//CvsPic32To16_ErrorDiffuse_2 316.62 FPS
//
函數效果:

(文章的 下篇 将讨論更快的速度或者更好的效果)