一、 实验原理
在绝大多数非天空的局部区域里,某一些像素总会有至少一个颜色通道有很低的值。换言之,该区域光强度的最小值是个很小的数。
我们给暗通道一个数学定义,对于任意的输入图像J,其暗通道可以用下式表达:
式中Jc表示彩色图像的每个通道 ,Ω(x)表示以像素X为中心的一个窗口。
式(5)的意义用代码表达也很简单,首先求出每个像素RGB分量中的最小值,存入一副和原始图像大小相同的灰度图中,然后再对这幅灰度图进行最小值滤波,滤波的半径由窗口大小决定,一般有WindowSize = 2 * Radius + 1;
暗通道先验的理论指出:
实际生活中造成暗原色中低通道值主要有三个因素:a)汽车、建筑物和城市中玻璃窗户的阴影,或者是树叶、树与岩石等自然景观的投影;b)色彩鲜艳的物体或表面,在RGB的三个通道中有些通道的值很低(比如绿色的草地/树/植物,红色或黄色的花朵/叶子,或者蓝色的水面);c)颜色较暗的物体或者表面,例如灰暗色的树干和石头。总之,自然景物中到处都是阴影或者彩色,这些景物的图像的暗原色总是很灰暗的。
首先,在计算机视觉和计算机图形中,下述方程所描述的雾图形成模型被广泛使用:
其中,I(X)就是我们现在已经有的图像(待去雾的图像),J(x)是我们要恢复的无雾的图像,A是全球大气光成分, t(x)为透射率。现在的已知条件就是I(X),要求目标值J(x),显然,这是个有无数解的方程,因此,就需要一些先验了。
将式(1)稍作处理,变形为下式:
如上所述,上标C表示R/G/B三个通道的意思。
首先假设在每一个窗口内透射率t(x)为常数,定义他为,并且A值已经给定,然后对式(7)两边求两次最小值运算,得到下式:
上式中,J是待求的无雾的图像,根据前述的暗原色先验理论有:
因此,可推导出:
把式(10)带入式(8)中,得到:
在现实生活中,即使是晴天白云,空气中也存在着一些颗粒,因此,看远处的物体还是能感觉到雾的影响,另外,雾的存在让人类感到景深的存在,因此,有必要在去雾的时候保留一定程度的雾,这可以通过在式(11)中引入一个在[0,1]之间的因子,则式(11)修正为:
本文中所有的测试结果依赖于: ω=0.95。
上述推论中都是假设全球大气光A值时已知的,在实际中,我们可以借助于暗通道图来从有雾图像中获取该值。具体步骤如下:
1) 从暗通道图中按照亮度的大小取前0.1%的像素。
2) 在这些位置中,在原始有雾图像I中寻找对应的具有最高亮度的点的值,作为A值。
到这一步,我们就可以进行无雾图像的恢复了。由式(1)可知: J = ( I - A)/t + A ,现在I,A,t都已经求得了,因此,完全可以进行J的计算。
当投射图t 的值很小时,会导致J的值偏大,从而使淂图像整体向白场过度,因此一般可设置一阈值T0,当t值小于T0时,令t=T0,本文中所有效果图均以T0=0.1为标准计算。
因此,最终的恢复公式如下:
二、实验步骤
1、找出每个像素的RGB三通道的最小值、形成图像的暗通道图像。
2、在暗通道图片中每个像素的周围15*15的矩形中找像素最小的值,作为该像素的值。
3、由12式算出投射图。
具体代码如下:
<span style="font-size:14px;">void dark_channel(unsigned char* des,const unsigned char* img,int width,int height)
{
int A = 0;
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
int B = img[y * width * 3 + x * 3 + 0];
int G = img[y * width * 3 + x * 3 + 1];
int R = img[y * width * 3 + x * 3 + 2];
if(B<G)
des[y * width + x]=B;
else
des[y * width + x]=G;
if(R<des[y * width + x])
des[y * width + x]=R;
}
}
for(int y = 0; y < 5; y++)
{
for(int x = 0; x < width; x++)
{
if(des[y * width + x]>A)
A=des[y * width + x];
}
}
int patch= 6;
printf("A的值%d",A);
double w=0.95*A/255;
unsigned char* ocl = new unsigned char[width * height];
memcpy(ocl,des,width * height);
for(int y = patch; y < height-patch; y++)
{
for(int x = patch; x < width-patch; x++)
{
for(int i = -patch; i < patch+1; i++)
{
for(int j = -patch; j < patch+1; j++)
{
if(des[(y + i) * width + (x + j)]<ocl[y * width + x])
ocl[y * width + x]=des[(y + i) * width + (x + j)];
}
}
ocl[y * width + x]=int(255-w*ocl[y * width + x]);
}
}
memcpy(des,ocl,width * height);
}</span>
二、 实验结果