图像复原–Enhancing Underwater Images and Videos by Fusion
建议优先阅读原论文和以下参考链接。
参考链接:
https://ieeexplore.ieee.org/document/6247661
https://blog.csdn.net/weixin_45709608/article/details/118965983
https://blog.csdn.net/a6333230/article/details/86288906
https://github.com/bilityniu/underwater_image_fusion/tree/master
1. 摘要
文章方法建立在融合策略基础上,主要是从退化版本的图像中提取输入权重信息,作者还定义了针对原始水下图像颜色校正和对比度增强的两个输入,以及四种权重图, 主要为解决因介质散射和吸收、退化导致远处目标可见性较低问题。融合框架还能有效的保证边缘降噪,能够保证相邻帧之间的时间相干性(针对视频处理)。增强的图像和视频的具备低噪声图像的水准,在暗区曝光更合理,并且改善了全局对比度,对于细微的细节和边缘也能有显著的提高。
2. 背景
(待补充)
3. 算法实现
3.1 两个输入
算法输入包含两个部分,首先是对图像进行白平衡处理,采取的策略是灰度世界与直方图均衡化增加图像对比度,以此来作为后续输入1。需要注意的是numpy中quantile()函数跟matlab中的quantile()函数最后得到的百分位精度不一样,matlab的范围更大,python与其大概差2-3个灰度值。
'''
1. 对输入的彩色图像进行灰度世界白平衡处理(Gray World White Balancing)。
2. 进行直方图自动对比度调整,以增强图像的对比度。
'''
def simple_color_balance(img):
B, G, R = np.double(img[:, :, 0]), np.double(img[:, :, 1]), np.double(img[:, :, 2])
Ravg = np.mean(R)
Gavg = np.mean(G)
Bavg = np.mean(B)
Max = np.max([Ravg, Gavg, Bavg])
ratio = [Max / Ravg, Max / Gavg, Max / Bavg]
# 根据比例系数确定要保留的像素百分比,该值为每个通道的5%。
satLevel = 0.005 * np.array(ratio)
m,n,p = img.shape
imgRGB_orig = np.zeros((p, m*n))
for i in range(p):
imgRGB_orig[i, : ] = np.reshape(np.double(img[:, :, i]), [1, m * n])
imRGB = np.zeros(shape=(imgRGB_orig.shape))
# 对每个通道的像素值进行直方图拉伸,以增强对比度
num = 255
for ch in range(p):
q = [satLevel[ch], 1 - satLevel[ch]] # 注意这里是两个值
# percentile函数用于计算数组中给定百分位数的值, 百分位数表示在一组数据中有多少比例的数据小于或等于该值。
# imgRGB_orig[ch, :]计算百分位数的输入数组, q * 100要计算的百分位数值,比如[25, 75]表示计算1/4和3/4百分位数的值
# 这里的结果跟matlab计算的百分数精度不一致
# tiles1 = stats.mstats.mquantiles(imgRGB_orig[ch, :], q[0], alphap=0.5, betap=0.5)
# tiles2 = stats.mstats.mquantiles(imgRGB_orig[ch, :], q[1], alphap=0.5, betap=0.5)
tiles = np.quantile(imgRGB_orig[ch, :], q)
temp = imgRGB_orig[ch, :]
# 小于或者大于百分位数值范围之外的用百分位数值替换
temp[temp < tiles[0]] = tiles[0]
temp[temp > tiles[1]] = tiles[1]
imRGB[ch, :] = temp
pmin = np.min(imRGB[ch, :])
pmax = np.max(imRGB[ch, :])
# 将图像的像素值线性映射到[0,255]的范围内,以增强对比度
# (imRGB[ch, :] - pmin) * num 将图像的像素值映射到0到num之间,
# 对于8位图像的最大像素值, num通常为255
imRGB[ch, :] = (imRGB[ch, :] - pmin) * num / (pmax - pmin)
output = np.zeros_like(img)
for i in range(p):
output[:, :, i] = np.reshape(imRGB[i, :], (m, n))
output = cv2.merge([output[:, :, 0], output[:, :, 1], output[:, :, 2]])
output = output.astype(np.uint8)
return output
对输入1进行ALB变换, 对L通道进行亮通道分块直方图均衡化,该策略不会对亮的地方造成影响,处理完成后再对块进行过度, 该项作为输入2。这里的分开直方图均衡化采用的是opencv中的createCLAHE()函数,原matlab代码中作者是自己实现的。
bilateral_filter_lab_input2 = bilateral_filter_lab(input2)
# 自适应直方图均衡化
# 利用opencv中的createCLAHE函数替代matlab中的adapthisteq函数进行凸显自适应直方图均衡化
# 创建CLAHE对象(对比度有限的自适应直方图均衡化)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
# 对亮度通道进行自适应直方图均衡化
bilateral_filter_lab_input2_L = bilateral_filter_lab_input2[:,:,0]
bilateral_filter_lab_input2_L = clahe.apply(bilateral_filter_lab_input2_L)
3.2 四种权重计算
Laplacian contrast weight(拉普拉斯对比度):对每个输入亮度通道应用拉普拉斯滤波并计算滤波结果的绝对值,但是拉普拉斯对比度无法区分斜坡和平坦区域,所以增加了后续对比度方法。这里的matlab代码跟中计算拉普拉斯对比度的方法不大一样。
# 1. 计算拉普拉斯对比度, 这里的matlab代码跟上一份计算拉普拉斯对比度的方法不大一样啊
laplacian_filter = cv2.Laplacian(R1, cv2.CV_64F)
WL1 = np.abs(laplacian_filter)
laplacian_filter = cv2.Laplacian(R2, cv2.CV_64F)
WL2 = np.abs(laplacian_filter)
Local contrast weight(局部对比度):计算每个像素与其邻域平均值之间的关系。该权重是为了加强了局部对比,因为它主要在输入2的高亮和阴影部分过渡。该权重由像素亮度水平与其周围区域局部平均值之间的标准差计算得到。
W
L
C
(
x
,
y
)
=
∣
∣
I
k
−
I
w
h
c
k
∣
∣
(4)
W_{LC}(x,y)=||I^{k}-I^{k}_{w_{hc}}|| \tag{4}
WLC(x,y)=∣∣Ik−Iwhck∣∣(4)
其中
I
k
I^k
Ik表示的是亮度通道的输入,
I
w
h
k
I^k_{w_h}
Iwhk表示的是经过低通滤波版本。
I
w
h
k
I^k_{w_h}
Iwhk的计算利用5×5的可分离二项核
(
1
16
[
1
,
4
,
6
,
4
,
1
]
)
(\frac{1}{16}[1,4,6,4,1])
(161[1,4,6,4,1]),高频cut-off值为
w
h
c
=
π
2.75
w_{hc}=\frac{\pi}{2.75}
whc=2.75π。对于小卷积核函数,二项式核函数是高斯核函数的一个很好近似,可以更有效地进行计算。
# 2. 计算局部对比度
h = np.array([1, 4, 6, 4, 1]) / 16.0
whc = np.pi / 2.75 # 高频cut-off值
WLC1 = cv2.filter2D(R1, -1, np.outer(h, h), borderType=cv2.BORDER_REPLICATE)
WLC1[WLC1 > whc] = whc
WLC1 = (R1 - WLC1) ** 2
WLC2 = cv2.filter2D(R2, -1, np.outer(h, h), borderType=cv2.BORDER_REPLICATE)
WLC2[WLC2 > whc] = whc
WLC2 = (R2 - WLC2) ** 2
Saliency weight(显著性权重):主要为了增强在水下场景中失去其突出性的识别对象。显著性权重倾向于突出显示的区域。为了提高结果的准确性,引入了曝光图来保护在某些特定情况下可能被改变的中间色调。
def getSaliencyWeight(img):
lab_color = color.rgb2lab(img)
l = np.double(lab_color[:, :, 0])
a = np.double(lab_color[:, :, 1])
b = np.double(lab_color[:, :, 2])
lm = np.mean(l)
am = np.mean(a)
bm = np.mean(b)
sm_weight = np.power((l-lm),2) +np.power((a-am),2)+ np.power((b-bm),2)
return sm_weight
Exposedness weight(曝光权重):衡量像素值被曝光的权重大小,通常,像素归一化值接近0.5的平均值时,像素倾向于具有较高的曝光权重。
W
E
(
x
,
y
)
=
e
x
p
(
−
(
I
k
(
x
,
y
)
−
0.5
)
2
2
σ
2
)
(5)
W_E(x,y)=exp(-\frac{(I^k(x,y)-0.5)^2}{2\sigma^2}) \tag{5}
WE(x,y)=exp(−2σ2(Ik(x,y)−0.5)2)(5)
其中
I
k
(
x
,
y
)
I^k(x,y)
Ik(x,y)表示
(
x
,
y
)
(x,y)
(x,y)位置的像素值,标准差
σ
=
0.25
\sigma=0.25
σ=0.25。上述表达式会对计算距离区域0的区域赋予更高的权重值,相反距离越远,曝光权重越小。
verage = 0.5
sigma = 0.25
WE1 = np.exp(-np.power((R1 - average), 2) / (2 * sigma**2))
WE2 = np.exp(-np.power((R2 - average), 2) / (2 * sigma**2))
权重归一化
为了产生一致行的结果,使用归一化的权重值 W k ˉ \bar{W^k} Wkˉ(对于输入k,归一化的权重计算为 W k = W k ∑ k = 1 K W k ˉ \bar{W^k=\frac{W^k}{\sum^K_{k=1}W^k}} Wk=∑k=1KWkWkˉ),通过约束权重 W W W在每个像素位置的总和等于1。即对上述2个输入共8种不同的权重值进行归一化。
3.3 多尺度融合
本文的融合跟《Color Balance and Fusion for Underwater Image Enhancement》一文中的尺度融合的思想是一致的,都是分别对权重和处理后的多尺度图像进行融合。
金字塔权重分解
利用高斯金字塔对归一化的权重进行下采样,用拉普拉斯金字塔对两个输入的图像进行下采样,下采样的层数为5,获得下采样同尺度的权重和图像以后,将权重与图像中每个像素位置(x, y)的灰度值融合,得到增强版本图像
R
(
x
,
y
)
R(x,y)
R(x,y):
R
(
x
,
y
)
=
∑
k
=
1
K
W
ˉ
k
(
x
,
y
)
I
k
(
x
,
y
)
(6)
R(x,y)=\sum^K_{k=1}\bar{W}^k(x,y)I^k(x,y) \tag{6}
R(x,y)=k=1∑KWˉk(x,y)Ik(x,y)(6)
其中
I
k
I^k
Ik表示的是输入图像,
W
k
ˉ
\bar{W^k}
Wkˉ表示的是归一化的权重值。上述式子在本文中表示输入1×自己的权重+输入2×自己的权重。
图像融合
利用拉普拉斯金字塔将图像分解成不同尺度的子图,再使用高斯金字塔对归一化后的权重值
W
k
ˉ
\bar{W^k}
Wkˉ进行分解,分解后权重和图像每个尺度下的大小是一致,然后进行对应元素相乘,再将所有尺度累加得到最终的图像融合结果。
R
l
(
x
,
y
)
=
∑
k
=
1
K
G
l
{
W
ˉ
k
(
x
,
y
)
}
L
l
{
I
k
(
x
,
y
)
}
(6)
R^l(x,y)=\sum^K_{k=1}G^l\{\bar{W}^k(x,y)\}L^l\{I^k(x,y)\} \tag{6}
Rl(x,y)=k=1∑KGl{Wˉk(x,y)}Ll{Ik(x,y)}(6)
其中
l
l
l表示金字塔的层数,通常将
l
=
5
l=5
l=5,
L
{
l
}
L\{l\}
L{l}是对图像进行拉普拉斯金字塔分解,
G
{
W
ˉ
}
G\{\bar{W}\}
G{Wˉ}表示对归一化权重
W
ˉ
\bar{W}
Wˉ执行高斯金字塔分解。以上两个步骤以自底向上的方式对每个金字塔层依次执行。最终结果通过对所有输入融合求和得到最终输出。
4. 最终结果