本教程将学习如下内容:
- 学习使用OpenCV和Numpy函数查找直方图
- 介绍两个关键函数:
cv2.calcHist()
、np.hostogram()
扩展阅读:
理论基础
直方图是什么呢?您可以将直方图视为一个图形或绘图,可以对图像的强度分布有一个总体的了解。 这是一个X轴上像素值(范围从0到255,并不总是如此)和Y轴上图像中相应像素数的图。
这只是理解图像的另一种方式。通过查看图像的直方图,可以直观地了解该图像的对比度、亮度、强度分布等。 几乎所有的图像处理工具都提供直方图功能。 以下是剑桥色彩网站上的图片,建议访问该网站以获取更多详细信息。
可以看到图像及其直方图。(记住,此直方图是为灰度图像绘制的,而不是为彩色图像绘制的)。 直方图的左侧区域显示图像中较暗像素的数量,右侧区域显示较亮像素的数量。 从直方图中可以看到暗区比亮区多,中间色调的数量(中间范围的像素值,比如127左右)非常少。
查找直方图
现在我们对直方图有了一个概念,可以研究如何找到它。OpenCV和Numpy都内置了此功能。 在使用这些函数之前,需要了解一些与直方图相关的术语。
BINS:上面的直方图显示了每个像素值的像素数,即从0到255。 即需要256个值来显示上面的直方图。但是如果不需要分别找到所有像素值的像素数,而是需要找到像素值区间内的像素数呢? 例如,需要找到0到15之间的像素数,然后是16到31。。。,240至255。 只需要16个值来表示直方图。这就是OpenCV教程中关于直方图的示例所示。
因此,只需将整个直方图拆分为16个子部分,每个子部分的值就是其中所有像素计数的总和。 每个子部分称为“BIN”。在第一种情况下,仓的数量为256(每个像素一个),而在第二种情况下只有16个。 BINS在OpenCV文档中由术语histSize表示。
DIMS:这是收集数据的参数数量。在这种情况下,只收集关于一件事的数据,即强度值。所以这里是1。
RANGE:这是要测量的强度值的范围。通常,它是[0,256]
(所有强度值)。
OpenCV 中的直方图计算
所以现在使用cv2.calcHist()
函数来查找直方图。先熟悉一下函数及其参数:
cv2.calcHist(images, channels, mask, histSize, ranges\[, hist\[,accumulate\]\])
images
:它是uint8或float32类型的源图像,应该放在方括号内,即[img]
。channels
:它也在方括号中给出,是计算直方图的通道指数。例如,如果输入是灰度图像,则其值为[0]
。对于彩色图像,可通过[0]
、[1]
或[2]
分别计算蓝色、绿色或红色通道的直方图。mask
:遮罩图像。为了找到完整图像的直方图,将其设置为“无”。但是,如果想找到图像特定区域的直方图,必须为此创建一个掩模图像,并将其作为掩模。(稍后将展示一个示例。)histSize
:这代表BIN计数。需要放在方括号内。对于全尺寸,需要传递[256]
。ranges
:这是范围,通常为[0256]
。
那么,让我们从一个示例图像开始。只需在灰度模式下加载图像并找到其完整的直方图。
import cv2
import numpy as np
img = cv2.imread('/data/cvdata/home.jpg',0)
hist = cv2.calcHist([img],[0],None,[256],[0,256])
hist,bins = np.histogram(img.ravel(),256,[0,256])
hist
和之前计算的一样。但是bin
将有257个元素,因为Numpy将bin计算为0-0.99、1-1.99、2-2.99等。
因此,最终的射程将是255-255.99。为了表示这一点,还在箱子的末尾添加了256个。
但我们不需要256。255就足够了。
Numpy还有另一个函数np.bincount()
,它比np.hostogram()
快得多(大约10倍)。
所以对于一维直方图,可以更好地尝试一下。
别忘了在np.bincount
中设置minlength=256
。例如,hist=np.bincount(img.ravel(),minlength=256)
。
注意:
OpenCV函数比np.hostogram()
快(大约40倍)。所以坚持使用OpenCV函数。
那么如何绘制直方图呢?
绘制直方图
有如下两种方法,
- Short Way:使用 Matplotlib 绘图函数
- Long Way:使用 OpenCV 绘图功能
使用Matplotlib
Matplotlib附带了一个直方图绘制函数:Matplotlib.pyplot.hist()
它直接找到直方图并绘制出来,不需要使用calcHist()
或np.hostogram()
函数来查找直方图。
请参阅下面的代码:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('/data/cvdata/home.jpg',0)
plt.hist(img.ravel(),256,[0,256]); plt.show()
/tmp/ipykernel_992/175618049.py:2: MatplotlibDeprecationWarning: Passing the range parameter of hist() positionally is deprecated since Matplotlib 3.9; the parameter will become keyword-only in 3.11. plt.hist(img.ravel(),256,[0,256]); plt.show()
如下图所示:
或者可以使用matplotlib的法线图,这对BGR图很有帮助。 为此,需要先找到直方图数据。请尝试以下代码:
%matplotlib inline
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('/data/cvdata/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()
img = cv2.imread('/data/cvdata/home.jpg',0)
生成掩膜。
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img,img,mask = 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()
如图所示,直方图中蓝线代表全图像素分布,绿线则对应掩膜区域的统计结果。