赞
踩
图像增强是一种基本的图像处理操作,简单的来说就是把图像变的更清晰,或者说感兴趣的某个区域需要变的更加清晰。
而清晰度这个概念,在清晰度计算这一章节中提到过,一般来说,像素之间的梯度越大,图像就越清晰。
而直方图是用于统计像素分布的一个工具,计算每幅图像的直方图是传统图像处理中的一种基本操作,直方图表现了一张图像所有的像素的分布情况,直方图增强就是通过调整直方图的分布来实现图像的增强,简单的说就是把图像像素重新分布一下,提高图像中像素的整体梯度,让图像变得更清晰。
这个过程被称作直方图均衡化。
附上之前直方图的基本计算方式:
https://blog.csdn.net/pcgamer/article/details/124989015?spm=1001.2014.3001.5501
清晰度计算:
https://blog.csdn.net/pcgamer/article/details/127942102?spm=1001.2014.3001.5501
上面提到了均衡化的过程,其实就是把图像像素的分布改变一下。那么问题来了,怎么变?根据什么变?
首先来看一下opencv中的函数*equalizeHist()*的方式。
首先说下累计分布函数CDF(cumulative distribution function),这个函数可以这么理解:
直方图就是统计了某个灰度值的像素个数,比如灰度为100的像素个数有50个,总共有256中灰度值,那么可以记做:
n
100
=
50
n_{100} = 50
n100=50
,或者是$ n_i = 100, i = 100, 0<i<255$
归一化到[0, 1]的范围内,其实就是求这个灰度值出现的概率: p ( i ) = n i n p(i) = \frac{n_i}{n} p(i)=nni,n为像素总数。
那么累计分布函数就是:
H
(
x
)
=
∑
j
=
1
i
p
x
(
j
)
H(x) = \sum_{j=1}^ip_x(j)
H(x)=j=1∑ipx(j)
也就是某个灰度值所有的累计分布,比如灰度值100的H(x)就是从0-100的所有灰度值概率分布之和。
通过把每个点的像素值通过 H ( x ) H(x) H(x)来进行转换获得新的目标图像的灰度值。
有意思的是为什么要这么进行转换,这个证明过程不复杂,可以简单列一列。
首先可以认为原始图S,和目标图D。
原始图S的直方图分布记做 H A ( S ) H_A(S) HA(S),目标图D的直方图分布记做 H B ( D ) H_B(D) HB(D)。
我们的目的就是要找到一个映射关系 f f f,可以把原始图S中的像素值映射到目标图D,也就是说 D = f ( S ) D = f(S) D=f(S)。
直方图归一化到[0, 1]后,实际上就是某个灰度值的概率,所有的概率之和都是等于。
对于原图的直方图表示:
∑
0
S
H
(
S
)
\sum_0^SH(S)
∑0SH(S)就表示所有的概率之和。那么目标图的表示就是
∑
0
D
H
(
D
)
\sum_0^DH(D)
∑0DH(D), 两者是相等的。可以表示为:
∑
0
S
H
(
S
)
=
∑
0
D
H
(
D
)
\sum_0^SH(S) = \sum_0^DH(D)
0∑SH(S)=0∑DH(D)
最理想的目标图分布是均匀分布,也就是
H
(
D
)
=
A
N
H(D) = \frac{A}{N}
H(D)=NA, 其中的A表示每种像素值的值(每种都是相同的)。那么上面的公式就可以写成:
∑
0
S
H
(
S
)
=
∑
0
D
H
(
D
)
=
D
A
N
\sum_0^SH(S) = \sum_0^DH(D) = \frac{DA}{N}
0∑SH(S)=0∑DH(D)=NDA
其中
D
=
f
(
S
)
D=f(S)
D=f(S)
所有有可以写成:
∑
0
S
H
(
S
)
=
f
(
S
)
A
N
\sum_0^SH(S) = \frac{f(S)A}{N}
0∑SH(S)=Nf(S)A
换一下项就可以得到:
f
(
S
)
=
N
A
∑
0
S
H
(
S
)
f(S) = \frac{N}{A}\sum_0^SH(S)
f(S)=AN0∑SH(S)
最右边的那一坨中的H(S)就是上面提到的累积概率分布,只是这里要对整张图像的像素再做一次求和或者积分。
具体怎么计算,这里就不说了,有兴趣的朋友可以去了解下。
当然,这里有个问题,上面的理想状态是不太可能达到的,如果某些图像的直方图在某个小区域出现比较大的聚集的话,可能就没法非常好的进行平均分布了。
在opencv中,用于直方图均衡化的函数就是equalizeHist:
先上代码:
img = cv2.imread("xxxx")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
plt.title('Gray Histogram Contour')
plt.xlabel('gray level')
plt.ylabel('number of pixels')
plt.figure(1)
plt.plot(hist)
dst = cv2.equalizeHist(img)
hist_new = cv2.calcHist([dst], [0], None, [256], [0, 255])
plt.title('Gray Histogram Contour new')
plt.xlabel('gray level')
plt.ylabel('number of pixels')
plt.figure(2)
plt.plot(hist_new)
plt.show()
cv2.imshow("src", img)
cv2.imshow("new", dst)
cv2.waitKey()
cv2.destroyAllWindows()
在调用均衡化之前,先要计算图像的直方图,用于后续进行对比实验。
计算图像直方图的函数:调用opencv中的calcHist函数:
然后再通过plt库进行绘图展示。
直方图均衡化,这个函数就相对比较简单了,直接从源图到目标图进行转换。
我们看一下上面代码的结果,用来处理经典的一张图:
源图:
源图直方图
目标图
目标图直方图
从直方图分布可以看出,均衡化已经将像素点从相对集中变成了相对平衡的分布了。而这个累积分布概率函数的转换就是表明希望通过这样一个转换,使得像素尽量去满足一个像素值平均分布。
从最终形成的图像上来看,也是把一幅雾蒙蒙的图像变得相对清晰了。
但是存在一个这个方法典型的确定,马赛克现象比较严重。
上面提到的均衡化方法有两个比较明显的不足:
进一步的一个算法就是自适应直方图均衡化。
简单来说就是在上面的算法上做了两点改动:
代码也挺简单:
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl1 = clahe.apply(img)
hist_cl1 = cv2.calcHist([cl1], [0], None, [256], [0, 255])
plt.title('Gray Histogram Contour hist_cl1')
plt.xlabel('gray level')
plt.ylabel('number of pixels')
plt.figure(3)
plt.plot(hist_cl1)
plt.show()
cv2.imshow("hist_cl1", cl1)
其中的方法cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))是创建了一个CLANE类,其中的两个参数
相比之前算法的图,有两点改进:
当然,不是在所有的图像上,自适应方法都可以比普通的均衡化方法更好的,需要根据图像的特征来进行判断。``
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。