OpenCV学习笔记(八)——直方图的计算与绘制(cv.calcHist()、plt.hist()、plt.imshow())

目录

  • 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()

运行结果如下图所示。
OpenCV学习笔记(八)——直方图的计算与绘制(cv.calcHist()、plt.hist()、plt.imshow())_第1张图片

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()

运行结果如下图所示。
OpenCV学习笔记(八)——直方图的计算与绘制(cv.calcHist()、plt.hist()、plt.imshow())_第2张图片

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()

运行结果如下图所示。
OpenCV学习笔记(八)——直方图的计算与绘制(cv.calcHist()、plt.hist()、plt.imshow())_第3张图片

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()

运行结果如下图所示。
OpenCV学习笔记(八)——直方图的计算与绘制(cv.calcHist()、plt.hist()、plt.imshow())_第4张图片

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学习笔记(八)——直方图的计算与绘制(cv.calcHist()、plt.hist()、plt.imshow())_第5张图片

下一篇将会介绍OpenCV中直方图的常用操作。

你可能感兴趣的:(OpenCV学习笔记,opencv,python)