目录
- 1 直方图的计算
- 2 直方图的绘制
- 2.1 cv.line()和cv.polylines()
- 2.2 plt.hist()
- 3 2D 直方图
- 3.1 cv.calcHist()
- 3.2 plt.imshow()
直方图是是图像处理中非常重要的像素统计工具,不再表征任何的图像纹理信息,而是表示像素的统计特性。由于同一物体无论是旋转还是平移,在图像中都应具有相同的灰度值,因此直方图具有
平移不变性、缩放不变性等优点。可以通过这些特性来查看图像整体的变化形式,例如图像是否变暗、图像灰度值主要集中在哪些区域等。在这些特定条件下,可以利用直方图进行简单图像的识别,例如
数字、字母的识别。
1 直方图的计算
在图像处理中,直方图就是统计图像中每个灰度值的个数之后,将回到沪指作为横轴以灰度值的个数,或者所占比值作为纵轴绘制的统计图。通过直方图,我们可以看出图像中哪些灰度值的数目较多。通常情况下,灰度值代表亮暗程度,因此通过直方图可以分析图像亮暗对比度,并调整图像的亮暗程度。
在OpenCV4中可以使用cv.calcHist()函来统计图像中每个灰度值的个数。
#cv.calcHist()函数原型
cv.calcHist(images,
channels,
mask,
histSize,
ranges,
[, hist
[, accumulate]])
其中各返回值和参数的含义分别为:
images:待计算直方图的图像数组
channels:需要统计的通道索引
mask:图像掩模
histSize:存放每个维度直方图的数组尺寸
ranges:每个图像通道中灰度值的取值范围
hist:输出的直方图,是一个数组
accumulate:表示是否积累统计直方图的标志
示例代码
# -*- coding:utf-8 -*-
import cv2 as cv
import sys
import numpy as np
# 设置不显示科学计数法,显示普通数字
np.set_printoptions(suppress=True)
if __name__ == '__main__':
# 以灰度方式读取图像
image = cv.imread('../images/BRZ.jpg', 0)
# 判断是否读取成功
if image is None:
print("Failed to read BRZ.jpg.")
sys.exit()
# 对图像进行直方图计算
hist = cv.calcHist([image], [0], None, [256], [0, 256])
# 输出结果
print('统计灰度直方图为:\n{}'.format(hist)
2 直方图的绘制
2.1 cv.line()和cv.polylines()
我们利用线段将每个像素值的数目表示出来,线段的长度表示数目的多少来绘制直方图。对于灰度图,需要先对图像进行直方图计算和归一化处理,对于每个灰度值x及对应数目y,先使用cv.line()函数连接(x,0)和(x,y)两点;对于彩色图像,使用cv.polylines()函数绘制直方图。
示例代码
# -*- coding:utf-8 -*-
import cv2 as cv
import numpy as np
import sys
# 设定bins的数目
bins = np.arange(256).reshape(256, 1)
def draw_gray_histogram(image):
# 创建一个全0矩阵以绘制直方图
new = np.zeros((image.shape[0], 256, 3))
# 对图像进行直方图计算
hist_item = cv.calcHist([image], [0], None, [256], [0, 256])
cv.normalize(hist_item, hist_item, 0, 255, cv.NORM_MINMAX)
hist = np.int32(np.around(hist_item))
for x, y in enumerate(hist):
cv.line(new, (int(x), 0), (int(x),int(y)), (255, 255, 255))
# 由于绘制时是从顶部开始绘制,因此需要将矩阵进行翻转
result = cv.flip(new, 0)
return result
def draw_bgr_histogram(image):
# 创建一个3通道的全0矩阵以绘制直方图
new = np.zeros((image.shape[0], 256, 3))
# 声明BGR三种颜色
bgr = [(255, 0, 0), (0, 255, 0), (0, 0, 255)]
for i, col in enumerate(bgr):
hist_item = cv.calcHist([image], [i], None, [256], [0, 256])
cv.normalize(hist_item, hist_item, 0, 255, cv.NORM_MINMAX)
hist = np.int32(np.around(hist_item))
hist = np.int32(np.column_stack((bins, hist)))
cv.polylines(new, [hist], False, col)
result = cv.flip(new, 0)
return result
if __name__ == '__main__':
# 读取图像BRZ.jpg
img = cv.imread('../images/BRZ.jpg')
# 判断是否读取成功
if img is None:
print("Failed to read BRZ.jpg.")
sys.exit()
# 将图片转为灰度模式
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 计算并绘制灰度图像直方图和BGR图像直方图
gray_histogram = draw_gray_histogram(gray)
bgr_histogram = draw_bgr_histogram(img)
cv.imshow('Origin Image', img)
cv.imshow('Gray Histogram', gray_histogram)
cv.imshow('BGR Histogram', bgr_histogram)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果如下图所示。
2.2 plt.hist()
除了使用cv.line()函数绘制直方图,还可以使用Matplotlib库中的hist()函数之间统计并进行直方图的绘制。
#plt.hist()函数原型
n, bins, patcher = plt.hist(x,
bins=None,
range=None,
density=None,
weights=None,
cumulative=False,
bottom=None,
histtype='bar',
align='mid',
orientation='vertical',
rwidth=None,
log=False,
color=None,
label=None,
stacked=False,
data=None,
**kwargs)
其中各返回值和参数的含义分别为:
x:待绘制直方图的图像
bins:设置每个维度的直方图的数组大小
range:绘制直方图中数据的取值范围
density:确定返回值n为每个维度的频率还是频数
weights:与x尺寸相同的权重数组
cumulative:表示是否绘制累计直方图
bottom:底部基线的位置,默认值为0
histtype:绘制的直方图类型
align:直方图的绘制方式
orientation:确定沿水平方向或垂直方向绘制
rwidth:每个维度之间的宽度,默认值为0
log:表示是否设置为读数刻度
color:指定绘制的直方图的颜色
label:字符串或匹配多个数据集的字符串序列
stacked:确定数据堆叠或并列排序
data:关键字参数,默认不适用
plt.hist() 绘制灰度图像直方图示例代码
# -*- coding:utf-8 -*-
import cv2 as cv
import numpy as np
import sys
# 设定bins的数目
bins = np.arange(256).reshape(256, 1)
def draw_gray_histogram(image):
# 创建一个全0矩阵以绘制直方图
new = np.zeros((image.shape[0], 256, 3))
# 对图像进行直方图计算
hist_item = cv.calcHist([image], [0], None, [256], [0, 256])
cv.normalize(hist_item, hist_item, 0, 255, cv.NORM_MINMAX)
hist = np.int32(np.around(hist_item))
for x, y in enumerate(hist):
cv.line(new, (int(x), 0), (int(x),int(y)), (255, 255, 255))
# 由于绘制时是从顶部开始绘制,因此需要将矩阵进行翻转
result = cv.flip(new, 0)
return result
def draw_bgr_histogram(image):
# 创建一个3通道的全0矩阵以绘制直方图
new = np.zeros((image.shape[0], 256, 3))
# 声明BGR三种颜色
bgr = [(255, 0, 0), (0, 255, 0), (0, 0, 255)]
for i, col in enumerate(bgr):
hist_item = cv.calcHist([image], [i], None, [256], [0, 256])
cv.normalize(hist_item, hist_item, 0, 255, cv.NORM_MINMAX)
hist = np.int32(np.around(hist_item))
hist = np.int32(np.column_stack((bins, hist)))
cv.polylines(new, [hist], False, col)
result = cv.flip(new, 0)
return result
if __name__ == '__main__':
# 读取图像BRZ.jpg
img = cv.imread('../images/BRZ.jpg')
# 判断是否读取成功
if img is None:
print("Failed to read BRZ.jpg.")
sys.exit()
# 将图片转为灰度模式
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 计算并绘制灰度图像直方图和BGR图像直方图
gray_histogram = draw_gray_histogram(gray)
bgr_histogram = draw_bgr_histogram(img)
cv.imshow('Origin Image', img)
cv.imshow('Gray Histogram', gray_histogram)
cv.imshow('BGR Histogram', bgr_histogram)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果如下图所示。
plt.hist() 绘制RGB图像直方图示例代码
# -*- coding:utf-8 -*-
import cv2 as cv
from matplotlib import pyplot as plt
import sys
if __name__ == '__main__':
# 读取图像
img = cv.imread('../images/BRZ.jpg')
# 判断图像是否读取成功
if img is None:
print('Failed to read BRZ.jpg.')
sys.exit()
# 绘制直方图并展示
color = ('b', 'g', 'r')
for i, col in enumerate(color):
hist_item = cv.calcHist([img], [i], None, [256], [0, 256])
plt.plot(hist_item, color=col)
cv.imshow('image', img)
plt.show()
cv.waitKey(0)
cv.destroyAllWindows()
运行结果如下图所示。
3 2D 直方图
上文中绘制的直方图由于只考虑了图像灰度值这一特征,所以称之为一维直方图。但是对于彩色图像,我们需要考虑图像的色度(hue)和饱和度(saturation),并根据这两个特征值进行 2D直方图的统计。
2D直方图的计算与一维直方图类似,同样需要使用cv.calcHist()函数,但是在计算前,需要将图像从RGB格式转换为HSV格式。
3.1 cv.calcHist()
cv.calcHist() 绘2D直方图示例代码
# -*- coding:utf-8 -*-
import numpy as np
import cv2 as cv
import sys
if __name__ == '__main__':
# 构建一个HSV格式颜色地图,然后将其转换为BGR格式
hsv_map = np.zeros((180, 256, 3), dtype=np.uint8)
h, s = np.indices(hsv_map.shape[:2])
hsv_map[:, :, 0] = h
hsv_map[:, :, 1] = s
hsv_map[:, :, 2] = 255
hsv_map = cv.cvtColor(hsv_map, cv.COLOR_HSV2BGR)
# 读取图像road.jpg
image = cv.imread('../images/sky.jpg')
# 判断是否读取成功
if image is None:
print("Failed to read sky.jpg.")
sys.exit()
# 将图片由BGR格式转换成HSV格式
image_hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV)
# 计算2D直方图
image_hist = cv.calcHist([image_hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
print('2D直方图计算结果:\n{}'.format(image_hist))
image_hist = np.clip(image_hist * 0.05, 0, 1)
result = hsv_map * image_hist[:, :, np.newaxis] / 255.0
# 展示结果
cv.imshow('Origin Image', image)
cv.imshow('Hsv Map', hsv_map)
cv.imshow('2D Hist', result)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果如下图所示。
3.2 plt.imshow()
类似一维直方图的绘制,Matplotlib库中同样提供了plt.imshow()函数绘制 2D直方图。
#plt.imshow()函数原型
age = plt.imshow(x,
cmap=None,
norm=None,
aspect=None,
interpolation=None,
alpha=None,
vmin=None,
vmax=None,
origin=None,
extent=None,
filternorm=1,
filterrad=4.0,
resample=None,
hold=None,
data=None,
**kwargs)
其中各返回值和参数的含义分别为:
x:待绘制2D直方图的图像
camp:将数据映射到指定颜色空间显示
norm:使用camp参数前,将数据归一化至[0, 1]
aspect:控制轴的纵横比,可选参数为equal和auto
interpolation:使用的插值方式,默认值为antialiased
alpha:设置透明度,可以为一个标量或和x具有相同尺寸的数组。当设置为标量时,取值范围为[0, 1],0表示透明,1表示不透明。设置为数组时,每个值将作用与x的对应位置。
vmin:设置数据范围的下限
vmax:设置数据范围的上限
origin:设置原点位置,可选参数为upper和lower
extent:待填充的边界框的位置
filternorm:滤波器范数,默认值为1
filterrad:滤波器半径,当插值参数为sinc、lanczos或blackman时使用,默认值为4.0
resample:表示是否进行重采样的标志
url:设置创建结果的URL
data:关键字参数,默认不适用
plt.imshow 绘2D直方图示例代码
# -*- coding:utf-8 -*-
import cv2 as cv
from matplotlib import pyplot as plt
import sys
if __name__ == '__main__':
# 读取图像road.jpg
image = cv.imread('./images/road.jpg')
# 判断图片是否读取成功
if image is None:
print('Failed to read image.')
sys.exit()
# 将图像的颜色空间从BGR转为HSV
image_hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV)
# 计算2D直方图
image_hist = cv.calcHist([image_hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
# 展示图像及直方图结果
cv.imshow('Origin Image', image)
plt.imshow(image_hist, interpolation='nearest')
plt.show()
cv.waitKey(0)
cv.destroyAllWindows()
运行结果如下图所示。
下一篇将会介绍OpenCV中直方图的常用操作。