直方图基于OpenCV4
图像直方图是图像的重要统计特征,是表示数字图像中的每一灰度级与该灰度级出现的频数(该灰度级的数目)间的统计关系。按照直方图定义可表示为:
P
(
r
k
)
=
n
k
N
(
k
=
0
,
1
,
2
,
⋯
,
L
−
1
)
P(r_k)=\frac{n_k}{N} \qquad (k=0,1,2,\cdots ,L-1)
P(rk)=Nnk(k=0,1,2,⋯,L−1)
式中:
N
N
N为一幅图像的总像素数;
n
k
n_k
nk为第
k
k
k级灰度的像素数;
r
k
r_k
rk为第
k
k
k级灰度级;
L
L
L为灰度级数;
P
(
r
k
)
P(r_k)
P(rk)为该灰度级出现的概率。
直方图具有以下性质:
(1)直方图是图像的一维信息描述
在直方图中,由于他只能反应图像的灰度范围、灰度级的分布、整幅图像的平均亮度等信息,而未能反应图像某一灰度值像素所在的位置,从而失去了图像的二维特征。
(2)灰度直方图与图像的映射关系并不唯一
该性质可基于性质一,由于直方图不包含空间位置信息,一张灰度直方图可对应多张图像,但一张图像只能对应一张灰度直方图。
例如,上述两张图象在灰度像素数目相同的情况下,对应同一张灰度直方图。
(3)直方图满足叠加性
如果已知直方图被分割成几个区域后的各个区域的直方图,则把他们加起来就可得到这个图像的直方图。
我们常常基于opencv进行直方图的计算和绘制。
OpenCV4提供了函数cv.calcHist(),该函数能够统计出图象中的每个像素的个数:
hist=cv.calcHist(images,channels,mask,histSize,ranges,hist,accumulate)
- images:待统计直方图的图像数组,数组中所有的图像应具有相同的尺寸和数据类型,并且数据类型只能是CV_8U、CV_16U和CV_32F三种中的一种,但是不同图像的通道数可以不同。
- channels:需要统计的通道索引数组,第一个图像的通道索引从0到images[0].channels()-1,第二个图像通道索引从images[0].channels()到images[0].channels()+ images[1].channels()-1,以此类推。
- mask:可选的操作掩码,如果是空矩阵则表示图像中所有位置的像素都计入直方图中,如果矩阵不为空,则必须与输入图像尺寸相同且数据类型为CV_8U。
- hist:输出的统计直方图结果,是一个dims维度的数组。
- ranges:每个图像通道中灰度值的取值范围
- accumulate:是否累积统计直方图的标志,如果累积(true),则统计新图像的直方图时之前图像的统计结果不会被清除,该同能主要用于统计多个图像整体的直方图。
调用该函数后可得到图像中任意灰度级的像素频数,我们可依托Matplotlib库对生成的数列绘制灰度直方图。
import cv2 as cv
import sys
import numpy as np
from matplotlib import pyplot as plt
np.set_printoptions(suppress=True)
if __name__=='__main__':
#这里读入的三通道的图像,我们计算灰度直方图的时候需要每个通道单独处理
image=cv.imread('OIP-C.jfif')
cv.imshow('image',image)
if image is None:
print("Failed to read image")
sys.exit()
color=('r','g','b')
for i,col in enumerate(color):
#这里hist为每个通道的像素频数,如果需要概率值,需要除以每个通道的像素总数
hist = cv.calcHist([image], [i], None, [256], [0, 256])*3/image.size
plt.plot(hist,color=col)
#_,_,_=plt.hist(x=image.ravel(),bins=256,range=[0,256])
cv.imshow('image',image)
plt.show()
cv.waitKey(0)
cv.destroyAllWindows()
对于彩色图像来说,如果仅仅考虑灰度直方图的话不能满足我们的需求,因此我们通常考虑图像的色调和饱和度,并根据则两个特征进行2D直方图统计。
类似于一维直方图计算,2D直方图的计算同样使用函数cv.calcHist()函数,但在计算之前,需要将图像从BGR格式转化为HSV格式,且其中的几个参数需要进行相应的修改。其中,channels表示需要统计H和S两个通道,值为[0,1];histSize为[180,256],其中180代表H通道,256代表S通道;ranges为[0,180,0,256]
import cv2 as cv
import sys
import numpy as np
from matplotlib import pyplot as plt
np.set_printoptions(suppress=True)
if __name__=='__main__':
#这里读入的三通道的图像,我们计算灰度直方图的时候需要每个通道单独处理
image=cv.imread('OIP-C.jfif')
cv.imshow('image',image)
image_hsv=cv.cvtColor(image,cv.COLOR_BGR2HSV)
if image is None:
print("Failed to read image")
sys.exit()
#这里hist为每个通道的像素频数,如果需要概率值,需要除以每个通道的像素总数
hist = cv.calcHist([image_hsv], [0,1], None, [180,256], [0,180,0,256])
plt.imshow(image_hsv)
#_,_,_=plt.hist(x=image.ravel(),bins=256,range=[0,256])
plt.imshow(hist,interpolation='nearest')
plt.show()
cv.waitKey(0)
cv.destroyAllWindows()
生成2D直方图如下: