OpenCV  4.10.0
开源计算机视觉
加载中...
搜索中...
无匹配项
直方图计算

前一教程: 直方图均衡化
下一教程: 直方图比较

原始作者Ana Huamán
兼容性OpenCV >= 3.0

目标

在本教程中,你将学习如何

  • 使用 OpenCV 函数 cv::split 将一张图像分割成各个相应的平面。
  • 使用 OpenCV 函数 cv::calcHist 计算图像数组的直方图
  • 使用函数 cv::normalize 归一化数组
注意
在上一个教程(直方图均衡化)中,我们讨论了一种称为图像直方图的特殊直方图。现在我们将其考虑在内,从更一般的概念出发。继续阅读!

什么是直方图?

  • 直方图是将数据收集到一组预定义的中,并计数的数据
  • 当我们说数据时,我们并没有将它限定为强度值(正如我们在前一教程 直方图均衡化 中看到的那样)。收集的数据可以是你用于描述图像的任何有用特征。
  • 我们来看一个例子。想象一个矩阵包含一个图像的信息(即范围 \(0-255\) 的强度)
  • 如果我们想要以一种有条不紊的方式计数该数据,将会发生什么?由于我们知道此时信息值的范围是 256 个值,因此我们可以将我们的范围分割成子部分(称为),例如

\[\begin{array}{l} [0, 255] = { [0, 15] \cup [16, 31] \cup ....\cup [240,255] } \\ range = { bin_{1} \cup bin_{2} \cup ....\cup bin_{n = 15} } \end{array}\]

我们还可以统计落入每个 \(bin_{i}\) 范围的像素数。对上面的示例应用此方法,我们得到以下图像(x 轴表示箱,y 轴表示每个箱中的像素数)。

  • 这只是直方图工作原理和它为何有用的一个简单示例。直方图不仅可以统计色彩强度,还可以统计我们要测量的任何图像特征(即梯度、方向等)。
  • 我们来识别直方图的某些部分
    1. dims:要收集其数据的参数数。在我们的示例中,dims = 1,因为我们仅统计每个像素的强度值(在灰度图像中)。
    2. bin:它是每个维度上的子划分数量。在我们的示例中,bin = 16
    3. range:要测量的值的范围。在本例中:range = [0,255]
  • 如果要计数两个特征怎么办?在这种情况下,生成的直方图将是一个 3D 绘图(其中 x 和 y 是每个特征的 \(bin_{x}\) 和 \(bin_{y}\),z 是 \((bin_{x}, bin_{y})\) 的每个组合的计数)。对于更多特征也是如此(当然会更复杂)。

OpenCV 提供了什么

出于简单的目的,OpenCV 实现了函数 cv::calcHist,用于计算一组数组(通常是图像或图像平面)的直方图。它最多可以处理 32 个维度。我们会在下面的代码中看到它!

代码

  • 该程序做什么?
    • 加载图像
    • 使用函数 cv::split 将图像拆分为其 R、G 和 B 平面
    • 通过调用函数 cv::calcHist 计算每个 1 通道平面的直方图
    • 在窗口中绘制三个直方图

解释

  • 加载源图像

  • 在其三个 R、G 和 B 平面中分离源图像。为此,我们使用 OpenCV 函数 cv::split

    我们的输入就是要划分的图像(在这种情况下为三个通道),而输出是 Mat 向量)

  • 现在,我们可以开始为每个平面配置直方图。由于我们使用的是 B、G 和 R 平面,我们知道我们的值范围为 \([0,255]\)
  • 确定组数 (5, 10...)

  • 设置值范围(如前所述,在 0 和 255 之间)

  • 希望我们的组具有相同大小(均匀)并在开始时清除直方图,因此

  • 我们使用 OpenCV 函数 cv::calcHist

  • 计算直方图,其中参数为(C++ 代码
    • &bgr_planes[0]: 源数组
    • 1:源数组数(在这种情况下我们使用的是 1。我们还可以在此处输入一个数组列表)
    • 0:要测量的通道(dim)。在这种情况下,它只是强度(每个数组都是单通道),所以我们只写 0。
    • Mat():要在源阵列上使用的掩码(0 表示要忽略的像素)。如果没有定义,则不使用
    • b_hist:要存储直方图的 Mat 对象
    • 1:直方图维数。
    • histSize:每个已使用的维度的柱数量
    • histRange:每个维度要测量的值范围
    • uniformaccumulate:柱大小相同,直方图在开始时已清除。
  • 创建一个图像来显示直方图

  • 请注意,在绘制前,首先会对直方图进行 cv::normalize 操作,使其值落在输入参数指示的范围内

  • 此函数接收这些参数(C++ 代码
    • b_hist:输入阵列
    • b_hist:输出归一化后的阵列(可以相同)
    • 0histImage.rows:本例为归一化 r_hist 值的上下限
    • NORM_MINMAX:指示归一化类型(如上所述,它调整值使其介于之前设置的两个极限之间)的参数
    • -1:表示输出归一化阵列类型与输入类型相同
    • Mat():可选掩码
  • 注意要访问柱(在本例为 1D-直方图中的柱)

    我们使用 (C++ 代码) 表达式

    b_hist.at<float>(i)

    其中 \(i\) 表示维度。如果是 2D-直方图,我们要使用类似

    b_hist.at<float>( i, j )
  • 最后,我们显示我们的直方图,等到用户退出

结果

  1. 使用如下所示的图像作为输入参数
  1. 可生成以下直方图