圖形圖像處理-之-誤差擴散 上篇
[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)
A:程序將把一張真彩色圖片轉換成高彩色圖片作爲例子,顏色和圖片的數據定義:
//高彩色顏色和圖片數據定義 (
{
TUInt16 b: 5 ;
TUInt16 g: 5 ;
TUInt16 r: 5 ;
TUInt16 x: 1 ;
};
struct TPicRegion_RGB16_555 // 一塊顏色數據區的描述,便於參數傳遞
{
TRGB16_555 * pdata; // 顏色數據首地址
long byte_width; // 一行數據的物理寬度(字節寬度)
unsigned long width; // 像素寬度
unsigned long height; // 像素高度
};
inline TRGB16_555 & Pixels( const TPicRegion_RGB16_555 & pic, const long x, const long y)
{
return ( (TRGB16_555 * )((TUInt8 * )pic.pdata + pic.byte_width * y) )[x];
}
例子中使用的16bit高彩色的RGB顏色編碼爲555; 常見的編碼方式還有565和655,某些程序
裏面可能還會使用4:4:4:4 (4比特Alpha通道); (提示:利用宏或泛型的方式可以用一個函數
實現同時支持這些格式)
B:真彩色圖片直接轉換成高彩色圖片的簡單實現
TRGB16_555 result;
result.r = color.r >> 3 ;
result.g = color.g >> 3 ;
result.b = color.b >> 3 ;
return result;
}
for ( long y = 0 ;y < src.height; ++ y){
for ( long x = 0 ;x < src.width; ++ x){
Pixels(dst,x,y) = ToColor16(src.pixels(x,y));
}
}
}
來看一下函數效果
源圖片(800x600):
轉換後圖片:
可以看到,顏色位數的降低,很多區域都產生了失真的色塊
速度測試:
//////////////////////////////////////////////////////////////
//CvsPic32To16_0 386.95 FPS
//////////////////////////////////////////////////////////////
C:對直接轉換函數的簡單速度優化(功能一樣)
inline UInt16 ToColor16_1( const Color32 & color){
return ((color.r >> 3 ) << 10 ) | ((color.g >> 3 ) << 5 ) | (color.b >> 3 );
}
UInt16 * pDst = (UInt16 * )dst.pdata;
const Color32 * pSrc = src.pdata;
const long width = src.width;
for ( long y = 0 ;y < src.height; ++ y){
for ( long x = 0 ;x < width; ++ x){
pDst[x] = ToColor16_1(pSrc[x]);
}
(UInt8 *& )pDst += dst.byte_width;
(UInt8 *& )pSrc += src.byte_width;
}
}
速度測試:
//////////////////////////////////////////////////////////////
//CvsPic32To16_1 1221.00 FPS
//////////////////////////////////////////////////////////////
(當然,該函數還可以繼續優化的,比如使用MMX、SSE等指令,可以得到更快的速度;)
D:誤差擴散的顏色轉換函數實現
轉換過程中,將產生的轉換誤差,按一定的係數向右和向下傳遞(這樣寫代碼比較容易);
我使用的誤差傳遞係數爲:
* 2
1 1 0 /4
(
該模板的解釋:
算法需要整幅圖片按照從左到右,從上到下的順序依次對每個像素運用該模板
*代表當前轉換處理的像素 假設這個點轉換顏色後,顏色誤差爲D
那麼,將這個誤差傳遞到周圍的像素 將D*2/4的顏色量加到右邊的像素 將D*1/4的顏色量加到左下的像素和下面的像素
)
其他一些常見的誤差傳遞模板(也可以自己設定合適的模板係數係數),可以嘗試一下其轉換效果
* 3
0 3 2 /8
* 7
3 5 1 /16
* 8 4
2 4 8 4 2
1 2 4 2 1 /42
我使用了一個較爲簡單的模板,爲質量、速度、額外空間佔用做了折中;
簡單的實現:
//誤差傳遞係數爲:
// * 2
//1 1 0 /4
float dR;
float dG;
float dB;
};
float result = wantColor * ( 31.0f / 255 );
if (result <= 0 )
return 0 ;
else if (result >= 31 )
return 31 ;
else
return ( long )result;
}
TErrorColor_f 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)
{
// cB,cG,cR爲應該顯示的顏色
float cB = (pSrc[x].b + HErr.dB * 2 + PHLineErr[x].dB );
float cG = (pSrc[x].g + HErr.dG * 2 + PHLineErr[x].dG );
float cR = (pSrc[x].r + HErr.dR * 2 + PHLineErr[x].dR );
// rB,rG,rR爲轉換後的顏色(也就是實際顯示顏色)
long rB = getBestRGB16_555Color_f(cB);
long rG = getBestRGB16_555Color_f(cG);
long rR = getBestRGB16_555Color_f(cR);
pDst[x] = rB | (rG << 5 ) | (rR << 10 );
// 計算兩個顏色之間的差異的1/4
HErr.dB = (cB - (rB * ( 255.0f / 31 ))) * ( 1.0f / 4 );
HErr.dG = (cG - (rG * ( 255.0f / 31 ))) * ( 1.0f / 4 );
HErr.dR = (cR - (rR * ( 255.0f / 31 ))) * ( 1.0f / 4 );
PHLineErr[x - 1 ].dG += HErr.dG;
PHLineErr[x - 1 ].dR += HErr.dR;
}
}
UInt16 * pDst = (UInt16 * )dst.pdata;
const Color32 * pSrc = src.pdata;
const long width = src.width;
for ( long x = 0 ;x < width + 2 ; ++ x){
_HLineErr[x].dR = 0 ;
_HLineErr[x].dG = 0 ;
_HLineErr[x].dB = 0 ;
}
TErrorColor_f * HLineErr =& _HLineErr[ 1 ];
CvsPic32To16_ErrorDiffuse_Line_0(pDst,pSrc,width,HLineErr);
(UInt8 *& )pDst += dst.byte_width;
(UInt8 *& )pSrc += src.byte_width;
}
}
函數效果:
和上面的直接轉換效果對比,色深一樣但質量明顯好了很多:)
(可以放大該圖片來看看,對顏色誤差的傳遞會有一個更好的認識)
速度測試:
//////////////////////////////////////////////////////////////
//CvsPic32To16_ErrorDiffuse_0 97.13 FPS
//////////////////////////////////////////////////////////////
(文章的 中篇開始簡單的優化其速度)