通过本篇文章的学习,你将学习到以下内容:
考虑这样一幅图像,它的像素值仅局限于某个特定的值范围,例如,较亮的图像将把所有像素限制在高值上。但是一幅好的图像会有来自图像所有区域的像素。因此,你需要将这个直方图拉伸到两端,这就是直方图均衡化的作用(简单来说),这通常会提高图像的对比度。
下面我们以一幅图像为例,使用numpy库的np.historam()
函数实现该图像的直方图,使用hist.cumsum()
函数查看该图像的亮度分布–累积分布函数(CDF)图,理想情况下沿直线增长,可以通过直方图均衡,调整图片到最理想的状态,为后续的直方图的均衡化提供直观的参考。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# 加载原始灰度图像
img = cv.imread('./data/pool_table.jpg', 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, 'gray')
# 绘制亮度累积分布图与直方图
plt.subplot(122), plt.plot(cdf_normalized, color='g'), plt.hist(img.flatten(), 256, [0, 256], color='b')
# 设置横坐标范围
plt.xlim([0, 256])
# 设置图例
plt.legend(('cdf', 'histogram'), loc = 'upper left')
# 显示图像
plt.show()
从图2中可以看到,该示例图像整体较暗,从该图像的灰度直方图可以看出灰度值主要集中在[0, 50]
之间,亮度累积分布函数为非理想状态(非近似沿直线上升)。为此,我们需要一个转换函数,将亮区域的输入像素映射到整个区域的输出像素,改善整幅图像的质量,这就是直方图均衡化的作用。
OpenCV具有执行此操作的功能函数为cv.equalizeHist()
。它的输入是灰度图像,输出是我们的直方图均衡图像。 对示例图像进行直方图均衡化,下面是实现代码片段:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# 加载原始灰度图像
img = cv.imread('./data/pool_table.jpg', 0)
# 直方图均衡化
equ = cv.equalizeHist(img)
# 实现该图像的直方图
hist, bins = np.histogram(equ.flatten(), 256, [0, 256])
# 查看该图像的亮度累积分布
cdf = hist.cumsum()
# 亮度累积分布归一化
cdf_normalized = cdf*float(hist.max())/cdf.max()
# 显示原始图像
plt.subplot(131), plt.imshow(img, 'gray')
# 均衡化后的图像
plt.subplot(132), plt.imshow(equ, 'gray')
# 绘制亮度累积分布图与直方图
plt.subplot(133), plt.plot(cdf_normalized, color='g'), plt.hist(equ.flatten(), 256, [0, 256], color='b')
# 设置横坐标范围
plt.xlim([0, 256])
# 设置图例
plt.legend(('cdf', 'histogram'), loc = 'upper left')
# 显示图像
plt.show()
经过直方图均衡化处理后得到的结果图像比原图更加清晰地展示了图像中的细节。根据直方图均衡化原理,均衡化后的图像直方图应该是“平”的,但是实际的效果图中所呈现出的是参差不齐的外形,而不是“平”的,这是由于在一些灰度级处可能没有像素,而在另外一些灰度级处像素很拥挤造成的。
我们刚刚看到的直方图均衡化提升了图像的整体对比度,但在许多情况下,均衡化处理以后暗区域的噪声可能会被放大,这并不是我们想要的结果,例如,下图显示了输入图像及其在全局直方图均衡后的结果。
import cv2 as cv
from matplotlib import pyplot as plt
# 加载原始灰度图像
img = cv.imread('./data/clahe.jpg', 0)
# 直方图均衡化
equ = cv.equalizeHist(img)
plt.subplot(121), plt.imshow(img, 'gray'), plt.title('Original_image')
plt.subplot(122), plt.imshow(equ, 'gray'), plt.title('equalizeHist_image')
plt.show()
直方图均衡后,背景对比度确实得到了改善。仔细观察可以发现原图中比较亮的区域,经过全局直方图处理后出现了失真的情况,而且出现了明显的噪声。(尝试绘制输入图像的直方图,你将获得直观接结果)。
因此,为了解决这个问题,使用了自适应直方图均衡。在这种情况下,图像被分成称为“tiles”的小块(在OpenCV中,tileSize默认为 8x8
)。然后,对这些块中的每一个进行直方图均衡。因此,在较小的区域中,直方图将限制在一个较小的区域中(除非存在噪声)。
如果有噪音,它将被放大。为了避免这种情况,应用了对比度限制。如果任何直方图bin
超出指定的对比度限制(在OpenCV中默认为40
),则在应用直方图均衡之前,将这些像素裁剪并均匀地分布到其他bin
。均衡后,要消除图块边界中的伪影,请应用双线性插值。
import cv2 as cv
from matplotlib import pyplot as plt
# 加载原始灰度图像
img = cv.imread('./data/clahe.jpg', 0)
# 直方图均衡化
equ = cv.equalizeHist(img)
# CLAHE处理
# 创建CLAHE对象(参数是可选的)
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cll = clahe.apply(img)
plt.subplot(131), plt.imshow(img, 'gray'), plt.title('Original_image')
plt.subplot(132), plt.imshow(equ, 'gray'), plt.title('equalizeHist_image')
plt.subplot(133), plt.imshow(cll, 'gray'), plt.title('CLAHE_image')
plt.show()
在本篇文章中,主要介绍了直方图均衡化的概念,并利用它来提高图像的对比度,利用OpenCV的Python接口实现了全局直方图均衡化(HE)及对比度受限的自适应直方图均衡化(CLAHE),还介绍了这两种直方图均衡化的优劣势。
如果文章对你有帮助,欢迎一键三连哦~~