一、引言
在《数字图像处理:直方图均衡(Histogram Equalization)的原理及处理介绍 》中介绍了数字图像处理中应用直方图均衡进行图像增强的原理、应用示例,本文将介绍对应处理方法在OpenCV中的实现以及基于OpenCV-Python的应用样例。
二、直方图均衡的作用
在前面的博文中已经介绍了直方图均衡的作用,OpenCV中也简单进行了说明,相当简洁,在此从与前文稍有不同的另外一种方式介绍一下。
考虑如下代表同一个图像内容的两个不同灰度直方图:
左边的图,其像素值仅局限于某个特定的值范围。例如,较亮的图像将把所有像素限制在高值上。但是一幅好的图像应该会有来自图像所有区域的像素。因此需要将这个直方图拉伸到两端(如上边右边图所示),这就是直方图均衡化的作用。这通常会提高图像的对比度来增强图像。
三、OpenCV中基于Numpy实现的直方图均衡的样例代码
3.1、代码样例
下面的代码是OpenCV4.1中文官方文档提供的、基于直方图均衡原理使用Numpy实现的图像直方图均衡代码示例:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('wiki.jpg',0)
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_normalized = cdf * float(hist.max()) / cdf.max() #将值的区间上限限制在直方图像的y轴最大值范围内,确保累积曲线和直方图的值域范围对应
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()
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]
3.2、样例代码解读
以上代码先用numpy的histogram函数生成图像的直方图矩阵,该函数的两个返回值说明如下:
- bins:numpy一维数组,各直方图组的下界以及最后一组的上界,如像素值∈[0,255]区间,划分为8个组,则bins的返回值为有9个元素的一维数组[0,32,64,96,128,160,192,224,256],其中256是最后一组的上界
- hist:numpy一维数组,返回的直方图数据,即灰度值落在各bins区间的像素数目,第一个值对应第一组bins的像素数,如图像数组为:
img = np.array([[1, 0, 3], [4, 0, 5], [2, 4, 3]], dtype=np.uint8)
,则hist数据为[9,0,0,0,0,0,0,0]
得到直方图数组hist后,计算每个像素值的所有小于等于该像素值的像素个数累计值(即直方图的积分,其来由请参考《数字图像处理:直方图均衡(Histogram Equalization)的原理及处理介绍 》),这个用numpy的cumsum累积函数即可以实现。
累计值(直方图积分)数组生成后为了将积分曲线和直方图数据共用坐标系且方便对比,将累计值的区间压缩到了hist纵坐标的空间内,同时利用matplotlib绘制出该图像的直方图和累积值对应的曲线。如图:
最后使用掩码数组对每个像素值对应的累计像素个数进行均衡变换,得到均衡后的图像img2。下面是上图的均衡后结果图像及对应直方图:
3.3、由样例延伸得到的结论
上图的样例图像总体比较亮,均衡后改变的只是对比度。对于图像整体比较暗的情况,直方图均衡能得到类似的效果。这种效果的本质是使所有图像具有相同的照明条件。
3.4、关于直方图均衡算法
本案例的直方图计算对应的计算公式看起来与在《数字图像处理:直方图均衡(Histogram Equalization)的原理及处理介绍 》介绍的有所不同,在该文中介绍的均衡后的图像像素值与均衡前的像素值的映射关系为:
其中rk表示原图像中灰度值为k的灰度值,sk表示经过变换后rk映射到均衡后图像的灰度值。
从上面的代码可以看出,该算法与此至少看起来有些不同,也确实不同。具体的分析对比老猿在付费专栏的文章中《数字图像处理:OpenCV直方图均衡算法研究及模拟实现》进行了详细分析。
四、OpenCV直方图均衡函数equalizeHist详解
4.1、概述
上面第三部分介绍的直方图均衡处理的算法及案例,是OpenCV官方提供的基于Numpy实现的直方图均衡处理的样例。该样例是为了说明OpenCV的直方图均衡算法,实际上OpenCV提供了直方图均衡的一体化函数equalizeHist,经老猿测试上面样例的算法与OpenCV的直方图均衡函数equalizeHist的实现细节方面还是有些差异。这些内容老猿在《数字图像处理:OpenCV直方图均衡算法研究及模拟实现》中进行介绍,本部分主要详细介绍OpenCV-Python提供的直方图均衡函数equalizeHist。
4.2、equalizeHist语法说明
- 语法
equalizeHist函数的语法非常简单,调用语法如下:dst = cv.equalizeHist( src[, dst] )
- 参数及返回值说明
参数src为要均衡的输入图像,必须是8bit单通道图像,即灰度图,dst是原图像大小相等经过直方图均衡处理后的输出图像,参数dst可以不传入。 - 函数处理过程
该函数对应算法使图像的亮度正常化并增加图像的对比度,具体处理步骤如下:
√ 计算输入图像的直方图H;
√ 对直方图H进行归一化,使直方图bins的总和为255;
√ 计算直方图的积分(integral of the histogram),计算公式为: - √ 使用H′作为查找表( look-up table)对图像进行变换,具体像素值的变换公式为:
dst(x,y)=H'(src(x,y))
4.3、案例
下面读入2幅经典的直方图均衡使用的案例图像进行均衡处理。相关代码如下:
import cv2
from opencvPublic import preparePreviewImg,previewImgList,readImgFile,cmpMatrix,print2DMatrix
def test():
img1 = readImgFile(r'f:\pic\valley.png',True)
img2 = readImgFile(r'f:\pic\火星卫星.JPG',True)
imgEqu1 = cv2.equalizeHist(img1)
imgEqu2 = cv2.equalizeHist(img2)
preparePreviewImg('输入图像:山谷',img1,fontSize=40,color=(255,255,255))
preparePreviewImg('山谷图均衡效果',imgEqu1,fontSize=40,color=(255,255,255))
preparePreviewImg('', None, fontSize=36, color=(255,255,255))
preparePreviewImg('输入图像:火星卫星',img2,fontSize=40,color=(255,255,255))
preparePreviewImg('火卫图均衡效果', imgEqu2, fontSize=40, color=(255,255,255))
previewImgList()
test()
以上代码中,opencvPublic是老猿常用的图像处理自定义方法的公用模块,本文使用的自定义公用模块函数preparePreviewImg,previewImgList,readImgFile,cmpMatrix,print2DMatrix,其功能请参考《OpenCV-Python图形图像处理:自用的一些工具函数功能及调用语法介绍》中的介绍。
下面是最后预览图像:
从上面两张样例图及其均衡后结果图像对比可以看到,无论输入图像光线是否充足,对于对比度不是很明显的图像,直方图均衡都能有效增强图像的对比度,并使得图像都能模拟出相同的照明条件。
五、小结
本文介绍了OpenCV官方提供的直方图均衡原理、算法及算法实现样例,以及OpenCV-Python中的直方图均衡函数equalizeHist的调用语法、参数及返回值说明、处理过程描述,最后提供了一个使用equalizeHist函数对经典的两张直方图均衡样例图的处理代码和处理效果。通过相关内容的介绍,有助于大家理解直方图均衡的原理、算法及OpenCV中的处理方法。