官网参加https://docs.opencv.org/3.4.1/d1/db7/tutorial_py_histogram_begins.html
什么是是直方图?
直方图可以理解为是一个图表,通过它可以对图像的灰度分布有一个全面的了解。
它的X轴上是像素值(一般为0-255,不总是这样),Y轴是和X轴对应像素点的数量。
直方图是对图像的另外一种理解方式。
通过图像的直方图,你可以直观的了解图像的对比度、亮度、强度分布等。现在所有的图像处理工具都提供了直方图功能。
上图来自 www.cambridgeincolour.com/tutorials/histograms1.htm ,强烈推荐访问该网站获取更多信息。
观察上图(注意:直方图是灰度图,不是彩色图)。左边区域显示的是图像中较暗像素的数量,而右边区域显示的图像中明亮像素的数量。从直方图中可以发现,图像中较暗区域比明亮区域要大,而中间部分的像素数量很少。
1.获得直方图
我们已经知道了什么是直方图,现在可以研究如何获得直方图。opencv和numpy都有内置函数获得直方图。在使用这些函数之前,我们先来了解一下直方图的相关术语。
BINS :上面的直方图显示了每个像素值(pixel value)对应的像素数量。例如:像素值是从0到255,你就需要255个数来显示上面的直方图。但是,如果你不需要获得所有像素值对应的像素数量,而是只想知道2个像素值之间像素数量,该怎么办呢?例如:你需要知道0-15之间像素数量,然后是16-31…最后是240-255。此时你只需要16个值来绘制直方图。这个例子就在下面链接中 https://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html#histogram-calculation
所以,你只需要把直方图分成16个子部分,每个子部分的值就是其中所有像素数量之和。这里的子部分,我们就称为BINS。上面提到的第1个例子中有256个BIN,第2个例子中有16个BIN。在OpenCV 的文档中用histSize表示BINS。
DIMS : 我们收集数据的参数数目。在本例中,我们对收集到数据只考虑1件事情,灰度值。所以DIMS=1。
RANGE : 你要测量的灰度值范围。一般[0,255],也就是所有的灰度值。
1-1.opencv中的直方图计算
opencv提供cv.calcHist() 函数获得直方图。
hist = cv.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
- images:原图像(类型为uint8 或float32)。当传入函数时应该用中括号[] 括起来,例如:[img]。
- channels : 需要用中括号括起来。它是我们计算直方图的通道索引。如果是灰度图,值为[0];如果是彩色图,值为[0], [1] or [2],代表计算直方图的蓝色,绿色或者红色通道。
- mask :图像遮罩。如果要或者整个图像的直方图,就设置为None。如果要获得图像中某个区域的直方图,你就必须创建1个遮罩并使用它。
- histSize : BINS数量。要用中括号括起来。例如:全部,[256]。
- ranges :像素值范围。通常为[0,256]
例,获得下图灰度图的直方图
代码
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('result.jpg',0)
hist = cv2.calcHist([img],[0],None,[256],[0,256])
plt.plot(hist)
plt.xlim([0,256])
plt.show()
hist 是一个256x1的数组,每一个值代表了与次灰度值对应的像素点数目。
1-2.numpy中的直方图计算
numpy中提供np.histogram()函数获得直方图。
例,获得直方图
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('result.jpg',0)
hist,bins = np.histogram(img.ravel(),256,[0,256])
plt.plot(hist)
plt.xlim([0,256])
plt.show()
和opencv中对比,区别就是下面这句
hist,bins = np.histogram(img.ravel(),256,[0,256])
hist 与opencv计算一样。计算的一样。但是这里的bins是257,因为Numpy计算bins 的方式为:0-0.99,1-1.99,2-2.99 等。所以最后一个范围是255-255.99。为了表示它,所以在bins 的结尾加上了256。但是我们不需要256,到255就够了。
numpy还有另外一个函数np.bincount() ,它的运行速度是histogram()的10倍。如果是1维直方图,请使用np.bincount()。使用别忘记设置minlength = 256。例如
hist = np.bincount(img.ravel(),minlength=256)
注意:opencv的calcHist()要比np.histgram()快40倍。
2.绘制直方图
有2种方法可以绘制直方图
a.简便方法:使用Matplotlib的绘图函数
b.复杂方法:使用opencv中的绘图函数
2-1.使用Matplotlib
Matplotlib提供绘图函数matplotlib.pyplot.hist()。这个函数可以直接统计并绘制直方图。不再需要使用calcHist()或者np.histogram()函数。
例1,使用Matplotlib绘制直方图
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('result.jpg',0)
plt.hist(img.ravel(),256,[0,256])
plt.show()
直方图如上。
你也可以使用Matplotlib的普通绘画功能,这个对于bgr图非常好。不过,首先必须找到直方图数据。
例2,使用Matplotlib的普通绘画函数绘制bgr图像的直方图
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('result.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-2.使用opencv
你也可以使用opencv的cv.line() 和cv.polyline()函数来画出上面的图。
OpenCV-Python2有官方的例子,samples/python/hist.py。
3.掩膜应用
我们可以使用cv.calcHist() 函数去找到整个图像的直方图。那么如何获得局部区域的直方图呢?可以创建一个掩膜图像,需要获得直方图区域设置为白色,剩余部分设置为黑色。然后把这个掩膜图像传入函数。
例,创建掩膜
# -*- coding: cp936 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('result.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()
结果如上图。蓝色是整个图像的直方图,红色是局部区域直方图。