OpenCV学习笔记(8)——直方图与掩膜
- 蒙面西红柿
- 2,059
1. 直方图的概念
- 横坐标:图像中各个像素点的灰度级
- 纵坐标:具有该灰度级的像素个数

直方图是根据灰度图像绘制的,而不是彩色图像。直方图的左边区域像是了暗一点的像素数量,右侧显示了亮一点的像素的数量。从这幅图上你可以看到灰暗的区域比两的区域要大,而处于中间部分的像素点很少。

在归一化直方图中:
横坐标代表图中个个像素点的灰度级;纵坐标代表出现这个灰度级的概率。
2. Matplotlib绘制直方图
Python中主要使用matplotlib库来绘制直方图。
import matplotlib.pyplot as plt
#hist(数据源,像素集)
#数据源:图像,必须是1维数组。(将图像转化为一维数组)。使用b = a.ravel() 将二维转化为一维。
#像素集:一般是256,指[0, 255]
plt.hist(img1,ravel(), 256)
plt.show()
3. OpenCV统计直方图
#hist = cv2.calcHist(img1, channels, mask, histSize, ranges[,accumulate])
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
print(type(hist))
#<class 'numpy.ndarray'>
print(hist.size)
#256
print(hist.shape) #(256, 1)
print(hist) #每一个灰度级对应的灰度点数量
- hist:返回的直方图,是一个二维数组。
- channels:指定通道。灰度图的值为[0],彩色图像[0], [1], [2]对应BGR
- mask: 掩码图像。统计整个直方图时设为None,只计算一部分时需要掩码图像。
- histSize: BINS的数量,如[256]
- ranges: 像素值的范围。一般是[0, 255]
- accumulate: 累计默认值为false, 如果为true,则在开始分配时不清零。该参数允许从多个对象中计算单个直方图,或者实时更新直方图。多个直方图的累计结果,用于对一组图像计算直方图。
4. 绘制OpenCV统计直方图
使用Matplotlib调用第三节中OpenCV生成的数据,作为y值绘制。
#灰度图
hist = cv2.calcHist([img1], [0], None, [256], [0, 255])
plt.plot(hist, color = 'b')
plt.show()
#BGR图
histb = cv2.calcHist([img1], [0], None, [256], [0, 255])
histg = cv2.calcHist([img1], [1], None, [256], [0, 255])
histr = cv2.calcHist([img1], [2], None, [256], [0, 255])
plt.plot(histb, color = 'b')
plt.plot(histg, color = 'g')
plt.plot(histr, color = 'r')
plt.show()
5. 掩膜原理
OpenCV中很多函数都带有一个mask参数,mask被称为掩模。图像掩模一般用来对处理的图像(全部或者局部)进行遮挡,来控制图像处理的区域或处理过程。
掩膜黑色部分为不透明,白色部分为透明。将掩膜放在原始图像上,黑色部分被覆盖掉,白色部分映衬原始图像。结果图像只有白色部分,黑色被覆盖。

因此在OpenCV中,掩模一般是小于等于源图像的单通道矩阵,掩模中的值分为两种0和非0。以当mask的值不为0,则将源图像拷贝到目标图像,当mask为0,则不进行拷贝,目标图像保持不变。

通过使用OpenCV中的bitwise_and进行与操作,生成掩膜图像:
mask = np.zeros(img1.shape, np.uint8)
mask[100:200, 100:200] = 255
img2 = cv2.bitwise_and(img1, mask)

6. 使用掩膜构建直方图
#第一步,创建一个全0数组,大小为img大小
mask = np.zeros(img1.shape, np.uint8)
#第二部,在图像中创建透明部分。(其实就是将数组中部分值修改为255)
mask[200:400, 200:400] = 255
#第三步,使用第3步的统计直方图函数使用掩膜
hist = cv2.calcHist([img1], [0], mask, [256], [0, 255])
注意:
- 掩膜的维度应该与图像相同,若是BGR图像则应该使用cv2.IMREAD_GRAYSCALE读取为灰度图。
- 掩膜截取大小不能超过掩膜数组大小。(听起来很幼稚但就是犯了这种低级错误。。)
7. 直方图均衡化
7.1 直方图均衡化原理
直方图均衡化(Histogram Equalization)是一种增强图像对比度(Image Contrast)的方法,其主要思想是将一副图像的直方图分布变成近似均匀分布,从而增强图像的对比度。直方图均衡化虽然只是数字图像处理(Digital Image Processing)里面的基本方法,但是其作用很强大,是一种很经典的算法。
均衡化的过程主要分为三步:
- 计算累计直方图
- 将累计直方图进行区间转换
- 在累计直方图中,概率相近的原始值会被处理为相同的值。(因为肉眼很难区分)
假设有图像:

得图像的统计信息如下图所示,并根据统计信息获得累计直方图:

根据统计信息完成灰度值映射(0-255最大值255,所以乘以255):

映射后的像素分布如下所示:

虽然两者相似,但是右侧的分布更均衡。相邻像素级概率与高概率近似相等(因为肉眼很难分辨)。
应用场合:
- 医疗图像处理
- 车牌识别
- 人脸识别
7.2 直方图均衡化函数
img2 = cv2.equalizeHist(img1)
效果:

8. Matplotlib小技巧
8.1 图像分屏
在一个图像里显示多个窗口


#subplot(nrows, ncols, plot_number)
#当每一个参数都小于10时,可以省略逗号
plt.subplot(221),plt.hist(img1.ravel(), 256)
plt.subplot(222),plt.hist(img2.ravel(), 256)
plt.subplot(223),plt.imshow(img1)
plt.subplot(224),plt.imshow(img2)
效果:

可以发现,排版正确了但是读取的图像发绿。具体解决方法在下一节介绍。
8.2 显示图像
#imshow(X, cmap = None)
#X:要绘制的图像
#colormap:颜色图谱,默认为RGB(A)
#显示灰色图像
#第一步,载入灰度图像
img1 = cv2.imread("Lenna.jpg", cv2.IMREAD_GRAYSCALE)
#或先载入彩色然后转换
img1 = cv2.imread("Lenna.jpg")
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
#第二步,显示灰度图像,使用plt.axis('off')关闭坐标轴
plt.imshow(img1, cmap = plt.cm.gray),plt.axis('off')
#显示彩色图像
#第一步,载入以后分割通道
b, g, r = cv2.split(img1)
#第二部,按照RGB顺序重组图片
img2 = cv2.merge([r, g, b])
#第三步,显示RGB图像
plt.imshow(img2),plt.axis('off')
灰度图像:cmap = plt.cm.gray
彩色图像:默认为RGB(A), 如果使用OpenCV读取图像,默认空间为BGR,需要调整色彩空间为RGB。
文章评论