WHCSRL 技术网

opencv22:Histograms直方图均衡_uncle

目标

在本节中

  • 学习直方图均衡化的概念
  • 利用它来提高图像的对比度

理论

假设一个图像,它的像素值仅局限于某个特定的值范围。例如,较亮的图像其所有像素值都比较大。但是一幅好的图像会有来自图像所有区域的像素。因此,需要将这个直方图拉伸到两端(如下图所示),这就是直方图均衡化的作用,这通常会提高图像的对比度。即,直方图均衡化,也就是调整图像的对比度。
均衡化方法是通过累计分布进行变换的,详情参看附加资源中的wiki。允许较低局部对比区域以获得更高的对比度。直方图均衡通过有效地扩展用于降低图像对比度的高度填充的强度值来实现这一点。该方法可用于图像与既明亮或黑暗的背景和前景。特别地,该方法可以导致X射线图像中的骨结构的更好观点,并更好地在曝光或未暴露的照片中更好地细节。该方法的一个关键优点是它是一种相当简单的技术,适用于输入图像和可逆的操作员。因此,如果已知直方图均衡函数,则可以恢复原始直方图。计算不是计算密集的。该方法的缺点是它是不分体的。它可能会增加背景噪声的对比度,同时降低可用信号。
在这里插入图片描述

Numpy中的均衡化实现

img = cv2.imread('wiki.png', 0)
hist, bins = np.histogram(img.ravel(), 256, [0,256])
cdf = hist.cumsum()
cdf_normalized = cdf * float(hist.max()-hist.min())/cdf.max()
plt.subplot(121)
plt.imshow(img, cmap='gray')
plt.subplot(122)
plt.plot(cdf_normalized, color='b')
plt.hist(img.ravel(), 256, [0, 256], color='r')
plt.xlim([0, 256])
plt.legend(('cdf', 'historgram'), loc='upper left')
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在这里插入图片描述

可以看到直方图位于较亮的区域。现在需要将其扩充到整个频谱。为此,需要一个转换函数,将亮区域的输入像素映射到整个区域的输出像素这就是直方图均衡化的作用

现在找到最小的直方图的值(不包括0),并应用wiki页面中给出的直方图均衡化方程。

在这里插入图片描述
但在这里用的是来自Numpy的掩码数组概念数组。对于掩码数组,所有操作都在非掩码元素上执行。可以从Numpy文档中了解更多关于掩码数组的信息。

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')
  • 1
  • 2
  • 3

现在有了查找表,该表为此提供了有关每个输入像素值的输出像素值是什么的信息。因此,仅应用变换。

img2 = cdf[img] 
hist, bins = np.histogram(img2.ravel(), 256, [0,256])

plt.subplot(121)
plt.imshow(img2, cmap='gray')
plt.subplot(122)
plt.plot(cdf_normalized, color='b')
plt.hist(img2.ravel(), 256, [0, 256], color='r')
plt.xlim([0, 256])
plt.legend(('cdf', 'historgram'), loc='upper left')
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

现在,像以前一样计算其直方图和cdf,结果如下所示:
在这里插入图片描述

另一个重要的特征是,即使图像是一个较暗的图像,经过均衡后,将得到亮度几乎相同的图像。因此,这是作为一个“参考工具”,使所有的图像具有相同的照明条件。这在很多情况下都很有用。例如,在人脸识别中,在对人脸数据进行训练之前,对人脸图像进行直方图均衡化处理,使其具有相同的光照条件

OpenCV中的直方图均衡实现

OpenCV具有执行此操作的函数cv.equalizeHist()。它的输入是灰度图像,输出的是直方图均衡图像。 代码如下:

img = cv2.imread('wiki.png', 0)
equ = cv2.equalizeHist(img)

# res = np.hstack((img, equ))  # stacking images side-by-side
plt.subplot(121)
plt.imshow(img, cmap='gray')

plt.subplot(122)
plt.imshow(equ, cmap='gray')
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

因此,可以在不同的光照条件下拍摄不同的图像,对其进行均衡并检查结果。

当图像的直方图限制在特定区域时,直方图均衡化效果很好。在直方图覆盖较大区域(即同时存在亮像素和暗像素)的强度变化较大的地方,效果不好

CLAHE(对比度受限的自适应直方图均衡)

上面看到的第一个直方图均衡化是考虑了图像的整体对比度。在许多情况下,这不是一个好主意。例如,下图显示了输入图像及其在全局直方图均衡后的结果。
在这里插入图片描述
直方图均衡后,背景对比度确实得到了改善。但是比较两个图像中雕像的脸,由于亮度过高,在那里丢失了大多数信息。这是因为它的直方图不像在前面的案例中所看到的那样局限于特定区域。

因此,为了解决这个问题,使用了自适应直方图均衡。在这种情况下,图像被分成称为tiles的小块(在OpenCV中,tileSize默认为8x8 。然后,像之前一样对这些块中的每一块进行直方图均衡。因此,在较小的区域中,直方图将限制在一个较小的区域中(除非存在噪声)。如果有噪音,它将被放大。为了避免这种情况,应用了对比度限制。如果任何直方图bin超出指定的对比度限制(在OpenCV中默认为40),则在应用直方图均衡之前,将这些像素裁剪并均匀地分布到其他bin。均衡后,要消除图块边界中的伪影,请应用双线性插值

下面的代码片段显示了如何在OpenCV中应用CLAHE

# clahe
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('idelo.png', 0)

# create a clahe object
clahe = cv2.createCLAHE(clipLimit=2.0,  tileGridSize=(8,8))
cl1 = clahe.apply(img)

plt.subplot(121)
plt.imshow(img, cmap='gray')

plt.subplot(122)
plt.imshow(cl1, cmap='gray')
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

查看下面的结果,并将其与上面的结果进行比较,尤其是雕像区域:

在这里插入图片描述

附加资源

推荐阅读