Welsh等人在Reinhard等人的彩色图像间色彩迁移算法的基础上,提出了灰度图像彩色化的思想,并提出了相应的算法。该算法主要利用查找匹配像素来实现灰度图像的色彩迁移,因为灰度图像只有亮度信息,所以该算法主要通过像素的亮度值匹配,来实现灰度图像的自动彩色化。
具体步骤如下:
(1)将参考图像和灰度图像分别由RGB空间转换到lαβ色彩空间。
(2)根据灰度图像的亮度及标准差,对参考图像进行亮度重映射。
由于参考图像和灰度图像的直方图取值不一定在同一范围,那么如果将一幅亮度值很低的参考图像与一幅亮度值很高的目标图像的直方图进行对比将会出现较大的误差。因此,需要对参考图像进行亮度重映射:
L = (nl’ / nl)* (l – ml) + ml’
其中,l是源图像l通道的数据,L是变换后得到新的源图像l通道的值,ml和ml’ 分别是源图像和着色图像的l通道的均值,nl和nl’表示它们的l通道标准方差。
(3)从参考图像中随机选取一批样本点,将像素点的亮度和邻域范围内亮度的标准差的线性组合值作为权值,计算公式如下:
W = l/2 + σ/2
其中,w为权值,l为像素点的亮度值,σ为该像素点周围某个邻域内亮度值的标准差。关于邻域的大小,Welsh指出一般取5X5,对个别图像取更大的邻域效果会更好。
(4)按行扫描灰度图像,对于每个像素点,由公式计算其权值,并在参考图像的样本点中找到一个最接近的样本点,即最佳匹配点,将匹配点的仅和口通道的值赋给灰度图像的对应像素点,从而实现色彩的传递。
(5)将参考图像和灰度图像从lαβ空间转换到RGB空间。
图 welsh效果图
Welsh等人的算法能有效地实现灰度图像的着色,但仍存在一些不足之处。首先,最佳匹配样本点的查找过程所花费时间较长,从而降低了算法的效率;其次,算法仅利用亮度和标准差来进行最佳匹配,对亮度与颜色对应的一致性要求很高。
代码在下面,执行起来花费时间较长,测试时请做好心理准备~~~
void C***View::OnMenuWelsh() //Welsh灰度图像迁移算法
{
int x,y,r,t;
for(x=0;x<m_bmp.lWidth;x++)
for(y=0;y<m_bmp.lHeight;y++)
{
//Luminance Remapping:使得颜色图像的l分量(等同于灰度分量)与形状图像保持一致
Carray_L[y][x]=(SsigmaL/CsigmaL)*(Carray_L[y][x]-CavgL)+SavgL;
}
for(t=0;t<m_bmp.lWidth;t++)
for(r=0;r<m_bmp.lHeight;r++)
{
//计算颜色图像5×5邻域统计量
int ns,a,b;
if(r>1&&r<m_bmp.lHeight-2&&t>1&&t<m_bmp.lWidth-2)
{
ns=25;
for(a=0;a<5;a++)
for(b=0;b<5;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a+r-2][b+t-2];
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==0&&t>1&&t<m_bmp.lWidth-2)
{
ns=0;
for(a=r;a<=r+2;a++)
for(b=t-2;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==1&&t>1&&t<m_bmp.lWidth-2)
{
ns=0;
for(a=r-1;a<=r+2;a++)
for(b=t-2;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==m_bmp.lHeight-1&&t>1&&t<m_bmp.lWidth-2)
{
ns=0;
for(a=r-2;a<=r;a++)
for(b=t-2;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==m_bmp.lHeight-2&&t>1&&t<m_bmp.lWidth-2)
{
ns=0;
for(a=r-2;a<=r+1;a++)
for(b=t-2;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
//
else if(t==0&&r>1&&r<m_bmp.lHeight-2)
{
ns=0;
for(a=r-2;a<=r+2;a++)
for(b=t;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(t==1&&r>1&&r<m_bmp.lHeight-2)
{
ns=0;
for(a=r-2;a<=r+2;a++)
for(b=t-1;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(t==m_bmp.lWidth-1&&r>1&&r<m_bmp.lHeight-2)
{
ns=0;
for(a=r-2;a<=r+2;a++)
for(b=t-2;b<=t;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(t==m_bmp.lWidth-2&&r>1&&r<m_bmp.lHeight-2)
{
ns=0;
for(a=r-2;a<=r+2;a++)
for(b=t-2;b<=t+1;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
/
else if(r==0&&t==0)
{
ns=0;
for(a=r;a<=r+2;a++)
for(b=t;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==1&&t==0)
{
ns=0;
for(a=r-1;a<=r+2;a++)
for(b=t;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==m_bmp.lHeight-2&&t==0)
{
ns=0;
for(a=r-2;a<=r+1;a++)
for(b=t;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==m_bmp.lHeight-1&&t==0)
{
ns=0;
for(a=r-2;a<=r;a++)
for(b=t;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
/
else if(r==0&&t==1)
{
ns=0;
for(a=r;a<=r+2;a++)
for(b=t-1;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==1&&t==1)
{
ns=0;
for(a=r-1;a<=r+2;a++)
for(b=t-1;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==m_bmp.lHeight-2&&t==1)
{
ns=0;
for(a=r-2;a<=r+1;a++)
for(b=t-1;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==m_bmp.lHeight-1&&t==1)
{
ns=0;
for(a=r-2;a<=r;a++)
for(b=t-1;b<=t+2;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
/
else if(r==0&&t==m_bmp.lWidth-2)
{
ns=0;
for(a=r;a<=r+2;a++)
for(b=t-2;b<=t+1;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==1&&t==m_bmp.lWidth-2)
{
ns=0;
for(a=r-1;a<=r+2;a++)
for(b=t-2;b<=t+1;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==m_bmp.lHeight-2&&t==m_bmp.lWidth-2)
{
ns=0;
for(a=r-2;a<=r+1;a++)
for(b=t-2;b<=t+1;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==m_bmp.lHeight-1&&t==m_bmp.lWidth-2)
{
ns=0;
for(a=r-2;a<=r;a++)
for(b=t-2;b<=t+1;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==0&&t==m_bmp.lWidth-1)
{
ns=0;
for(a=r;a<=r+2;a++)
for(b=t-2;b<=t;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==1&&t==m_bmp.lWidth-1)
{
ns=0;
for(a=r-1;a<=r+2;a++)
for(b=t-2;b<=t;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==m_bmp.lHeight-2&&t==m_bmp.lWidth-1)
{
ns=0;
for(a=r-2;a<=r+1;a++)
for(b=t-2;b<=t;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
else if(r==m_bmp.lHeight-1&&t==m_bmp.lWidth-1)
{
ns=0;
for(a=r-2;a<=r;a++)
for(b=t-2;b<=t;b++)
{
Cavg_L[r][t]=Cavg_L[r][t]+Carray_L[a][b];
ns++;
}
CSigma_L[r][t]=Cavg_L[r][t]/ns;
}
}
///全局搜索匹配//
double appr,apprtemp;
int apprnum[2];
int flag=0;
CDC *dc=GetDC();
t_bmp=s_bmp;
apprnum[0]=0;
apprnum[1]=0;
for(int a=0;a<s_bmp.lWidth;a++)
for(int b=0;b<s_bmp.lHeight;b++)
{
appr=(CSigma_L[0][0]+Carray_L[0][0])-(SSigma_L[b][a]+Sarray_L[b][a]);
appr=appr*appr;
for(t=0;t<m_bmp.lWidth;t++)
for(r=0;r<m_bmp.lHeight;r++)
{
apprtemp=(CSigma_L[r][t]+Carray_L[r][t])-(SSigma_L[b][a]+Sarray_L[b][a]);
apprtemp=apprtemp*apprtemp;
if(appr>=apprtemp)
{
flag++;
appr=apprtemp;
apprnum[0]=r;
apprnum[1]=t;
}
}
Tarray_L[b][a]=Sarray_L[b][a];
Tarray_A[b][a]=Carray_A[apprnum[0]][apprnum[1]];
Tarray_B[b][a]=Carray_B[apprnum[0]][apprnum[1]];
l=Tarray_L[b][a]/sqrt(3)+Tarray_A[b][a]/sqrt(6)+Tarray_B[b][a]/sqrt(2);
m=Tarray_L[b][a]/sqrt(3)+Tarray_A[b][a]/sqrt(6)-Tarray_B[b][a]/sqrt(2);
s=Tarray_L[b][a]/sqrt(3)-2*Tarray_A[b][a]/sqrt(6);
//取反对数,10次方
l=pow(10,l);
m=pow(10,m);
s=pow(10,s);
//转换回RGB颜色空间
t_bmp.pArray_R[b][a]=4.4687*l-3.5887*m+0.1196*s;
t_bmp.pArray_G[b][a]=-1.2197*l+2.3831*m-0.1626*s;
t_bmp.pArray_B[b][a]=0.0585*l-0.2611*m+1.2057*s;
//控制RGB值>255溢出
if(t_bmp.pArray_R[b][a]>255) {t_bmp.pArray_R[b][a]=255;}
if(t_bmp.pArray_G[b][a]>255) {t_bmp.pArray_G[b][a]=255;}
if(t_bmp.pArray_B[b][a]>255) {t_bmp.pArray_B[b][a]=255;}
//控制RGB值<0溢出
if(t_bmp.pArray_R[b][a]<0) {t_bmp.pArray_R[b][a]=0;}
if(t_bmp.pArray_G[b][a]<0) {t_bmp.pArray_G[b][a]=0;}
if(t_bmp.pArray_B[b][a]<0) {t_bmp.pArray_B[b][a]=0;}
dc->SetPixel(a+t_bmp.lWidth+m_bmp.lWidth+20,b,RGB(t_bmp.pArray_R[b][a],t_bmp.pArray_G[b][a],t_bmp.pArray_B[b][a]));
}