学习目标
- 计算直方图,使用OpenCV和Numpy.
- 画直方图,使用OpenCV和Matplotlib.
- 学习函数:
cv2.calcHist()
,np.histogram()
.
理论知识
直方图是什么?可以将直方图看成是图表,可以直观给出图像的整体像素值的分布情况。通常绘制直方图的X轴
是256级(0-255)灰度,Y轴
统计该灰度下的像素数量。
直方图是理解图像的另外一种方式。通过直方图,可以直观上获得对比度,亮度,强度分布等。当前,几乎所有的图像处理工具提供了直方图特征。下图来自 Cambridge in Color website:
从图中可以看出(统计直方图时,是灰度图像),直方图的左边区域的像素亮度较暗,右边的区域较亮。并且,暗的像素大于亮的像素,处于中间的像素(127)较少。
直方图计算
通过上面的介绍,我们对直方图有直观的概念,下面将研究如何计算直方图。OpenCV和Numpy都可以计算。使用内置函数计算之前,我们需要理解直方图的相关原理。
BINS:上面的图显示了每一个像素值的统计数量,比如0-255。但是,通常我们不需要每一个像素值的统计数量,而是一个像素区间的统计数量,比如,[0-15],[16-31],…, [240-255]. Y轴只需16个值表达。其中每一个子区间称之为 “BIN”,在OpenCV中,BINS用histSize
参数表示。
DIMS:用于收集数据的参数数量,这里我们只用灰度图的灰度值作为统计参数,所以dims=1
.
Range:统计的像素强度的范围,一般情况下,[0,256],是所有的强度值。
1. Histogram Calculation in OpenCV
现在,我们使用cv2.calcHist()
计算直方图,该函数的定义如下:
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
"""
功能:计算直方图
images:输入图像,类型为uint8或者float32. 输入的时候需要添加中括号,[img].
channels:同样需要添加中括号,该值是指定计算哪个通道的直方图。比如,如果输入的是
灰度图像,那么值为[0]。彩色图像,可以传入[0],[1],[2]分别计算blue,green,red通道
的直方图统计。
mask:掩码图像。如果计算全图的直方图,该参数的值为None。但是,如果计算图像中某个
区域的直方图统计,那么需要掩码图像。
histSize:表示BINS的个数,也需要有中括号,对于全图,则需传入[256].
ranges:像素值的范围,通常为[0,256].
"""
下面以一个例子展示使用:
img = cv2.imread('home.jpg',0)
hist = cv2.calcHist([img],[0],None,[256],[0,256])
2. Histogram Calculation in Numpy
Numpy也可以计算直方图,np.histogram()
,使用如下:
hist,bins = np.histogram(img.ravel(),256,[0,256])
返回值跟CV
计算的返回值一样。因为Numpy计算BINS为,0-0.99,1-1.99,2-2.99,…,255-255.99 等,所以BINS是257个值。
- numpy还有另外一个函数,np.bincount(),比np.histogram()快10倍以上。所以一维的直方图,可以尝试。但是要设置
minlength=256
。比如,hist = np.bincount(img.ravel(),minlength=256)
。- OpenCV函数比
np.histogram()
快40倍。
Plotting Histogram
两种方式可以画图:
- Short Way:使用Matplotlib。
- Long Way:OpenCV。
1. Using Matplotlib
Matplotlib可以画直方图的函数为:Matplotlib.pyplot.hist(),可以直接得到直方图,然后绘制。代码如下:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('home.jpg', 0)
plt.hist(img.ravel(), 256, [0,256])
plt.show()
或者可以使用如下的方式绘制直方图:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('home.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.show()
运行结果如下:
2. Using OpenCV
OpenCV也可以达到上述的效果,需要使用cv2.line()
,或者cv2.polyline()
.
Application of Mask
有时候,我们需要计算图像部分区域的直方图。那么我们需要创建一个掩码区域,实例如下:
img = cv2.imread('home.jpg',0)
# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img,img,mask = mask)
# Calculate histogram with mask and without mask
# Check third argument for mask
hist_full = cv2.calcHist([img],[0],None,[256],[0,256])
hist_mask = cv2.calcHist([img],[0],mask,[256],[0,256])
plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask,'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0,256])
plt.show()
运行结果如下: