基于暗通道先验的去雾算法
发布于 202278|遵循 CC BY-NC-SA 4.0 许可

短学期以项目为导向,我在的小组要做的方向是图像预处理——影像去云雾。第一步是对传统(非机器学习)方法去云雾进行探索。这篇文章是阅读了《Single Image Haze Removal Using Dark Channel Prior》以及相关解读博客之后的一个笔记留档。

去雾原理

暗通道先验是基于现实世界中的一个观察,即在室外无云雾的图像(outdoor haze-free images)中,至少有一个颜色通道的一些像素的强度(intensity)非常低,接近于零。也就是说,图像在无云雾的情况下最小强度接近于零。

为了检验这个先验的是否成立,作者从 Flickr.com 中收集了 5000+张的图片,并手动裁剪掉了天空部分。经过 resizing 和暗通道计算后,亮度直方图显示 75%的像素的亮度为 0,90%的像素的亮度值小于 25。

因素

经过分析,暗通道的低强度主要缘于 3 个因素:

  1. Shadows. e.g.,the shadows of cars, buildings and the inside of windows in cityscape images.
  2. Colorful objects or surfaces. Lacking color in any color channel will result in low values in the dark channel.
  3. Dark objects or surfaces. e.g., dark tree trunk and stone.

限制

如果目标场景内在的就和大气光类似,比如雪地、白色背景墙、大海等,暗通道先验就失效了,相应的去雾结果也会失效。

公式推导

首先我们知道暗通道可以由下面的公式得到:

Jdark(x)=minyΩ(x)(miny{r,g,b}Jc(y)),(1)\textbf{J}^{\textrm{dark}}(\textbf{x})=\min_{y\in \Omega(\textbf{x})}(\min_{y\in \{r,g,b\}}\textbf{J}^{\textrm{c}}(\textbf{y})),\quad(1)

根据暗通道先验,我们知道该值趋向于 0:

Jdark(x)=minyΩ(x)(miny{r,g,b}Jc(y))0,(2)\textbf{J}^{\textrm{dark}}(\textbf{x})=\min_{y\in \Omega(\textbf{x})}(\min_{y\in \{r,g,b\}}\textbf{J}^{\textrm{c}}(\textbf{y}))\rightarrow \textbf{0},\quad(2)

接下来,引入一个常用于 CV 和 CG 领域的有云图表示方法:

I(x)=J(x)t(x)+A(1t(x)),(3)\textbf{I}(\textbf{x})=\textbf{J}(\textbf{x})\textbf{t}(\textbf{x})+\textbf{A}(\textbf{1}-\textbf{t}(\textbf{x})),\quad(3)

其中:

  • I(x)\textbf{I}({\textbf x})为雾化图像(observed intensity)
  • J(x)\textbf J({\textbf x})是恢复图像(scene radiance)
  • t(x)\textbf t({\textbf x})为透射率( medium transmission describing the portion of the light that is not scattered and reaches the camera)
  • A\bf A为全局大气光线(global atmospheric light)

对于去云场景,我们会有一张有云的图像I(x)\textbf{I}({\textbf x}),需要通过去云处理得到恢复图像J(x)\textbf J({\textbf x})。因此,我们需要知道t(x)\textbf t({\textbf x})A\bf A的值。

透射率 t

接着(3)继续推导,两边同时除以A\bf A

IcAc=JcAct+1t,(4)\frac{\bf I^c}{\bf A^c}=\frac{\bf J^c}{\bf A^c}t+1-t,\quad(4)

再同时对(4)两边取暗通道:

minΩ(minc(IcAc))=minΩ(minc(JcAc))t+1t,(5)\min_{\Omega}\Big(\min_{c}(\frac{\bf I^c}{\bf A^c})\Big)=\min_{\Omega}\Big(\min_{c}(\frac{\bf J^c}{\bf A^c})\Big)t+1-t,\quad(5)

根据(2)我们可以推出:

minΩ(minc(JcAc))0,(6)\min_{\Omega}\Big(\min_{c}(\frac{\bf J^c}{\bf A^c})\Big)\rightarrow0,\quad(6)

将这个结论带入(5),我们可以估算得到透射率的公式:

t=1minΩ(minc(IcAc)),(7)t=1-\min_{\Omega}\Big(\min_{c}(\frac{\bf I^c}{\bf A^c})\Big),\quad(7)

当投射图t(x)\textbf t({\textbf x})的值很小时,会导致J(x)\bf J(\bf x)的值偏大,从而使得图像整体向白场过度,因此一般可设置一最小阈值t0t_0,使得t=max(t0,t(x))t=\max{(t_0, t(x))}

修正值ω\omega

通常而言,现实世界中即使在晴朗无云的情况下也会因为空气中的颗粒而有一定的“云雾”效果,因此,需要引入一个ω\omega保留一定程度的 haze。

t=1ωminΩ(minc(IcAc)),(8)t=1-\omega\min_{\Omega}\Big(\min_{c}(\frac{\bf I^c}{\bf A^c})\Big),\quad(8)

估算A

接着,需要估算全局大气光线A\textbf{A},具体步骤如下:

  1. 从暗通道图中按照亮度的大小取前 0.1%的像素。
  2. 在这些位置中,在原始有雾图像 I 中寻找对应的具有最高亮度的点的值,作为 A 值。

比起直接从图像中选取最高亮度点的值,这种方法避免了图像中白色物体(例如白色的建筑物、汽车等)造成的偏差。 最终,恢复图像如下:

J(x)=I(x)Amax(t(x),t0)+A,(9)\bf J(\bf x)={\bf I(\bf x)-\bf A\over {\rm max} (t(\bf x), t_0)} +{\bf A},\quad(9)

其他参数

Patch size 也是算法中较为重要的参数,窗口越大,其包含暗通道的概率越大,暗通道也就越黑。从实践的效果来看,窗口越大,去雾的效果越不明显。

源码实现

My-Single-Image-Haze-Removal-Using-Dark-Channel-Prior Fork 自Single-Image-Haze-Removal-Using-Dark-Channel-Prior,个人仅修改了一些运行时出的 bug。

Reference

Comments