目标
本文档尝试解答如下问题:
Note
在上一篇中 (直方图均衡化) 我们介绍了一种特殊直方图叫做 图像直方图 。现在我们从更加广义的角度来考虑直方图的概念,继续往下读!
什么是直方图?
-
直方图是对数据的集合 统计 ,并将统计结果分布于一系列预定义的 bins 中。
-
这里的 数据 不仅仅指的是灰度值 (如上一篇您所看到的), 统计数据可能是任何能有效描述图像的特征。
-
先看一个例子吧。 假设有一个矩阵包含一张图像的信息 (灰度值
):
-
如果我们按照某种方式去 统计 这些数字,会发生什么情况呢? 既然已知数字的 范围 包含 256 个值, 我们可以将这个范围分割成子区域(称作 bins), 如:
然后再统计掉入每一个
的像素数目。采用这一方法来统计上面的数字矩阵,我们可以得到下图( x轴表示 bin, y轴表示各个bin中的像素个数)。
-
以上只是一个说明直方图如何工作以及它的用处的简单示例。直方图可以统计的不仅仅是颜色灰度, 它可以统计任何图像特征 (如 梯度, 方向等等)。
-
让我们再来搞清楚直方图的一些具体细节:
- dims: 需要统计的特征的数目, 在上例中, dims = 1 因为我们仅仅统计了灰度值(灰度图像)。
- bins: 每个特征空间 子区段 的数目,在上例中, bins = 16
- range: 每个特征空间的取值范围,在上例中, range = [0,255]
-
怎样去统计两个特征呢? 在这种情况下, 直方图就是3维的了,x轴和y轴分别代表一个特征, z轴是掉入
组合中的样本数目。 同样的方法适用于更高维的情形 (当然会变得很复杂)。
OpenCV的直方图计算
OpenCV提供了一个简单的计算数组集(通常是图像或分割后的通道)的直方图函数 calcHist 。 支持高达 32 维的直方图。下面的代码演示了如何使用该函数计算直方图!
解释
-
创建一些矩阵:
-
装载原图像
-
使用OpenCV函数 split 将图像分割成3个单通道图像:
输入的是要被分割的图像 (这里包含3个通道), 输出的则是Mat类型的的向量。
-
现在对每个通道配置 直方图 设置, 既然我们用到了 R, G 和 B 通道, 我们知道像素值的范围是
-
设定bins数目 (5, 10...):
-
设定像素值范围 (前面已经提到,在 0 到 255之间 )
-
我们要把bin范围设定成同样大小(均一)以及开始统计前先清除直方图中的痕迹:
-
最后创建储存直方图的矩阵:
-
下面使用OpenCV函数 calcHist 计算直方图:
参数说明如下:
- &rgb_planes[0]: 输入数组(或数组集)
- 1: 输入数组的个数 (这里我们使用了一个单通道图像,我们也可以输入数组集 )
- 0: 需要统计的通道 (dim)索引 ,这里我们只是统计了灰度 (且每个数组都是单通道)所以只要写 0 就行了。
- Mat(): 掩码( 0 表示忽略该像素), 如果未定义,则不使用掩码
- r_hist: 储存直方图的矩阵
- 1: 直方图维数
- histSize: 每个维度的bin数目
- histRange: 每个维度的取值范围
- uniform 和 accumulate: bin大小相同,清楚直方图痕迹
-
-
创建显示直方图的画布:
-
在画直方图之前,先使用 normalize 归一化直方图,这样直方图bin中的值就被缩放到指定范围:
该函数接受下列参数:
- r_hist: 输入数组
- r_hist: 归一化后的输出数组(支持原地计算)
- 0 及 histImage.rows: 这里,它们是归一化 r_hist 之后的取值极限
- NORM_MINMAX: 归一化方法 (例中指定的方法将数值缩放到以上指定范围)
- -1: 指示归一化后的输出数组与输入数组同类型
- Mat(): 可选的掩码
-
请注意这里如何读取直方图bin中的数据 (此处是一个1维直方图):
-
最后显示直方图并等待用户退出程序:
结果
-
使用下图作为输入图像:
-
产生以下直方图: