【第20节】OpenCV直方图
目标
使用OpenCV或Numpy函数计算直方图
使用OpenCV或Matplotlib函数绘制直方图
将要学习的函数有:CV2.calcHist(),np.histogram()
原理
什么是直方图呢?通过直方图你可以对整幅图像的灰度分布有一个整体的了解。 直方图的x 轴是灰度值(0到255 ), y轴是图片 中具有同一个灰度值的点的数目。
直方图其实就是对图像的另一种解释。以下图为例,通过直方图我们可以对图像的对比度,亮度,灰度分布等有一个直观的认识。几乎所有的图像处理软件都提供了直方图分析功能。
下图来自Cambridge(https://www.cambridgeincolour.com/tutorials/histograms1.htm),强烈推荐你到这个网站了解更多知识。

让我们来一起看看这幅图片和它的直方图吧。(要记住,直方图是根据灰度图像绘制的,而不是彩色图像)。直方图的左边区域像是了暗一点的像素数量,右侧显示了亮一点的像素的数量。从这幅图上你可以看到灰暗的区域比两的区域要大,而处于中间部分的像素点很少。
1、统计直方图
现在我们知道什么是直方图了,那怎样获得一副图像的直方图呢?OpenCV和Numpy都有内置函数做这件事。在使用这些函数之前我们有必要想了解一下直方图相关的术语。
BINS:上面的直方图显示了每个灰度值对应的像素数。如果像素值为0
到255,你就需要256个数来显示上面的直方图。但是,如果你不需要知道每一个像素值的像素点数目的,而只希望知道两个像素值之间的像素点数目怎么办呢?举例来说,我们想知道像素值在0到15之间的像素点的数目,接着是16到3.....240到255。我们只需要16个值来绘制直方图。OpenCV Tutorials on histograms(https://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html#histogram-calculation)中例子所演示的内容。
那到底怎么做呢?你只需要把原来的256个值等分成16小组,取每组的总和。而这里的每一个小组就被成为BIN。第一个例子中有256个BIN,第二个例子中有16个BIN。在OpenCV的文档中用histSize表示BINS。
DIMS:表示我们收集数据的参数数目。在本例中,我们对收集到的数据.
只考虑一件事:灰度值。所以这里就是1。
RANGE:就是要统计的灰度值范围,-般来说为[0, 256],也就是说所
有的灰度值
使用OpenCV统计直方图函数 CV2.calcHist可以帮助我们统计一幅图像的直方图。我们一起来熟悉一下这个函数和它的参数:
1. images:原图像(图像格式为uint8或float32 )。当传入函数时应该用中括号[]括起来,例如: [img]。.
2. channels:同样需要用中括号括起来,它会告诉函数我们要统计那幅图像的直方图。如果输入图像是灰度图,它的值就是[0];如果是彩色图像的话,传入的参数可以是[0], [1], [2] 它们分别对应着通道B, G, R。
3. mask:掩模图像。要统计整幅图像的直方图就把它设为None。但是如果你想统计图像某一部分的直方图的话, 你就需要制作一个掩模图像, 并使用它。(后边有例子)
4. histSize:BIN的数目。也应该用中括号括起来,例如: [256]。
5. ranges:像素值范围,通常为[0, 256]
让我们从一副简单图像开始吧。以灰度格式加载一幅图像并统计图像的直方图。
hist是一个256x1的数组,每一个值代表了与次灰度值对应的像素点数目。使用Numpy统计直方图Numpy 中的函数np.histogram()也可以帮我们统计直方图。你也可以尝试一下下面的代码:
hist与上面计算的一样。但是这里的bins是257,因为Numpy计算bins的方式为: 0-0.99,1-1.99,2-2.99等。所以最后一个范围是255-255.99。为了表示它,所以在bins的结尾加上了256。但是我们不需要256,到255就够了。
其他: Numpy还有一个函数np.bincount(, 它的运行速度是np.histgram的十倍。所以对于一维直方图,我们最好使用这个函数。使用np.bincount 时别忘了设置minlength=256。例如,hist= np.bincount(img.ravelO, minlength= 256)
注意: OpenCV的函数要比np.histgram()快40倍。所以坚持使用OpenCV函数。
2、绘制直方图
有两种方法来绘制直方图:
1)Short Way(简单方法):使用 Matplotlib 中的绘图函数。
2)Long Way(复杂方法):使用 OpenCV 绘图函数
使用Matplotlib
Matplotlib 中直方图绘制函数:matplotlib.pyplot.hist(),示例如下:
你会得到下面这样一幅图:

或者你可以只使用matplotlib的绘图功能,这在同时绘制多通道( BGR )的直方图,很有用。但是你首先要告诉绘图函数你的直方图数据在哪里。运行一下下面的代码:
结果:

使用OpenCV
使用Opencv自带的绘图函数比较麻烦,这里不做推荐,给出示例代码,有兴趣的自己可以研究:
结果显示:

3、使用掩膜
要统计图像某个局部区域的直方图只需要构建一副掩模图像。将要统计的部分设置成白色,其余部分为黑色,就构成了一副掩模图像。然后把这个掩模图像传给函数就可以了。
结果如下,其中蓝线是整幅图像的直方图,橘黄色是进行掩膜之后的直方图。
