# 「Single Image Haze Removal Using Dark Channel Prior」Python Implementation with OpenCV

### A Brief Review

Single Image Haze Removal Using Dark Channel Prior」是 2009 年 CVPR 最佳论文，何凯明博士在这篇论文中提出了暗通道先验的图像去雾算法。

It is based on a key observation most local patches in haze-free outdoor images contain some pixels which have very low intensities in at least one color channel.

Last, the haze removal can produce depth information and benefit many vision algorithms and advanced image editing. Haze or fog can be a useful depth clue for scene understanding. The bad haze image can be put to good use.

$$\mathbf{I}(\mathbf{x})=\mathbf{J}(\mathbf{x})t(\mathbf{x})+\mathbf{A}(1-t(\mathbf{x}))\tag{1}$$

### Dark Channel Prior

$$J^{dark}(\mathbf{x})=\min_{c\in\{r,g,b\}}(\min_{\mathbf{y}\in\Omega(\mathbf{x})}(J^c(\mathbf{y})))\tag{5}$$

### 估算透射率

$$\mathbf{I}(\mathbf{x})=\mathbf{J}(\mathbf{x})t(\mathbf{x})+\mathbf{A}(1-t(\mathbf{x}))\tag{1}$$

$$\min_{\mathbf{y}\in\Omega(\mathbf{x})}(\mathbf{I}^c(\mathbf{y}))=\overset{\sim}{t}(\mathbf{x})\min_{\mathbf{y}\in\Omega(\mathbf{x})}(J^c(\mathbf{y}))+(1-\overset{\sim}{t}(\mathbf{x})\mathbf{A}^c)\tag{6}$$

$$\min_{\mathbf{y}\in\Omega(\mathbf{x})}(\frac{\mathbf{I}^c(\mathbf{y})}{\mathbf{A}^c})=\overset{\sim}{t}(\mathbf{x})\min_{\mathbf{y}\in\Omega(\mathbf{x})}(\frac{J^c(\mathbf{y})}{\mathbf{A}^c})+(1-\overset{\sim}{t}(\mathbf{x}))\tag{7}$$

$$\min_{c}(\min_{\mathbf{y}\in\Omega(\mathbf{x})}(\frac{\mathbf{I}^c(\mathbf{y})}{\mathbf{A}^c}))=\overset{\sim}{t}(\mathbf{x})\min_{c}(\min_{\mathbf{y}\in\Omega(\mathbf{x})}(\frac{J^c(\mathbf{y})}{\mathbf{A}^c}))+(1-\overset{\sim}{t}(\mathbf{x}))\tag{8}$$

$$J^{dark}(\mathbf{x})=\min_{c}(\min_{\mathbf{y}\in\Omega(\mathbf{x})}(J^c(\mathbf{y})))=0\tag{9}$$

$$\min_{c}(\min_{\mathbf{y}\in\Omega(\mathbf{x})}(\frac{J^c(\mathbf{y})}{\mathbf{A}^c}))=0\tag{10}$$

$$\overset{\sim}{t}(\mathbf{x})=1-\min_{c}(\min_{\mathbf{y}\in\Omega(\mathbf{x})}(\frac{I^c(\mathbf{y})}{\mathbf{A}^c}))$$

$$\overset{\sim}{t}(\mathbf{x})=1-\omega\min_{c}(\min_{\mathbf{y}\in\Omega(\mathbf{x})}(\frac{I^c(\mathbf{y})}{\mathbf{A}^c}))$$

### 估算全局大气光强（global atmospheric light）

\begin{aligned} \mathbf{I}(\mathbf{x})&=\mathbf{J}(\mathbf{x})t(\mathbf{x})+\mathbf{A}(1-t(\mathbf{x}))\\ \mathbf{I}(\mathbf{x})&=\mathbf{J}(\mathbf{x})t(\mathbf{x})+\mathbf{A}-t(\mathbf{x})\mathbf{A}\\ \mathbf{I}(\mathbf{x})-\mathbf{A}&=\mathbf{J}(\mathbf{x})t(\mathbf{x})-t(\mathbf{x})\mathbf{A}\\ \mathbf{J}(\mathbf{x})t(\mathbf{x})-t(\mathbf{x})\mathbf{A}&=\mathbf{I}(\mathbf{x})-\mathbf{A}\\ \mathbf{J}(\mathbf{x})t(\mathbf{x})-t(\mathbf{x})\mathbf{A}&=\mathbf{I}(\mathbf{x})-\mathbf{A}\\ \mathbf{J}(\mathbf{x})-\mathbf{A}&=\frac{\mathbf{I}(\mathbf{x})-\mathbf{A}}{t(\mathbf{x})}\\ \mathbf{J}(\mathbf{x})&=\frac{\mathbf{I}(\mathbf{x})-\mathbf{A}}{t(\mathbf{x})}+\mathbf{A}\\ \end{aligned}

\mathbf{J}(\mathbf{x})=\frac{\mathbf{I}(\mathbf{x})-\mathbf{A}}{\max(t(\mathbf{x}), t_{0})}+\mathbf{A}\\

### Python Implementation Using OpenCV

#!/usr/bin/env python3.5
# -*- coding:utf-8 -*-

import optparse
import cv2
import numpy as np
import sys

def create_dehaze_options():
usage = "usage: %prog <options>"
parser = optparse.OptionParser(prog='dehaze', usage=usage)
parser.add_option('-i', '--image', type='string', dest='image', help='Image to dehaze')
parser.add_option('-o', '--output', type='string', dest='output', help='Path to save the output image')
return parser

def zmMinFilterGray(src, r=7):
'''minimum filter with radius'''
if r <= 0:
return src
h, w = src.shape[: 2]
I = src
res = np.minimum(I, I[[0] + list(range(h - 1)), :])
res = np.minimum(res, I[list(range(1, h)) + [h - 1], :])
I = res
res = np.minimum(I, I[: , [0] + list(range(w - 1))])
res = np.minimum(res, I[: , list(range(1, w)) + [w - 1]])
return zmMinFilterGray(res, r - 1)

def guidedfilter(I, p, r, eps):
'''Guided Filter'''
height, width = I.shape
m_I = cv2.boxFilter(I, -1, (r, r))
m_p = cv2.boxFilter(p, -1, (r, r))
m_Ip = cv2.boxFilter(I * p, -1, (r, r))
cov_Ip = m_Ip - m_I * m_p
m_II = cv2.boxFilter(I * I, -1, (r, r))
var_I = m_II - m_I * m_I
a = cov_Ip / (var_I + eps)
b = m_p - a * m_I
m_a = cv2.boxFilter(a, -1, (r, r))
m_b = cv2.boxFilter(b, -1, (r, r))
return m_a * I + m_b

def getV1(m, r, eps, w, maxV1):
'''
m is a RGB image with color value ranged in [0, 1]
Computing transmittance mask image V1
and Airlight A
'''
# Dark Channel Image
V1 = np.min(m, 2)

# Apply Guided Filter
V1 = guidedfilter(V1, zmMinFilterGray(V1, 7), r, eps)

# Calculating Airlight
bins = 2000
ht = np.histogram(V1, bins)
d = np.cumsum(ht[0]) / float(V1.size)
for lmax in range(bins - 1, 0, -1):
if d[lmax] <= 0.999:
break
A = np.mean(m, 2)[V1 >= ht[1][lmax]].max()

# Limit value range
V1 = np.minimum(V1 * w, maxV1)
return V1, A

def deHaze(m, r = 81, eps = 0.001, w = 0.95, maxV1 = 0.80, bGamma = False):
Y = np.zeros(m.shape)
# Calculating transmittance mask and airlight
V1, A = getV1(m, r, eps, w, maxV1)
for k in range(3):
# Color correction
Y[: , :, k] = (m[: , :, k] - V1) / (1 - V1 / A)
Y = np.clip(Y, 0, 1)

# Gamma correction if required
if bGamma:
Y = Y ** (np.log(0.5) / np.log(Y.mean()))
return Y

if __name__ == '__main__':
parser = create_dehaze_options()
(option, args) = parser.parse_args()

if option.image == None or option.output == None:
print(parser.format_help())
sys.exit(1)

# 读取需要去雾的图片
# 并将所有的值映射到[0, 1]内
haze = cv2.imread(option.image) / 255.0

# 应用暗通道先验去雾算法
# 重新将色彩的值映射回[0, 255]
dehazed = deHaze(haze) * 255

# 输出图像
cv2.imwrite(option.output, dehazed)

## 2 thoughts on “「Single Image Haze Removal Using Dark Channel Prior」Python Implementation with OpenCV”

1. 感觉会很有用……emmmm 虽然没看明白○( ＾皿＾)っHiahiahia…