图形图像处理-之-误差扩散 上篇

  [email protected]    2008.04.22

tag: 误差扩散,真彩色到高彩色转换
  
摘要: 在图像的颜色转换过程中,由于颜色值域的不同,转换过程中可能会产生误差;
误差扩散算法通过将误差传递到周围像素而减轻其造成的视觉误差。
       
正文:
  代码使用C++,编译器:VC2005
  测试平台:(CPU:AMD64x2 4200+(2.37G); 内存:DDR2 677(双通道); 编译器:VC2005)
  
A:程序将把一张真彩色图片转换成高彩色图片作为例子,颜色和图片的数据定义:

typedef unsigned  char   TUInt8;  //  [0..255]
typedef unsigned  short  TUInt16;
typedef unsigned 
long   TUInt32;


struct  TARGB32       // 32 bit color
{
    TUInt8  b,g,r,a;          
//  a is alpha
};

struct  TPicRegion   // 一块颜色数据区的描述,便于参数传递
{
    TARGB32
*         pdata;         // 颜色数据首地址
     long             byte_width;    // 一行数据的物理宽度(字节宽度);注意: abs(byte_width)有可能大于等于width*sizeof(TARGB32);
    unsigned  long    width;         // 像素宽度
    unsigned  long    height;        // 像素高度
};

// 那么访问一个点的函数可以写为:
inline TARGB32 &  Pixels( const  TPicRegion &  pic, const   long  x, const   long  y)
{
    
return  ( (TARGB32 * )((TUInt8 * )pic.pdata + pic.byte_width * y) )[x];
}

//高彩色颜色和图片数据定义 (

struct  TRGB16_555   // 16bit 5:5:5 high color
{
   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:真彩色图片直接转换成高彩色图片的简单实现

inline TRGB16_555 ToColor16( const  TARGB32 &  color){
    TRGB16_555 result;
    result.r
= color.r >> 3 ;
    result.g
= color.g >> 3 ;
    result.b
= color.b >> 3 ;
    
return  result;
}

void  CvsPic32To16_0( const  TPicRegion_RGB16_555 &  dst, const  TPicRegion &  src){
    
for  ( long  y = 0 ;y < src.height; ++ y){
        
for  ( long  x = 0 ;x < src.width; ++ x){
            Pixels(dst,x,y)
= ToColor16(Pixels(src,x,y));
        }
    }
}

来看一下函数效果

   源图片(800x600):
图形图像处理-之-误差扩散 上篇_第1张图片 
   转换后图片:
图形图像处理-之-误差扩散 上篇_第2张图片 
可以看到,颜色位数的降低,很多区域都产生了失真的色块

速度测试:
//////////////////////////////////////////////////////////////
//CvsPic32To16_0                  204.5  FPS 
//////////////////////////////////////////////////////////////


C:对直接转换函数的简单速度优化(功能一样)

inline TUInt16 ToColor16_1( const  TARGB32 &  color){
    
return  ((color.r >> 3 ) << 10 ) | ((color.g >> 3 ) << 5 ) | (color.b >> 3 );
}

void  CvsPic32To16_1( const  TPicRegion_RGB16_555 &  dst, const  TPicRegion &  src){
    TUInt16
*  pDst = (TUInt16 * )dst.pdata;
    
const  TARGB32 *  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]);
        }
        (TUInt8
*& )pDst += dst.byte_width;
        (TUInt8
*& )pSrc += src.byte_width;
    }
}

速度测试:
//////////////////////////////////////////////////////////////
//CvsPic32To16_1                  507.9  FPS 
//////////////////////////////////////////////////////////////
  (当然,该函数还可以继续优化的,比如使用MMX、SSE等指令,可以得到更快的速度;)

D:误差扩散的颜色转换函数实现
   转换过程中,将产生的转换误差,按一定的系数向右和向下传递(这样写代码比较容易); 
我使用的误差传递系数为:
  * 2
1 1 0    /4

其他一些常见的误差传递模板(也可以自己设定合适的模板系数系数),可以尝试一下其转换效果
  
  * 3
0 3 2   /8
  
  * 7
3 5 1   /16
  
    * 8 4
2 4 8 4 2
1 2 4 2 1  /42

我使用了一个较为简单的模板,为质量、速度、额外空间占用做了折中;
简单的实现:

     struct  TErrorColor_0{
        
float  dR;
        
float  dG;
        
float  dB;
    };

    inline 
long  getBestRGB16_555Color_0( const   float  wantColor){
        
float  result = wantColor * ( 31.0 / 255 );
        
if  (result <= 0
            
return   0 ;
        
else   if  (result >= 31 )
            
return   31 ;
        
else
            
return  ( long )result;
    }

    
void  CvsPic32To16_ErrorDiffuse_Line_0(TUInt16 *  pDst, const  TARGB32 *  pSrc, long  width,TErrorColor_0 *  PHLineErr){
        TErrorColor_0 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 + PHLineErr[x - 1 ].dB);
            
float  cG = (pSrc[x].g + HErr.dG * 2 + PHLineErr[x].dG + PHLineErr[x - 1 ].dG);
            
float  cR = (pSrc[x].r + HErr.dR * 2 + PHLineErr[x].dR + PHLineErr[x - 1 ].dR);
            
// rB,rG,rR为转换后的颜色(也就是实际显示颜色)
             long  rB = getBestRGB16_555Color_0(cB);
            
long  rG = getBestRGB16_555Color_0(cG);
            
long  rR = getBestRGB16_555Color_0(cR);
            pDst[x]
=  rB | (rG << 5 ) | (rR << 10 );
            
// 计算两个颜色之间的差异的1/4
            HErr.dB = (cB - (rB * ( 255.0 / 31 ))) * ( 1.0 / 4 );
            HErr.dG
= (cG - (rG * ( 255.0 / 31 ))) * ( 1.0 / 4 );
            HErr.dR
= (cR - (rR * ( 255.0 / 31 ))) * ( 1.0 / 4 );

            PHLineErr[x
- 1 ].dB += HErr.dB;
            PHLineErr[x
- 1 ].dG += HErr.dG;
            PHLineErr[x
- 1 ].dR += HErr.dR;

            PHLineErr[x]
= HErr;
        }
    }

void  CvsPic32To16_ErrorDiffuse_0( const  TPicRegion_RGB16_555 &  dst, const  TPicRegion &  src){
    TUInt16
*  pDst = (TUInt16 * )dst.pdata;
    
const  TARGB32 *  pSrc = src.pdata;
    
const   long  width = src.width;

    TErrorColor_0
*  _HLineErr = new  TErrorColor_0[width + 2 ]; 
    
for  ( long  x = 0 ;x < width + 2 ; ++ x){
        _HLineErr[x].dR
= 0 ;
        _HLineErr[x].dG
= 0 ;
        _HLineErr[x].dB
= 0 ;
    }
    TErrorColor_0
*  HLineErr =& _HLineErr[ 1 ];

    
for  ( long  y = 0 ;y < src.height; ++ y){
        CvsPic32To16_ErrorDiffuse_Line_0(pDst,pSrc,width,HLineErr);
        (TUInt8
*& )pDst += dst.byte_width;
        (TUInt8
*& )pSrc += src.byte_width;
    }

    delete[]_HLineErr;
}

函数效果:
图形图像处理-之-误差扩散 上篇_第3张图片 

  和上面的直接转换效果对比,色深一样但质量明显好了很多:)

(可以放大该图片来看看,对颜色误差的传递会有一个更好的认识)

速度测试:
//////////////////////////////////////////////////////////////
//CvsPic32To16_ErrorDiffuse_0      33.6  FPS
//////////////////////////////////////////////////////////////

你可能感兴趣的:(图形图像处理-之-误差扩散 上篇)