直方图计算
本教程中你将学到如何:
- 使用 OpenCV 函数 cv::split 将一幅图像划分到其对应的各平面。
- 使用 OpenCV 函数 cv::calcHist 计算图像数组的直方图
- 使用函数 cv::normalize 对数组进行归一化
注意: 在上一个教程(tutorial_histogram_equalization)中,我们讨论了一种称为图像直方图(Image histogram)的特殊直方图。现在我们将从更一般的概念来考察它。继续往下读!
什么是直方图?
Section titled “什么是直方图?”-
直方图是将数据组织到一组预定义bin(区间)中后收集到的计数。
-
当我们说数据时,并不局限于强度值(正如我们在上一个教程 tutorial_histogram_equalization 中所见)。所采集的数据可以是任何你认为对描述图像有用的特征。
-
来看一个例子。假设一个矩阵包含某幅图像的信息(例如取值范围为 的强度):
-
如果我们想以一种有条理的方式对这些数据进行计数,会怎样?由于我们知道本例中信息值的范围是 256 个值,我们可以将范围划分为若干子部分(称为 bin),例如:
[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 轴表示 bin,y 轴表示每个 bin 中的像素数)。 -
这只是一个说明直方图如何工作以及为何有用的简单示例。直方图不仅可以统计颜色强度,还可以统计任何我们想测量的图像特征(例如梯度、方向等)。
-
我们来认识一下直方图的几个组成部分:
- dims:你想要采集数据的参数个数。在我们的例子中,dims = 1,因为我们只统计每个像素的强度值(在灰度图中)。
- bins:每个维度上的细分数量。在我们的例子中,bins = 16
- range:要测量的值的范围界限。本例中:range = [0,255]
-
如果你想统计两个特征呢?此时得到的直方图将是一个三维图(其中 x 与 y 分别是每个特征的 与 ,z 是每个 组合的计数)。对更多特征也同理适用(当然会变得更棘手)。
OpenCV 提供了什么
Section titled “OpenCV 提供了什么”为简单起见,OpenCV 实现了函数 cv::calcHist,它计算一组数组(通常是图像或图像平面)的直方图。它最多可处理 32 维。我们将在下面的代码中见到它!
-
这个程序做什么?
- 载入一幅图像
- 使用函数 cv::split 将图像划分到其 R、G、B 平面
- 通过调用函数 cv::calcHist 计算每个单通道平面的直方图
- 在一个窗口中绘制三个直方图
-
可下载代码:点击 这里
-
代码一览: 代码见下方示例
-
可下载代码:点击 这里
-
代码一览: 代码见下方示例
-
可下载代码:点击 这里
-
代码一览: 代码见下方示例
-
载入源图像
-
将源图像分离为其 R、G、B 三个平面。为此我们使用 OpenCV 函数 cv::split:
我们的输入是要被划分的图像(本例中为三通道),输出是一个 Mat 向量)
-
现在我们准备好为每个平面配置直方图了。由于我们处理的是 B、G、R 平面,我们知道取值将落在区间 内
-
设定 bin 数量(5、10……):
-
设置取值范围(如前所述,介于 0 到 255 之间)
-
我们希望各 bin 大小相同(uniform),并在开始时清空直方图,因此:
-
我们使用 OpenCV 函数 cv::calcHist 来计算直方图:
-
其中参数为(C++ 代码):
- &bgr_planes[0]: 源数组
- 1: 源数组的数量(本例中为 1。这里也可以传入一个数组列表)
- 0: 要测量的通道(dim)。本例中仅为强度(每个数组都是单通道),所以写 0。
- Mat(): 用于源数组的掩膜(零表示要忽略的像素)。若未定义则不使用
- b_hist: 存放直方图的 Mat 对象
- 1: 直方图的维数。
- histSize: 每个使用维度上的 bin 数
- histRange: 每个维度上要测量的取值范围
- uniform 与 accumulate: 各 bin 大小相同,且直方图在开始时被清空。
-
创建一幅用于显示直方图的图像:
-
注意,在绘制之前,我们先对直方图进行 cv::normalize,使其值落入由所输入参数指定的范围内:
-
该函数接收以下参数(C++ 代码):
- b_hist: 输入数组
- b_hist: 归一化后的输出数组(可以与输入相同)
- 0 与 histImage.rows:本例中,它们是对 r_hist 值进行归一化的下限与上限
- NORM_MINMAX: 指示归一化类型的参数(如前所述,它将值调整到先前设定的两个界限之间)
- -1: 表示归一化后的输出数组与输入类型相同
- Mat(): 可选的掩膜
-
注意,要访问 bin(本例中为一维直方图的 bin):
我们使用如下表达式(C++ 代码):
b_hist.at<float>(i)

其中 $i$ 表示维度。如果是二维直方图,我们会用类似这样的写法: b_hist.at<float>( i, j )

- 最后我们显示直方图并等待用户退出:
-
以下图所示的图像作为输入:
-
得到如下直方图: