一种自动阈值白平衡算法实现
1.算法原理
白平衡是图像处理的一个极重要概念。所谓白平衡(英文名称为White Balance),就是对白色物体的还原。当我们用肉眼观看这大千世界时,在不同的光线下,对相同的颜色的感觉基本是相同的,比如在早晨旭日初升时,我们看一个白色的物体,感到它是白的;而我们在夜晚昏暗的灯光下,看到的白色物体,感到它仍然是白的。这是由于人类从出生以后的成长过程中,人的大脑已经对不同光线下的物体的彩色还原有了适应性。但是,作为拍摄设备,如数码相机,可没有人眼的适应性,在不同的光线下,由于CCD输出的不平衡性,造成数码相机彩色还原失真。一般情况下,我们习惯性地认为太阳光是白色的,已知直射日光的色温是5200K左右,白炽灯的色温是3000K左右。用传统相机的日光片拍摄时,白炽灯光由于色温太低,所以偏黄偏红。所以通常现场光线的色温低于相机设定的色温时,往往偏黄偏红,现场光线的色温高于相机设定时,就会偏蓝。
为了解决不同色温下,引起的白色漂移现象。由于白色对色温变化的响应最大,通常用白色来作为调整的基色。通常的白平衡技术有:自动白平衡、钨光白平衡、荧光白平衡、室内白平衡、手动调节。本文仅介绍其中的一种自动白平衡。
白平衡算法通常分为两步:白色点的检测,白色点的调整。本方法采用一个动态的阀值来检测白色点。详细算法过程为:
1. 把图像w*h从RGB空间转换到YCrCb空间。
2. 选择参考白色点:
a. 把图像分成宽高比为4:3个块(块数可选)。
b. 对每个块,分别计算Cr,Cb的平均值Mr,Mb。
c. 对每个块,根据Mr,Mb,用下面公式分别计算Cr,Cb的方差Dr,Db。
d. 判定每个块的近白区域(near-white region)。
判别表达式为:
设一个“参考白色点”的亮度矩阵RL,大小为w*h。
若符合判别式,则作为“参考白色点”,并把该点(i,j)的亮度(Y分量)值赋给RL(i,j);
若不符合,则该点的RL(i,j)值为0。
3. 选取参考“参考白色点”中最大的10%的亮度(Y分量)值,并选取其中的最小值Lu_min.
4. 调整RL,若RL(i,j)<Lu_min, RL(i,j)=0; 否则,RL(i,j)=1;
5. 分别把R,G,B与RL相乘,得到R2,G2,B2。 分别计算R2,G2,B2的平均值,Rav,Gav,Bav;
6. 得到调整增益: Ymax=double(max(max(Y)))/5;
Rgain=Ymax/Rav;
Ggain=Ymax/Gav;
Bgain=Ymax/Bav;
7. 调整原图像:Ro= R*Rgain; Go= G*Ggain; Bo= B*Bgain;
2.关键实现代码
-
int RGB2YCbCr(IMAGE_TYPE *bmp_img,T_U8*Y_img,double *Cb_img,double *Cr_img,DWORD width,DWORD height)
-
{
-
T_U32 lineByte,Source_linebyte,source_index,dst_index;
-
T_U16 i,j,Y;
-
T_U16 k =
0;
-
T_U8 *Source_img,R,G,B;
-
-
double Cr;
-
double Cb;
-
-
lineByte = (width *
8 /
8 +
3) /
4 *
4;
-
Source_img = bmp_img+
54;
-
Source_linebyte = WIDTHBYTES(width*
24);
-
-
-
-
for (i =
0; i < height;i++)
-
{
-
for (j =
0;j < width;j++)
-
{
-
source_index = Source_linebyte*i+
3*j;
-
dst_index = lineByte*i+j;
-
-
-
R = Source_img[source_index+
2];
-
G = Source_img[source_index+
1];
-
B = Source_img[source_index];
-
-
-
Y =
0.299*R+
0.587*G+
0.114*B;
-
Cr =
0.5*R
-0.419*G
-0.081*B;
-
Cb =
-0.169*R
-0.331*G+
0.5*B;
-
-
-
Y_img[dst_index] = (T_U8)Y;
-
Cr_img[dst_index] = Cr;
-
Cb_img[dst_index] = Cb;
-
}
-
}
-
return
0;
-
}
-
-
-
int AutoWhiteBalance_Optimi(IMAGE_TYPE *bmp_img,DWORD width,DWORD height)
-
{
-
-
T_U8*Y_img,*Ydata_img,*SignData,R,G,B,*bmp_data,*Dstbmp_img,*Dstbmp_data;
-
T_U16 height_step = height/
3,witdth_step = width/
4;
-
DWORD PixNum = height_step*witdth_step,i,j,m,n,Threshold =
0,YLumi[
256] = {
0};
-
DWORD line_width,source_line_width,source_index,CbCr_indx,index,WhitePoint =
0,WhitePointCount =
0,WhitePoint10 =
0;
-
int arrindex=
0,YMax =
-999;
-
double Mr,Mb,Dr,Db,b1,b2,b,c,*Cb_img,*Cr_img,*Cbdata_img,*Crdata_img;
-
double MeanSumr,MeanSumb;
-
double absSumr,absSumb,Rave,Gave,Bave,RGain,GGain,BGain;
-
-
-
-
-
FILE *AutoWhiteBalance_fp = fopen(
"AutoBalance.bmp",
"wb");
-
-
if(
NULL == AutoWhiteBalance_fp)
-
{
-
printf(
"Can't open AutoBalance.bmp\n");
-
return
-1;
-
}
-
-
-
-
line_width = (width *
8 /
8 +
3) /
4 *
4;
//8位深的BMP图像输入图像
-
source_line_width = ((width *
24 /
8 +
3) /
4 *
4 );
-
Cb_img = (
double*)
malloc(width*height*
sizeof(
double));
-
Cr_img = (
double*)
malloc(width*height*
sizeof(
double));
-
Y_img = (T_U8*)
malloc(line_width*height);
-
Dstbmp_img = (T_U8*)
malloc(source_line_width*height+BMPHEADSIZE);
-
SignData = (T_U8*)
malloc(line_width*height);
-
memcpy(Dstbmp_img,bmp_img,source_line_width*height+BMPHEADSIZE);
-
-
RGB2YCbCr(bmp_img,Y_img,Cb_img,Cr_img,width,height);
-
-
Cbdata_img = Cb_img;
-
Crdata_img = Cr_img;
-
Ydata_img = Y_img;
-
-
WhitePoint =
0;
-
for (i=
0;i < height; i += height_step)
-
{
-
for (j =
0; j <width; j += witdth_step)
-
{
-
Mb =
0;
-
Mr =
0;
-
MeanSumr =
0;
-
MeanSumb =
0;
-
absSumr =
0;
-
absSumb =
0;
-
for (m =
0; m < height_step;m++)
-
{
-
for (n =
0; n <witdth_step;n++)
-
{
-
index = (m+i)*width+n+j;
-
MeanSumr += (Crdata_img[index]);
-
MeanSumb += (Cbdata_img[index]);
-
}
-
}
-
-
//计每个块Cb,Cr的均值
-
Mr = MeanSumr/(
double)PixNum;
-
Mb = MeanSumb/(
double)PixNum;
-
-
for (m =
0; m < height_step;m++)
-
{
-
for (n =
0; n <witdth_step;n++)
-
{
-
index = (m+i)*width+n+j;
-
absSumr +=
abs(Crdata_img[index]-Mr);
-
absSumb +=
abs(Cbdata_img[index]-Mb);
-
}
-
}
-
-
//计算每个块绝对差累加值
-
Dr = absSumr / PixNum;
-
Db = absSumb / PixNum;
-
-
-
if (Mb<
0)
//计算mb+db*sign(mb)
-
{
-
b=Mb+Db*(
-1);
-
}
-
else
-
b=Mb+Db;
-
-
-
if (Mr<
0)
//计算1.5*mr+dr*sign(mb);
-
{
-
c=
1.5*Mr+Dr*(
-1);
-
}
-
else
-
c=
1.5*Mr+Dr;
-
-
-
//候选白点像素计算
-
for (m =
0; m < height_step;m++)
-
{
-
for (n =
0; n <witdth_step;n++)
-
{
-
index =(m+i)*line_width+n+j;
-
CbCr_indx = (m+i)*width+n+j;
-
if(
abs(Cbdata_img[CbCr_indx]-b)<(
1.5*Db) &&
abs(Crdata_img[CbCr_indx]-c)<(
1.5*Dr))
-
{
-
YLumi[Ydata_img[index]]++;
-
SignData[index] = Ydata_img[index];
-
WhitePoint++;
-
}
-
-
}
-
}
-
-
}
-
}
-
-
//选取候选白点数的最亮10%确定为最终白点,并选择其前10%中的最小亮度值
-
WhitePointCount =
0;
-
for(i =
255; i >
0; i--)
-
{
-
WhitePointCount += YLumi[i];
-
-
if(WhitePointCount >= (
double)WhitePoint/
10)
-
{
-
Threshold = i;
-
break;
-
}
-
}
-
-
WhitePoint10 =
0;
-
for(i =
0; i < height;i++)
-
{
-
for(j =
0;j < width;j++)
-
{
-
index = i*line_width+j;
-
if(SignData[index] >= Threshold)
-
{
-
SignData[index] =
1;
-
WhitePoint10++;
-
}
-
else
-
SignData[index] =
0;
-
}
-
}
-
-
-
bmp_data = bmp_img+
54;
-
Dstbmp_data = Dstbmp_img +
54;
-
Rave =
0;
-
Gave =
0;
-
Bave =
0;
-
-
//白点的RGB三分量的平均值
-
for(i =
0;i < height;i++)
-
{
-
for(j =
0; j <width;j++)
-
{
-
source_index = i*source_line_width+
3*j;
-
index = i*line_width+j;
-
Dstbmp_data[source_index+
0] = bmp_data[source_index]*SignData[index];
-
Dstbmp_data[source_index+
1] = bmp_data[source_index+
1]*SignData[index];
-
Dstbmp_data[source_index+
2] = bmp_data[source_index+
2]*SignData[index];
-
-
Rave += Dstbmp_data[source_index+
2];
-
Gave += Dstbmp_data[source_index+
1];
-
Bave += Dstbmp_data[source_index+
0];
-
}
-
}
-
-
Rave = Rave / (WhitePoint10);
-
Gave = Gave / (WhitePoint10);
-
Bave = Bave / (WhitePoint10);
-
-
-
for(i =
0; i < height;i++)
-
{
-
for(j =
0; j < width;j++)
-
{
-
index = i*line_width+j;
-
if(YMax < Ydata_img[index])
-
YMax = Ydata_img[index];
-
}
-
}
-
-
//增益调整
-
YMax = YMax /
3.0;
-
-
RGain = YMax / Rave;
-
GGain = YMax / Gave;
-
BGain = YMax / Bave;
-
-
//白平衡校正
-
for(i =
0; i <height; i++)
-
{
-
for(j =
0; j < width;j++)
-
{
-
source_index = i*source_line_width+
3*j;
-
bmp_data[source_index] = (T_U8)(BGain* bmp_data[source_index]);
-
bmp_data[source_index+
1] = (T_U8)(GGain* bmp_data[source_index+
1]);
-
bmp_data[source_index+
2] = (T_U8)(RGain* bmp_data[source_index+
2]);
-
-
}
-
}
-
-
-
fwrite(bmp_img, source_line_width*height+BMPHEADSIZE,
1, AutoWhiteBalance_fp);
-
fclose(AutoWhiteBalance_fp);
-
free(Cb_img);
-
free(Cr_img);
-
free(Y_img);
-
-
return
0;
-
-
}
3.图像效果
A色温校正对比图
TL84色温校正对比图
D65色温校正对比图
参考文献:
1.http://www.cnblogs.com/haar/articles/1392227.html
2.基于灰度世界、完美反射、动态阈值等图像自动白平衡算法的原理、实现及效果
3.https://www.cnblogs.com/Imageshop/archive/2013/04/20/3032062.html