• OpenCV官方教程中文版 —— 直方图的计算,绘制与分析


    OpenCV官方教程中文版 —— 直方图的计算,绘制与分析

    前言

    使用 OpenCV 或 Numpy 函数计算直方图

    使用 Opencv 或者 Matplotlib 函数绘制直方图

    将要学习的函数有:cv2.calcHist(),np.histogram()

    一、原理

    什么是直方图呢?通过直方图你可以对整幅图像的灰度分布有一个整体的了解。直方图的 x 轴是灰度值(0 到 255),y 轴是图片中具有同一个灰度值的点的数目。

    直方图其实就是对图像的另一种解释。一下图为例,通过直方图我们可以对图像的对比度,亮度,灰度分布等有一个直观的认识。几乎所有的图像处理软件都提供了直方图分析功能。下图来自Cambridge in Color website,强烈推荐你到这个网站了解更多知识。

    在这里插入图片描述
    让我们来一起看看这幅图片和它的直方图吧。(要记住,直方图是根据灰度图像绘制的,而不是彩色图像)。直方图的左边区域像是了暗一点的像素数量,右侧显示了亮一点的像素的数量。从这幅图上你可以看到灰暗的区域比两的区域要大,而处于中间部分的像素点很少。

    1.统计直方图

    现在我们知道什么是直方图了,那怎样获得一副图像的直方图呢?OpenCV 和 Numpy 都有内置函数做这件事。在使用这些函数之前我们有必要想了解一下直方图相关的术语。

    BINS:上面的直方图显示了每个灰度值对应的像素数。如果像素值为 0到 255,你就需要 256 个数来显示上面的直方图。但是,如果你不需要知道每一个像素值的像素点数目的,而只希望知道两个像素值之间的像素点数目怎么办呢?举例来说,我们想知道像素值在 0 到 15 之间的像素点的数目,接着是 16 到 31,…,240 到 255。我们只需要 16 个值来绘制直方图。

    那到底怎么做呢?你只需要把原来的 256 个值等分成 16 小组,取每组的总和。而这里的每一个小组就被成为 BIN。第一个例子中有 256 个 BIN,第二个例子中有 16 个 BIN。在 OpenCV 的文档中用 histSize 表示 BINS。

    DIMS:表示我们收集数据的参数数目。在本例中,我们对收集到的数据只考虑一件事:灰度值。所以这里就是 1。

    RANGE:就是要统计的灰度值范围,一般来说为 [0,256],也就是说所有的灰度值

    cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
    
    • 1
    1. images: 原图像(图像格式为 uint8 或 float32)。当传入函数时应该用中括号 [] 括起来,例如:[img]。
    2. channels: 同样需要用中括号括起来,它会告诉函数我们要统计那幅图像的直方图。如果输入图像是灰度图,它的值就是 [0];如果是彩色图像的话,传入的参数可以是 [0],[1],[2] 它们分别对应着通道 B,G,R。
    3. mask: 掩模图像。要统计整幅图像的直方图就把它设为 None。但是如果你想统计图像某一部分的直方图的话,你就需要制作一个掩模图像,并使用它。(后边有例子)
    4. histSize:BIN 的数目。也应该用中括号括起来,例如:[256]。
    5. ranges: 像素值范围,通常为 [0,256]

    让我们从一副简单图像开始吧。以灰度格式加载一幅图像并统计图像的直方图。

    img = cv2.imread('home.jpg',0)
    # 别忘了中括号 [img],[0],None,[256],[0,256],只有 mask 没有中括号
    hist = cv2.calcHist([img],[0],None,[256],[0,256])
    
    • 1
    • 2
    • 3

    hist 是一个 256x1 的数组,每一个值代表了与次灰度值对应的像素点数目。

    使用 Numpy 统计直方图 Numpy 中的函数 np.histogram() 也可以帮我们统计直方图。你也可以尝试一下下面的代码:

    #img.ravel() 将图像转成一维数组,这里没有中括号。
    hist,bins = np.histogram(img.ravel(),256,[0,256])
    
    • 1
    • 2

    hist 与上面计算的一样。但是这里的 bins 是 256,因为 Numpy 计算bins 的方式为:0-0.99,1-1.99,2-2.99 等。所以最后一个范围是 255-255.99。为了表示它,所以在 bins 的结尾加上了 256。但是我们不需要 256,到 255就够了。

    其他:Numpy还有一个函数 np.bincount(),它的运行速度是np.histgram 的十倍。所以对于一维直方图,我们最好使用这个函数。使用 np.bincount 时别忘了设置 minlength=256。 例如,hist=np.bincount(img.ravel(),minlength=256)

    注意:OpenCV 的函数要比 np.histgram() 快 40 倍。所以坚持使用OpenCV 函数。

    2. 绘制直方图

    有两种方法来绘制直方图:

    1. Short Way(简单方法):使用 Matplotlib 中的绘图函数。
    2. Long Way(复杂方法):使用 OpenCV 绘图函数

    使用 Matplotlib Matplotlib 中有直方图绘制函数:matplotlib.pyplot.hist()它可以直接统计并绘制直方图。

    # -*- coding: utf-8 -*-
    import cv2
    from matplotlib import pyplot as plt
    import numpy as np
    A = cv2.imread('apple.png', 0)
    plt.figure()
    plt.subplot(1, 2, 1)
    plt.imshow(A, cmap='gray', interpolation='bicubic')  # expect true color
    plt.xticks([]), plt.yticks([])  # to hide tick values on X and Y axis
    plt.subplot(1, 2, 2)
    plt.hist(A.ravel(), 256, [0, 256])
    plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    你会得到下面这样一幅图:
    在这里插入图片描述
    或者你可以只使用 matplotlib 的绘图功能,这在同时绘制多通道(BGR)的直方图,很有用。但是你首先要告诉绘图函数你的直方图数据在哪里。运行一下下面的代码:

    img = cv2.imread('apple.png')
    color = ('b', 'g', 'r')
    # 对一个列表或数组既要遍历索引又要遍历元素时
    # 使用内置 enumerrate 函数会有更加直接,优美的做法
    # enumerate 会将数组或列表组成一个索引序列。
    # 使我们再获取索引和索引内容的时候更加方便
    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    3. 使用掩模

    要统计图像某个局部区域的直方图只需要构建一副掩模图像。将要统计的部分设置成白色,其余部分为黑色,就构成了一副掩模图像。然后把这个掩模图像传给函数就可以了。

    img = cv2.imread('home.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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    结果如下,其中蓝线是整幅图像的直方图,绿线是进行掩模之后的直方图。

    在这里插入图片描述

  • 相关阅读:
    定义函数(简单介绍)-def
    hi3559AV100调试记录
    黑*头条_第1章_项目介绍和工程搭建
    Who will wash the dishes Privacy Policy
    密码加密解密之路
    青少年软件编程(202209)(C语言)等级考试(五级)试题及参考答案
    Linux——生产者消费者模型
    进制转换间的那点事
    仓库太大,clone 后,git pull 老分支成功,最新分支失败
    唤醒键盘后无法立即隐藏键盘问题与键盘隐藏的四种方式
  • 原文地址:https://blog.csdn.net/weixin_42207434/article/details/134020709