要计算未知光源的特性必须从图片中提取相关的统计特性。当我们能够仅使用一个统计特性就获得未知光源特性时,算法就变得非常简单了。在这种情况下,未知光源必须在整幅图片上都是统一的。均值于是就成为了此类方法之下最好的统计指标。而灰色世界法正是利用了均值作为估算未知光源的关键统计量。
从物理意义上说,灰色世界法假设自然界景物对于光线的平均反射的均值在总体上是个定值,这个定值近似地为“灰色”。在给定图片的白平衡算法中,灰色世界假设图片中的反射面足够丰富,以至于可以作为自然界景物的一个缩影。若这幅图片是在经典光源下拍摄的,其均值就应该等于灰色。若这幅图是在非经典光源下拍摄的,那么均值就会大于或者小于灰色值。而该均值对于灰色的偏离程度则反映了未知光源相对于已知光源的特性。 (就是因为这个假设,实际上灰度世界对图片色彩不丰富的情况,效果不是很好)
虽然这个方法比较简单,但是仍然可以从一些方面进行调整。一个方面就是对于灰色的定义形式的选择。包括对于光谱的定义、对于光谱成分的定义和在经典光源之下的 RGB 的响应。另一个更加重要的调整方面就是对于灰色的选择。不管如何定义灰色,最佳的灰色之选必然是自然界实事上出现的灰色。但是这个值是无法获得的(除非是合成数据),所以对于灰色的选择是不同的灰色世界算法的一个重要的区别点。
一个方法是假设这个灰色就是实事上的灰色。也就是说反射光谱是均衡的。给定光源之下的 RGB响应是纯白色对此光源的响应值的一部分。比方说,可以使用 50%作为反射率(虽然这个灰色值对于人眼视觉习惯而言可能过于明亮)。
另一种方法,就是根据大量的数据提炼出一个均值,并把它定义为灰色。这种方法提炼的灰色值可能因为数据库使用的不同而有所不同。最终提炼的灰色也能仅适用于原始的数据库,而对于数据库未包括的图片的适用度就会比较差一些。确定下来的灰色表达形式可以用来表示。下标 i 表示信道,上标 c为canonical的首字母,表示经典光源。
在确定灰色的表达形式后只要用 RGB响应与经典光源下灰色的比值来归一化图片就可以了。假设 RGB响应均值为,下标 i 表示信道,上标 U为Unknown的首字母,表示未知光源。那么归一化率的计算式为 如下式所示:
根据光源转换理论,从未知光源到经典光源下图片表达式的转换式如下式所定义:
以下是实现:
灰度世界算法(Gray World)是以灰度世界假设为基础的,该假设认为对于一幅有着大量色彩变化的图像, R、 G、 B 三个分量的平均值趋于同一个灰色K。一般有两种方法来确定该灰度。
(1)直接给定为固定值, 取其各通道最大值的一半,即取为127或128;反射光谱均衡
(2)令 K = (Raver+Gaver+Baver)/3,其中Raver,Gaver,Baver分别表示红、 绿、 蓝三个通道的平均值。最终提炼的灰色也能仅适用于原始的数据库,而对于数据库未包括的图片的适用度就会比较差一些。
算法的第二步是分别计算各通道的增益:归一化
Kr=K/Raver;
Kg=K/Gaver;
Kb=K/Baver;
算法第三步为根据Von Kries 对角模型,对于图像中的每个像素R、G、B,计算其结果值:光源转换
Rnew = R * Kr;
Gnew = G * Kg;
Bnew = B * Kb;
对于上式,计算中可能会存在溢出(>255,不会出现小于0的)现象,处理方式有两种。
a、 直接将像素设置为255,这可能会造成图像整体偏白。
b、 计算所有Rnew、Gnew、Bnew的最大值,然后利用该最大值将将计算后数据重新线性映射到[0,255]内。实践证明这种方式将会使图像整体偏暗,建议采用第一种方案。
二、完美反射算法
根据图像形成理论,对于纯白色的反射面而言,无论光源是什么颜色,最终反射后的颜色总能完全表现出光源的颜色。如果景物中有纯白的部分,那么就可以直接从这些像素中提取出光源信息。基于这种思想的方法都被称为是镜面法 。
与灰色世界法类似的,镜面法也有多种调整方式。在本文中着重介绍两种 。
第一种方法较为基本也较为简便。这种方法假设图片中一定有纯白色面存在,那么在经典光源下,图片中所有像素点三个信道的最大值都应该等于 255,或者是一个给定的纯白色的值。在任何其他的光源下,图片纯白色点的像素值就会小于经典光源下的纯白色,并且这些点将代表整幅图片中最亮的点。那么只要将各信道的值按比例规一化到最大值(纯白色)就可以获得在经典光源下的图片了。
假设经典的白色表达形式可以用来表示。下标 i 表示信道,上标 c 表示经典光源。假设 RGB 响应的最值为 ,下标 i 表示信道,上标 U 表示未知光源。那么归一化率的计算式为如下式所示:
根据光源转换理论,从未知光源到经典光源下图片表达式的转换同样可以用下式定义:
原理:完美全反射理论perfect Reflector假设图像上最亮点就是白点,并以此白点为参考对图像进行自动白平衡,最亮点定义为R+G+B的最大值,具体编码步骤如下:
(1)计算每个像素的R\G\B之和分布,并保存到一临时内存块中。
1
2
3
4
5
6
7
8
9
10
11
12
|
for
(Y = 0; Y < Height; Y++)
{
Pointer = bmp.Pointer + Y * Stride;
for
(X = 0; X < Width; X++)
{
Sum = (
short
)(*(Pointer) + *(Pointer + 1) + *(Pointer + 2));
// R+G+B
HistRGB[Sum]++;
*SumP = (
short
)Sum;
Pointer += 3;
SumP++;
}
}
|
(2)按R+G+B值的大小计算出其前10%或其他Ratio的白色参考点的的阈值T。
1
2
3
4
5
6
7
8
9
|
for
(Y = 767; Y >= 0; Y--)
{
Sum += HistRGB[Y];
if
(Sum > Width * Height * Ratio / 100)
{
Threshold = Y;
break
;
}
}
|
(3)遍历图像中的每个点,计算其中R+G+B值大于T的所有点的R\G\B分量的累积和的平均值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
for
(Y = 0; Y < Height; Y++)
{
Pointer = bmp.Pointer + Y * Stride;
for
(X = 0; X < Width; X++)
{
if
(*SumP > Threshold)
{
AvgB += *Pointer;
AvgG += *(Pointer + 1);
AvgR += *(Pointer + 2);
// 为获得增益做准备
Amount++;
}
Pointer += 3;
SumP++;
}
}
AvgB /= Amount;
AvgG /= Amount;
AvgR /= Amount;
|
(4)对每个点将像素量化到[0,255]之间。
for
(Y = 0; Y < Height; Y++)
{
Pointer = bmp.Pointer + Y * Stride;
for
(X = 0; X < Width; X++)
{
Blue = *Pointer * MaxValue / AvgB;
// 另外一种算法需要先计算不抑制重新计算的RGB的范围,然后求RGB的最大值,如果最大值大于255,则所有的结果都要除以最大值在乘以255,但实际表明该算法、 不合适;
Green = *(Pointer + 1) * MaxValue / AvgG;
Red = *(Pointer + 2) * MaxValue / AvgR;
if
(Red > 255) Red = 255;
else
if
(Red < 0) Red = 0;
// 这里需要判断,因为RGB空间所有的颜色转换到YCbCr后,并不是填充满了0-255的范围的,反转过去就会存在一些溢出的点。
if
(Green > 255) Green = 255;
else
if
(Green < 0) Green = 0;
// 编译后应该比三目运算符的效率高
if
(Blue > 255) Blue = 255;
else
if
(Blue < 0) Blue = 0;
*Pointer = (
byte
)Blue;
*(Pointer + 1) = (
byte
)Green;
*(Pointer + 2) = (
byte
)Red;
Pointer += 3;
}
}
三、动态阈值算法
参考论文:A Novel Automatic White Balance Method For Digital Still Cameras
同经典的一些算法相同,算法分为两个步骤:白点检测和白点调整。
白点检测:
(1)为了增强算法的鲁棒性,原文将图像分成12部分,其中宽高比为4:3,关于这一点,我认为不合理,对图像不是通用的,后文再说。
(2)计算每个区域的Cb\Cr分量的平均值Mb\Mr。
(3)按下式计算每个区域的Cb\Cr分量的绝对差的累积值Db\Dr:
上式中N为每个区域的像素数。
(4)如果Db/Dr的值偏小,则我们忽略这一块,因为这表明这一块的颜色分布比较均匀,而这样的局部对于白平衡不好。这个偏小的准则我们稍微再谈。
(5)统计对于除了符合第四条的的其他区域的Mb/Mr/Db/Dr的平均值作为整幅图像的Mb/Mr/Db/Dr值。
关于这一条,原文的话是:The final Mb、Mr、Db、Dr are obtained by taking the average of those regions that pass this additional step。
我在实际中做的时候就是分别对每块进行的,似乎效果也还不错。
(6)按下述规则初步确定哪些点是属于白色参考点:
(7)对于初步判断已经属于白色参考点的像素,按大小取其亮度值为前10%的为最终确定的白色参考点。
白点调整:
(1)计算白色参考点亮度值的平均值Raver,Gaver,Baver,(各通道分开计算)。
(2)按照以下各式计算每个通道的增益:
式中,Ymax就是YCbCr颜色空间中Y分量的在整幅图像中的最大值。
(3)按照以下各式计算最终每个通道的颜色值:
其中R/G/B为在原始的颜色空间中的值,注意这里要进行溢出检测的。
简单的谈下白点检测的分块操作吧,原文把图像分成4*3的12快,这样做是针对于我们很多数码照片是这个比例的,如果通用,我觉得应该用每个块的大小来控制,比如每块为 100*100个像素。