这是对于 OpenCV 官方文档中 图像处理 的学习笔记。学习笔记中会记录官方给出的例子,也会给出自己根据官方的例子完成的更改代码,同样彩蛋的实现也会结合多个知识点一起实现一些小功能,来帮助我们对学会的知识点进行结合应用。
如果有喜欢我笔记的请麻烦帮我关注、点赞、评论。谢谢诸位。
学习笔记:
学习笔记目录里面会收录我关于OpenCV系列学习笔记博文,大家如果有什么不懂的可以通过阅读我的学习笔记进行学习。
【OpenCV学习笔记】- 学习笔记目录
内容
- 我们将学习直方图均衡的概念,并用它来提高图像的对比度。
理论
考虑一个图像,其像素值仅局限于某个特定的值范围。例如,较亮的图像会将所有像素限制为高值。但好的图像将具有来自图像所有区域的像素。因此,您需要将该直方图拉伸到两端(如下图所示,来自维基百科),这就是直方图均衡的作用(简单来说)。这通常会提高图像的对比度。
我建议您阅读关于 直方图均衡 的维基百科页面以获取有关它的更多详细信息。它有很好的解释和示例,因此您在阅读后几乎会理解所有内容。相反,在这里我们将看到它的 Numpy 实现。之后,我们将看到 OpenCV 函数。
Numpy 实现直方图均衡
示例代码:
# OpenCV 中的直方图
# 直方图 - 2:直方图均衡
# 理论
# numpy 的直方图均衡实现
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('../image/3.7.1.png', 0)
hist, bins = np.histogram(img.flatten(), 256, [0, 256])
cdf = hist.cumsum()
cdf_normalized = cdf * float(hist.max()) / cdf.max()
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.show()
plt.plot(cdf_normalized, color='b')
plt.hist(img.flatten(), 256, [0, 256], color='r')
plt.xlim([0, 256])
plt.legend(('cdf', 'histogram'), loc='upper left')
plt.show()
效果图:
您可以看到直方图位于较亮的区域。我们需要全方位的服务。为此,我们需要一个转换函数,该函数将较亮区域中的输入像素映射到整个区域中的输出像素。这就是直方图均衡化的作用。
现在,我们找到最小的直方图值(不包括0)并应用Wiki页面中给出的直方图均衡方程。但是我在这里使用了Numpy的masked array概念数组。对于掩码数组,所有操作都在非掩码元素上执行。您可以从有关屏蔽数组的Numpy文档中了解有关此内容的更多信息。
示例代码:
# OpenCV 中的直方图
# 直方图 - 2:直方图均衡
# 理论
# numpy 的直方图均衡实现
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('../image/3.7.1.png', 0)
# cv.imshow("img", img)
# cv.waitKey(0)
hist, bins = np.histogram(img.flatten(), 256, [0, 256])
cdf = hist.cumsum()
# 获取掩码数组
cdf_m = np.ma.masked_equal(cdf, 0)
cdf_m = (cdf_m - cdf_m.min()) * 255 / (cdf_m.max() - cdf_m.min())
cdf = np.ma.filled(cdf_m, 0).astype('uint8')
# 均衡之后
# 应用变化
img2 = cdf[img]
# cv.imshow("img2", img2)
# cv.waitKey(0)
hist, bins = np.histogram(img2.flatten(), 256, [0, 256])
cdf = hist.cumsum()
cdf_normalized = cdf * float(hist.max()) / cdf.max()
plt.subplot(121), plt.imshow(img2, cmap='gray')
plt.show()
plt.plot(cdf_normalized, color='b')
plt.hist(img.flatten(), 256, [0, 256], color='r')
plt.xlim([0, 256])
plt.legend(('cdf', 'histogram'), loc='upper left')
plt.show()
# cv.destroyAllWindows()
效果图:
另一个重要特征是,即使图像是较暗的图像(而不是我们使用的较亮的图像),在均衡后,我们将获得与获得的图像几乎相同的图像。结果,它被用作“参考工具”,以使所有图像具有相同的照明条件。在许多情况下这很有用。例如,在人脸识别中,在训练人脸数据之前,将人脸图像进行直方图均衡,以使它们全部具有相同的光照条件。
OpenCV中的直方图均衡
OpenCV具有执行此操作的功能 cv.equalizeHist() 。它的输入只是灰度图像,输出是我们的直方图均衡图像。
下面是一个简单的代码片段,显示了它与我们使用的同一图像的用法:
示例代码:
# OpenCV 中的直方图
# 直方图 - 2:直方图均衡
# 理论
# OpenCV中的直方图均衡
import cv2 as cv
import numpy as np
img = cv.imread('../image/3.7.1.png', 0)
# cv.imshow("img", img)
# cv.waitKey(0)
equ = cv.equalizeHist(img)
# cv.imshow("equ", equ)
# cv.waitKey(0)
# stacking images side-by-side
res = np.hstack((img, equ))
cv.imshow("res", res)
cv.waitKey(0)
# cv.imwrite('res.png', res)
cv.destroyAllWindows()
效果图:
因此,现在您可以在不同的光照条件下拍摄不同的图像,对其进行均衡并检查结果。
当图像的直方图限制在特定区域时,直方图均衡化效果很好。在直方图覆盖较大区域(即同时存在亮像素和暗像素)的强度变化较大的地方,效果不好。请检查其他资源中的SOF链接。
CLAHE(对比度受限的自适应直方图均衡)
我们刚刚看到的第一个直方图均衡化考虑了图像的整体对比度。在许多情况下,这不是一个好主意。例如,下图显示了输入图像及其在全局直方图均衡后的结果。
使用OpenCV的直方图均衡效果:
效果图:
直方图均衡后,背景对比度确实得到了改善。但是在两个图像中比较雕像的脸。由于亮度过高,我们在那里丢失了大多数信息。这是因为它的直方图不像我们在前面的案例中所看到的那样局限于特定区域(尝试绘制输入图像的直方图,您将获得更多的直觉)。
因此,为了解决这个问题,使用了 自适应直方图均衡 。在这种情况下,图像被分成称为“ tiles”的小块(在OpenCV中,tileSize默认为8x8)。然后,像往常一样对这些块中的每一个进行直方图均衡。因此,在较小的区域中,直方图将局限于一个较小的区域(除非有噪声)。如果有噪音,它将被放大。为了避免这种情况,应用了 对比度限制 。如果任何直方图bin超过指定的对比度限制(在OpenCV中默认为40),则在应用直方图均衡之前,将这些像素裁剪并均匀地分布到其他bin。均衡后,要消除图块边界中的伪影,请应用双线性插值。
下面的代码片段显示了如何在OpenCV中应用CLAHE:
示例代码:
# OpenCV 中的直方图
# 直方图 - 2:直方图均衡
# 理论
# CLAHE(对比度受限的自适应直方图均衡)
import numpy as np
import cv2 as cv
img = cv.imread('../image/3.9.4-1.png', 0)
# create a CLAHE object (Arguments are optional).
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl1 = clahe.apply(img)
res = np.hstack((img, cl1))
cv.imshow("res", res)
cv.waitKey(0)
# cv.imwrite('clahe_2.jpg', cl1)
效果图:
其他资源
- 直方图均衡化的维基百科页面
- Numpy中的模版数组
还要检查有关对比度调整的以下SOF问题:
- 如何在C中的OpenCV中调整对比度?
- 如何使用opencv均衡图像的对比度和亮度?