颜色迁移之二——Reinhard经典算法

来源:互联网 发布:windows触摸屏无法使用 编辑:程序博客网 时间:2024/05/22 12:03

接自上一篇“基础知识”,本文里的色彩空间的转换不再赘述。。。        

Reinhard等人根据lαβ颜色空间中各通道互相不关联的特点,提出了一组适用于各颜色分量的色彩迁移公式,较好的实现了彩色图像之间的色彩迁移。基本思想就是根据着色图像的统计分析确定一个线性变换,使得目标图像和源图像在lαβ空间中具有同样的均值和方差。

因此需要计算两幅图像的均值和标准方差。假设l、a、b分别是源图像lαβ通道原有的数据,L、A、B分别是变换后得到新的源图像lαβ通道的值,ml、ma、mb和ml’、ma’、mb’分别是源图像和着色图像的三个颜色通道的均值,nl、na、nb和nl’、na’、nb’表示它们的标准方差。

首先,将源图像原有的数据减掉源图像的均值

L = l – ml

A = a – ma

B = b – mb

再将得到的新数据按比例放缩,其放缩系数是两幅图像标准方差的比值

L’ = (nl’ / nl)* L

A’ = (na’ / na)* A

B’ = (nb’ / nb)* B

将得到的l’、a’、b’分别加上目标图像三个通道的均值,得到最终数据

L = L’ + ml’

A = A’ + ma’

B = B’ + mb’

整理后得到目标图像与源图像之间的像素关系表达

L = (nl’ / nl)* (l – ml) + ml’

A = (na’ / na)* (a – ma) + ma’

B = (nb’ / nb)* (b – mb) + mb’

事实上这组式子表示的就是一个线性方程,以两幅图像的标准方差的比值作为斜率,两幅图像各个通道的均值作为一个点。这个简单的线性变换保证了目标图像和着色图像在lαβ颜色空间中具有相同的均值和方差。将变换后l、α、β的值作为新图像三个通道的值,然后显示的时候再将这三个通道转换为RGB值进行显示。

                                 

                                                                                           图 reinhard算法效果图

Reinhard 等人的色彩迁移算法的优点是实现简单,且运行效率很高。但该算法由于是整体色彩迁移,因此它对全局颜色基调单一的图像的有着良好的迁移效果。而对于颜色内容丰富的图像,则效果并不那么明显。一般解决方式是引入人机交互选取样本块的方法,同时还要求用户指定样本块之间的对应关系。这样就给用户增加了许多繁琐的交互。当图像的色彩比较复杂时,用户是无法手工精确地选取样本块的,此时,该算法也将失去作用。

 

BOOL TranRein(LPBYTE lpDIBBits, LONG lmageWidth, LONG lmageHeight,LPBYTE lpDIBBits2, LONG lmageWidth2, LONG lmageHeight2,LPBYTE lpDIBBits3){int i;int j;int nindex;double al,aa,ab,vl,va,vb,al2,aa2,ab2,vl2,va2,vb2;double* lpImageLab = new  double[lmageWidth*lmageHeight*3];double* lpImageLab2 = new  double[lmageWidth2*lmageHeight2*3];double* lpImageLab3 = new  double[lmageWidth*lmageHeight*3];//目标图像转换为lab,并求lab的均值及标准差for(j = 0;j <lmageHeight; j++){for(i = 0; i <lmageWidth; i++){nindex=((lmageHeight-j-1)*lmageWidth+i);RgbToLab(lpDIBBits[nindex*3+2],lpDIBBits[nindex*3+1],lpDIBBits[nindex*3+0],lpImageLab[nindex*3+0],lpImageLab[nindex*3+1],lpImageLab[nindex*3+2]);}}AverageVariance(lpImageLab,lmageWidth,lmageHeight,al,aa,ab,vl,va,vb);//源图像转换为lab,并求lab的均值及标准差for(j = 0;j <lmageHeight2; j++){for(i = 0; i <lmageWidth2; i++){nindex=((lmageHeight2-j-1)*lmageWidth2+i);RgbToLab(lpDIBBits2[nindex*3+2],lpDIBBits2[nindex*3+1],lpDIBBits2[nindex*3+0],lpImageLab2[nindex*3+0],lpImageLab2[nindex*3+1],lpImageLab2[nindex*3+2]);}}AverageVariance(lpImageLab2,lmageWidth2,lmageHeight2,al2,aa2,ab2,vl2,va2,vb2);//求结果图像的labfor(i = 0;i <lmageWidth*lmageHeight; i++){lpImageLab3[i*3+0] = (lpImageLab[i*3+0] - al) * vl2/vl + al2;lpImageLab3[i*3+1] = (lpImageLab[i*3+1] - aa) * va2/va + aa2;lpImageLab3[i*3+2] = (lpImageLab[i*3+2] - ab) * vb2/vb + ab2;}//将结果图像的lab转换为RGBfor(j = 0;j <lmageHeight; j++){for(i = 0; i <lmageWidth; i++){nindex=((lmageHeight-j-1)*lmageWidth+i);LabToRgb(lpImageLab3[nindex*3+0],lpImageLab3[nindex*3+1],lpImageLab3[nindex*3+2],lpDIBBits3[nindex*3+2],lpDIBBits3[nindex*3+1],lpDIBBits3[nindex*3+0]);}}return TRUE;}

void AverageVariance(double* lpLab,int Width,int Height,double& al,double& aa,double& ab,double& vl,double& va,double& vb){double suml=0;double suma=0;double sumb=0;double lsuml=0;double lsuma=0;double lsumb=0;//分行求平均,避免和过大而溢出for(int j=0;j<Height;j++){for(int i=0;i<Width;i++){lsuml+=lpLab[(j*Width+i)*3];lsuma+=lpLab[(j*Width+i)*3+1];lsumb+=lpLab[(j*Width+i)*3+2];}suml += lsuml/Width;suma += lsuma/Width;sumb += lsumb/Width;lsuml=lsuma=lsumb=0;}al = suml/Height;aa = suma/Height;ab = sumb/Height;suml=suma=sumb=0;for(int i=0;i<Width*Height;i++){suml += pow(lpLab[i*3]-al,2);suma += pow(lpLab[i*3+1]-aa,2);sumb += pow(lpLab[i*3+2]-ab,2);}vl = sqrt(suml);va = sqrt(suma);vb = sqrt(sumb);}

PS:其中RgbToLab函数与LabToRgb函数在上一篇中。。。。。。。。。。。。。。

 代码链接

11 0
原创粉丝点击