图像对比度越高,我们的眼睛对图像细节就更容易识别,此时,图像直方图则在整个像素值范围内(0-255)分布比较均匀,也表明了图像使用了更多的灰度级别,图像的细节表现的更好。对比度较低时,图像的直方图往往集中分布在一个较小的像素值范围内(例如:0-50、或者 100-200),接下来从两方面来了解如何增加图像的对比度:
- 图像直方图的绘制
- 直方图均衡化方法
1. 图像直方图的绘制
图像的灰度直方图显示了每个灰度级别在图像中出现的频率。通过灰度直方图,我们可以得到以下信息:
- 图像的亮度范围:灰度直方图可以显示出图像的亮度范围。如果灰度级别在图像中分布均匀,则灰度直方图将具有类似于均匀分布的形状。如果灰度级别主要集中在某个区域,则灰度直方图将具有较大的峰值。
- 对比度:通过灰度直方图,我们可以估计图像的对比度。如果图像的灰度分布广泛,则图像具有较高的对比度,反之则具有较低的对比度。
- 图像的亮度分布:灰度直方图可以告诉我们图像中像素的亮度分布情况。例如,如果灰度直方图呈现单峰形状,则图像中大多数像素的亮度都集中在该峰的灰度级别附近。如果灰度直方图呈现双峰形状,则图像中存在两个不同的灰度级别,可能代表不同的物体或者不同的光照条件。
总之,灰度直方图是图像处理中一个非常有用的工具,能够提供有关图像亮度、对比度、亮度分布和质量等方面的重要信息。下面是使用程序绘制灰度图直方图的示例代码:
import cv2 import matplotlib.pyplot as plt def test(): image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE) plt.subplot(121) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) plt.title('原图' + str(image.shape)) plt.imshow(image) plt.subplot(122) plt.title('灰度分布直方图') plt.hist(image.ravel(), bins=256, range=(0, 256)) plt.show() if __name__ == '__main__': test()
程序输出结果:
2. 直方图均衡化方法
直方图均衡化的基本思想是对输入图像的像素值进行统计分析,得到图像的灰度分布信息,然后重新分配像素值,使得输出图像的灰度分布尽可能均匀,从而增加图像的对比度。
函数 cv2.equalizeHist()
是 OpenCV 中的一种直方图均衡化方法,用于对图像进行对比度增强。该函数的实现过程如下:
- 计算输入图像的直方图:统计图像中每个像素值的数量,形成一个频率分布的直方图。
- 计算直方图的累积分布函数 CDF:将直方图中每个灰度值的像素数累加,得到一个累积值,然后将这些累积值归一化到 [0,1] 之间。
- 计算映射函数:将累积分布函数映射到 [0,255] 之间,形成一个灰度值的映射表。
L
表示映射表的大小(在 OpenCV 中为 256)v
表示输入像素值CDF(v)
表示输入图像的灰度值小于等于v
的像素值占整幅图像像素值总数的比例(即累积分布函数)
接下来,根据上面的映射公式对输入图像中的每个像素根据映射表将其重新分配为一个新的像素值,生成一个均衡化后的图像,使其灰度分布尽可能均匀。
示例代码:
import cv2 import matplotlib.pyplot as plt def test(): image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE) equal = cv2.equalizeHist(image) plt.subplot(221) plt.title('原图') plt.imshow(image, cmap='gray') plt.subplot(222) plt.title('均衡化') plt.imshow(equal, cmap='gray') plt.subplot(223) plt.title('原图直方图') plt.hist(image.ravel(), bins=256, range=(0, 256)) plt.subplot(224) plt.title('均衡化直方图') plt.hist(equal.ravel(), bins=256, range=(0, 256)) plt.show() if __name__ == '__main__': test()
程序执行结果:
很明显,均衡化之后确实提高了原图的对比度。对于图像的直方图集中在某一个区域,我们可以用上面这种方法增加图像对比度。对于直方图并不集中在某一区域的情况,我们可以使用自适应的直方图均衡化,它会将整幅图像会被分成很多小块(tiles,在 OpenCV 中 tiles 的大小默认是 8×8),然后再对每一个小块分别进行直方图均衡化。最后,为了去除每一个小块之间(由于算法造成)边界,再使用双线性插值,对小块进行缝合。
import cv2 import matplotlib.pyplot as plt def test(): image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE) # 初始化 CLAHE 对象 # clipLimit 对比度阈值限制 # tileGridSize 小块大小 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) # 对图像进行均衡化 image_clahe = clahe.apply(image) plt.subplot(221) plt.title('原图') plt.imshow(image, cmap='gray') plt.subplot(222) plt.title('自适应均衡化') plt.imshow(image_clahe, cmap='gray') plt.subplot(223) plt.title('原图直方图') plt.hist(image.ravel(), bins=256, range=(0, 256)) plt.subplot(224) plt.title('自适应均衡化直方图') plt.hist(image_clahe.ravel(), bins=256, range=(0, 256)) plt.show() if __name__ == '__main__': test()
程序执行结果: