HouSisong@GMail.com
2008.04.22
tag: 误差扩散,真彩色到高彩色转换
摘要: 在图像的颜色转换过程中,由于颜色值域的不同,转换过程中可能会产生误差;
误差扩散算法通过将误差传递到周围像素而减轻其造成的视觉误差。
正文:
代码使用C++,编译器:VC2005
测试平台:(CPU:AMD64x2 4200+(2.37G); 内存:DDR2 677(双通道); 编译器:VC2005)
(请先参看文章的上篇)
E: 将误差扩散的浮点实现改写为一个整数的定点数实现:
tag: 误差扩散,真彩色到高彩色转换
摘要: 在图像的颜色转换过程中,由于颜色值域的不同,转换过程中可能会产生误差;
误差扩散算法通过将误差传递到周围像素而减轻其造成的视觉误差。
正文:
代码使用C++,编译器:VC2005
测试平台:(CPU:AMD64x2 4200+(2.37G); 内存:DDR2 677(双通道); 编译器: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*(((1<<16)*255+30)/31) >>16 ;
}
void CvsPic32To16_ErrorDiffuse_Line_1(TUInt16* pDst,const TARGB32* 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+PHLineErr[x-1 ].dB);
long cG=(pSrc[x].g+HErr.dG*2+PHLineErr[x].dG+PHLineErr[x-1 ].dG);
long cR=(pSrc[x].r+HErr.dR*2+PHLineErr[x].dR+PHLineErr[x-1 ].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 TPicRegion& src){
TUInt16* pDst=(TUInt16* )dst.pdata;
const TARGB32* 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);
(TUInt8*&)pDst+= dst.byte_width;
(TUInt8*&)pSrc+= src.byte_width;
}
delete[]_HLineErr;
}
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*(((1<<16)*255+30)/31) >>16 ;
}
void CvsPic32To16_ErrorDiffuse_Line_1(TUInt16* pDst,const TARGB32* 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+PHLineErr[x-1 ].dB);
long cG=(pSrc[x].g+HErr.dG*2+PHLineErr[x].dG+PHLineErr[x-1 ].dG);
long cR=(pSrc[x].r+HErr.dR*2+PHLineErr[x].dR+PHLineErr[x-1 ].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 TPicRegion& src){
TUInt16* pDst=(TUInt16* )dst.pdata;
const TARGB32* 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);
(TUInt8*&)pDst+= dst.byte_width;
(TUInt8*&)pSrc+= src.byte_width;
}
delete[]_HLineErr;
}
速度测试:
//
//CvsPic32To16_ErrorDiffuse_1 154.4 FPS
//
F:继续优化
getBestRGB16_555Color函数有条件分支,建立一个查找表来代替,完整的实现如下:
(CvsPic32To16_ErrorDiffuse_Line_2和CvsPic32To16_ErrorDiffuse_Line_1完成的功能一致)
static TUInt8 _BestRGB16_555Color_Table[256*5
];
const TUInt8* 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;
void CvsPic32To16_ErrorDiffuse_Line_2(TUInt16* pDst,const TARGB32* 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+PHLineErr[x-1 ].dB);
long cG=(pSrc[x].g+HErr.dG*2+PHLineErr[x].dG+PHLineErr[x-1 ].dG);
long cR=(pSrc[x].r+HErr.dR*2+PHLineErr[x].dR+PHLineErr[x-1 ].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 );
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 TPicRegion& src){
TUInt16* pDst=(TUInt16* )dst.pdata;
const TARGB32* 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);
(TUInt8*&)pDst+= dst.byte_width;
(TUInt8*&)pSrc+= src.byte_width;
}
delete[]_HLineErr;
}
const TUInt8* 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;
void CvsPic32To16_ErrorDiffuse_Line_2(TUInt16* pDst,const TARGB32* 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+PHLineErr[x-1 ].dB);
long cG=(pSrc[x].g+HErr.dG*2+PHLineErr[x].dG+PHLineErr[x-1 ].dG);
long cR=(pSrc[x].r+HErr.dR*2+PHLineErr[x].dR+PHLineErr[x-1 ].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 );
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 TPicRegion& src){
TUInt16* pDst=(TUInt16* )dst.pdata;
const TARGB32* 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);
(TUInt8*&)pDst+= dst.byte_width;
(TUInt8*&)pSrc+= src.byte_width;
}
delete[]_HLineErr;
}
速度测试:
//
//CvsPic32To16_ErrorDiffuse_2 166.8 FPS
//
函数效果: