直方图(Histogram)又称质量分布图、柱状图,是一种统计报告图,也是表示资料变化情况的一种主要工具。直方图由一系列高度不等的纵向条纹或线段表示数据分布的情况,一般用横轴表示数据类型,纵轴表示分布情况。作直方图的目的就是通过观察图的形状,判断生产过程是否稳定,预测生产过程的质量。
直方图广泛应用于很多计算机视觉应用中。通过标记帧与帧之间显著的边缘和颜色的统计变化,直方图被用来检测视频中场景的变换。通过为每个兴趣点设置一个有相近特征的直方图所构成的“标签”,用以确定图像中的兴趣点。边缘、色彩、角等直方图构成了可以被传递给目标识别分类器的一个通用特征类型。色彩和边缘的直方图序列还可以用来识别网络视频是否被复制等。直方图是计算机视觉中最经典的工具之一。
1)CvHistogram:直方图的基本数据结构
typedef struct CvHistogram
CvHistogram* cvCreateHist( int dims, int* sizes, int type, float** ranges=NULL, int uniform=1 );
dims:表示直方图包含的维数,也就是有多少列。
sizes:变量必须为整数数组,数组长度等于dims,数组中的每一个整数表示分配给对应维数的bin的个数
tepe:既可以是CV_HIST_ARRAY,用来表示使用密集多维矩阵结构(如CvMatND)存储多维直方图,也可以是CV_HIST_SPARSE,当数据以稀疏矩阵(CvSparseMat)方式存储时,变量ranges可以是两个形式中的任意一种。
ranges:对于均匀直方图来说,ranges是浮点数对构成的数组,数组的个数等于维数。而对于非均匀直方图来说,则用包含分割非均匀bin的数据所构成的数组来代替均匀直方图所使用的数据对。
uniform:布尔类型 变量,说明直方图是否均匀的bin,因此也说明了ranges的值该如何解析,如果设置为非0值,则直方图是均匀的,也可以设置ranges为NULL,这时意味着ranges是未知的。
函数 cvCreateHist 创建一个指定尺寸的直方图,并且返回创建的直方图的指针。 如果数组的 ranges 是 0, 则直方块的范围必须由函数cvSetHistBinRanges 稍后指定。虽然 cvCalcHist 和 cvCalcBackProject 可以处理 8-比特图像而无需设置任何直方块的范围,但它们都被假设等分 0..255 之间的空间。
3)cvCalcHist:计算图像image(s) 的直方图
void cvCalcHist( IplImage** image, CvHistogram* hist, int accumulate=0, const CvArr* mask=NULL );
image
输入图像s (虽然也可以使用 CvMat** ).
hist
直方图指针
accumulate
累计标识。如果设置,则直方图在开始时不被清零。这个特征保证可以为多个图像计算一个单独的直方图,或者在线更新直方图。
mask
操作 mask, 确定输入图像的哪个象素被计数
函数 cvCalcHist 计算一张或多张单通道图像的直方图(译者注:若要计算多通道,可像以下例子那样用多个单通道图来表示)。 用来增加直方块的数组元素可从相应输入图像的同样位置提取。 Sample. 计算和显示彩色图像的 2D 色调-饱和度图像
4)double cvQueryHistValue_1D(CvHistogram * hist, int idx0);
double cvQueryHistValue_2D(CvHistogram * hist, int idx0, int idx1);
double cvQueryHistValue_3D(CvHistogram * hist, int idx0,int idx1,int idx2);
每个函数都返回相应bin中的值的浮点数。同样地,可以利用函数返回的bin的指针(不是bin的值)来设置(或者获得)直方图的bin的值。
float * cvGetHistValue_1D(CvHistogram * hist, int idx0);
float * cvGetHistValue_2D(CvHistogram * hist, int idx0, int idx1);
float * cvGetHistValue_3D(CvHistogram * hist, int idx0, int idx1, int idx2);
在这里关于RNG做一些延伸的学习。
(1)CvRNG cvRNG( int64 seed=-1 );
seed
64-bit 的值用来初始化一个随机序列
函数 cvRNG 初始化随机数生成器并返回其状态。指向这个状态的指针可以传递给函数 cvRandInt, cvRandReal 和 cvRandArr . 在通常的实现中使用一个 multiply-with-carry generator 。
RandArr:用随机数填充数组并更新 RNG 状态
(2)void cvRandArr( CvRNG* rng, CvArr* arr, int dist_type, CvScalar param1, CvScalar param2 );
rng
被 cvRNG 初始化的 RNG 状态.
arr
输出数组
dist_type
分布类型:
CV_RAND_UNI - 均匀分布
CV_RAND_NORMAL - 正态分布 或者 高斯分布
param1
分布的第一个参数。如果是均匀分布它是随机数范围的闭下边界。如果是正态分布它是随机数的平均值。
param2
分布的第二个参数。如果是均匀分布它是随机数范围的开上边界。如果是正态分布它是随机数的标准差。
函数 cvRandArr 用均匀分布的或者正态分布的随机数填充输出数组。在下面的例子中该函数被用来添加一些正态分布的浮点数到二维数组的随机位置。
RandInt:返回 32-bit 无符号整型并更新 RNG
(3)unsigned cvRandInt( CvRNG* rng );
rng
被 cvRNG 初始化的 RNG 状态,被 RandSetRange (虽然, 后面这个函数对我们正讨论的函数的结果没有什么影响)随意地设置。
函数 cvRandInt 返回均匀分布的随机 32-bit 无符号整型值并更新 RNG 状态。它和 C 运行库里面的 rand() 函数十分相似,
但是它产生的总是一个 32-bit 数而 rand() 返回一个 0 到 RAND_MAX (它是 2**16 或者 2**32, 依赖于操作平台)之间的数。
该函数用来产生一个标量随机数,例如点, patch sizes, table indices 等,用模操作可以产生一个确定边界的整数,
人和其他特定的边界缩放到 0.. 1可以产生一个浮点数。下面是用 cvRandInt 重写的前一个函数讨论的例子:
RandReal:返回浮点型随机数并更新 RNG
(4)double cvRandReal( CvRNG* rng );
rng
被 cvRNG 初始化的 RNG 状态
函数 cvRandReal 返回均匀分布的随机浮点数,范围在 0..1 之间 (不包括 1 )。
代码:
#include <cv.h>
#include <highgui.h>
#include <iostream>
#include <opencv2/legacy/legacy.hpp>
using namespace cv;
using namespace std;
int main()
{
CvRNG rng;//初始化随机数生成器状态
rng = cvRNG(cvGetTickCount());
IplImage* src = cvCreateImage(cvSize(10, 100), IPL_DEPTH_32F, 1);
for (int i = 0; i < 1000; ++i)
{
double value = cvRandReal(&rng);//返回浮点型随机数并更新 RNG
cvSetReal1D(src, i, value);//把返回的随机数值设置进目标数组src里面
}
int dims = 1; //直方图包含的维数为1维
int sizes[1] = { 10 }; //bin的个数,即是10列
int type = CV_HIST_ARRAY; //密集多维矩阵结构
float arr[] = { 0.0f, 1.0f };//数对
float * rangs[] = { arr }; //1个浮点数对
CvHistogram *histogram = cvCreateHist(dims, sizes, type, rangs, 1);
cvCalcHist(&src, histogram);//计算直方图
float sum = 0.0f;
for (int i = 0; i < 10; ++i)
{//依次访问此直方图中10列中的bin数据
float value = cvQueryHistValue_1D(histogram, i);
cout << value << endl;
}
//cvWaitKey(); 只有在之前调用该cvShowImage后才会等待,如果之前的代码没调过,则必须用 system("pause");
//才能暂停下来。但是,如果代码中有cvShowImage的操作,在 cvWaitKey(); 前一定不能使用 system("pause");
//不然程序一直会暂停那里,鼠标一直转圈,显示不出图片
system("pause");
cvWaitKey();
cvReleaseImage(&src);
cvDestroyAllWindows();
rangs[1] = NULL;
return 0;
}
输出结果:
下面有几个主要事项:
1,cvWaitKey(); 只有在之前调用该cvShowImage后才会等待,如果之前的代码没调过,则必须system("pause"); 才能暂停下来。但是,如果代码中有cvShowImage的操作,在 cvWaitKey(); 前一定不能使用 system("pause"); ,不然程序一直会暂停那里,鼠标一直转圈,显示不出图片。
2,cvCreateHist的参数sizes即直方图的大小,相当于直方图包含元素的个数(即有多少列),如果是二维直方图,即直方图大小为2,包含两个一维直方图。
3,sizes必须为整型数组,数组长度等于直方图的维数,数组中每一个整数表示分配给对应维数的bin的个数,例如直方图为一维,sizes必须为只包含一个整型元素的数组,该元素的值即为直方图bin的个数。
4,cvCreateHist的参数ranges是指向指针的指针,即二维数组,第一维的长度应等于bin的个数,第二维代表各个bin的的范围或分段情况,对于均匀直方图,第二维便是最小值和最大值组成的两个元素的数组;对于有N个bin的非均匀直方图,第二维是由N+1个元素组成的分段情况,例如本题如果使用非均匀可以是{0.0,0.14,0.25,0.31, 0.29,0.55, 0.60,0.66,0.79,0.82,1.0},这第二维工11个数,相邻的两个数代表一个bin的范围,这样若干个一维数组合成一个二维数组,本题的若干个=1个。
5,根据右左法则以 float * rangs[] 为例,rangs是一个数组,数组元素的类型为指针,指针式指向float的指针,其实这一点使用 float rangs[] 更容易理解,rangs是一个数组,数组元素是float型。
6,cvCalcHist计算直方图即根据直方图的参数,算出落在每个bin中数量。
7,cvQueryHistValue_1D等函数即获取各个bin中元素的数量。