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

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

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

目标

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

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

什么是直方图?

  • 直方图是将数据组织成一组预定义 bins(数据区间)计数 集合
  • 当我们说 数据 时,我们不将其限制为强度值(如我们在上一个教程 直方图均衡化 中所看到的)。收集到的数据可以是您认为对描述图像有用的任何特征。
  • 让我们看一个例子。想象一个矩阵包含图像信息(即强度范围在 \(0-255\) 之间)
  • 如果我们想以有组织的方式 计数 这些数据,会发生什么?由于我们知道在这种情况下,信息值的 范围 是 256 个值,我们可以将范围分割成子部分(称为 bins),例如:

    \[\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. bins(数据区间):每个维度中的 细分 数量。在我们的例子中,bins = 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 之间)

  • 我们希望数据区间大小相同(uniform),并在开始时清除直方图,所以

  • 我们使用 OpenCV 函数 cv::calcHist 来计算直方图

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

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

  • 此函数接收以下参数(C++ 代码
    • b_hist: 输入数组
    • b_hist: 输出归一化数组(可以是同一个)
    • 0histImage.rows: 在本例中,它们是归一化 r_hist 值的下限和上限
    • NORM_MINMAX: 指示归一化类型的参数(如上所述,它将值调整到之前设置的两个限制之间)
    • -1: 表示输出归一化数组将与输入类型相同
    • Mat(): 可选掩码
  • 请注意,要访问 bin(在此 1D 直方图中)

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

    b_hist.at<float>(i)

    其中 \(i\) 表示维度。如果它是 2D 直方图,我们将使用类似以下内容:

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

结果

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