直方图就是对数据进行统计,将统计值组织到一个事先定义好的bin中,bin中的数值是从数据中计算出来的特征统计量,这些数据可以是诸如阶梯方向,色彩或其他特征。无论如何,直方图获得的是数据分布的统计图。通常直方图的维数要低于原始数据。
直方图的基本数据结构
CvHistogram
多维直方图
typedef struct CvHistogram { int type; CvArr* bins; float thresh[CV_MAX_DIM][2]; /* for uniform histograms */ float** thresh2; /* for non-uniform histograms */ CvMatND mat; /* embedded matrix header for array histograms */ } CvHistogram;
bins : 用于存放直方图每个灰度级数目的数组指针,数组在cvCreateHist 的时候创建,其维数由cvCreateHist 确定(一般以一维比较常见)
要创建一个直方图,需要调用:CreateHist
创建直方图
CvHistogram* cvCreateHist( int dims, int* sizes, int type, float** ranges=NULL, int uniform=1 );
-
dims
- 直方图维数的数目 sizes
- 直方图维数尺寸的数组 type
- 直方图的表示格式: CV_HIST_ARRAY 意味着直方图数据表示为多维密集数组 CvMatND; CV_HIST_TREE 意味着直方图数据表示为多维稀疏数组 CvSparseMat. ranges
- 图中方块范围的数组. 它的内容取决于参数 uniform 的值。这个范围的用处是确定何时计算直方图或决定反向映射(backprojected ),每个方块对应于输入图像的哪个/哪组值。 uniform
- 归一化标识。 如果不为0,则ranges[i](0<=i<cDims,译者注:cDims为直方图的维数,对于灰度图为1,彩色图为3)是包含两个元素的范围数组,包括直方图第i维的上界和下界。在第i维上的整个区域 [lower,upper]被分割成 dims[i] 个相等的块(译者注:dims[i]表示直方图第i维的块数),这些块用来确定输入象素的第 i 个值(译者注:对于彩色图像,i确定R, G,或者B)的对应的块;如果为0,则ranges[i]是包含dims[i]+1个元素的范围数组,包括lower0, upper0, lower1, upper1 == lower2, ..., upperdims[i]-1, 其中lowerj 和upperj分别是直方图第i维上第 j 个方块的上下界(针对输入象素的第 i 个值)。任何情况下,输入值如果超出了一个直方块所指定的范围外,都不会被 cvCalcHist 计数,而且会被函数 cvCalcBackProject 置零。
函数 cvCreateHist 创建一个指定尺寸的直方图,并且返回创建的直方图的指针。 如果数组的 ranges 是 0, 则直方块的范围必须由函数 cvSetHistBinRanges 稍后指定。虽然 cvCalcHist 和 cvCalcBackProject 可以处理 8-比特图像而无需设置任何直方块的范围,但它们都被假设等分 0..255 之间的空间。
SetHistBinRanges
设置直方块的区间
void cvSetHistBinRanges( CvHistogram* hist, float** ranges, int uniform=1 );
hist
- 直方图.
ranges
- 直方块范围数组的数组,见 cvCreateHist.
uniform
- 归一化标识,见 cvCreateHist.
函数 cvSetHistBinRanges 是一个独立的函数,完成直方块的区间设置。更多详细的关于参数 ranges 和 uniform 的描述,请参考函数 cvCalcHist , 该函数也可以初始化区间。直方块的区间的设置必须在计算直方图之前,或 在计算直方图的反射图之前。
ClearHist
清除直方图
void cvClearHist( CvHistogram* hist );
-
hist
- 直方图.
函数 cvClearHist 当直方图是稠密数组时将所有直方块设置为 0,当直方图是稀疏数组时,除去所有的直方块。
ReleaseHist
释放直方图结构
void cvReleaseHist( CvHistogram** hist );
-
hist
- 被释放的直方图结构的双指针.
函数 cvReleaseHist 释放直方图 (头和数据). 指向直方图的指针被函数所清空。如果 *hist 指针已经为 NULL, 则函数不做任何事情。
根据已经给出的数据创建直方图:
MakeHistHeaderForArray
从数组中创建直方图
CvHistogram* cvMakeHistHeaderForArray( int dims, int* sizes, CvHistogram* hist, float* data, float** ranges=NULL, int uniform=1 );
-
dims
- 直方图维数. sizes
- 直方图维数尺寸的数组 hist
- 为函数所初始化的直方图头 data
- 用来存储直方块的数组 ranges
- 直方块范围,见 cvCreateHist. uniform
- 归一化标识,见 cvCreateHist.
函数 cvMakeHistHeaderForArray 初始化直方图,其中头和直方块为用户所分配。以后不需要调用 cvReleaseHist 只有稠密直方图可以采用这种方法,函数返回 hist.
访问直方图:
QueryHistValue_1D
查询直方块的值
#define cvQueryHistValue_1D( hist, idx0 ) \
cvGetReal1D( (hist)->bins, (idx0) )
#define cvQueryHistValue_2D( hist, idx0, idx1 ) \
cvGetReal2D( (hist)->bins, (idx0), (idx1) )
#define cvQueryHistValue_3D( hist, idx0, idx1, idx2 ) \
cvGetReal3D( (hist)->bins, (idx0), (idx1), (idx2) )
#define cvQueryHistValue_nD( hist, idx ) \
cvGetRealND( (hist)->bins, (idx) )
-
hist
- 直方图 idx0, idx1, idx2, idx3
- 直方块的下标索引 idx
- 下标数组
宏 cvQueryHistValue_*D 返回 1D, 2D, 3D 或 N-D 直方图的指定直方块的值。对稀疏直方图,如果方块在直方图中不存在,函数返回 0, 而且不创建新的直方块。
GetHistValue_1D
返回直方块的指针
#define cvGetHistValue_1D( hist, idx0 ) \ ((float*)(cvPtr1D( (hist)->bins, (idx0), 0 )) #define cvGetHistValue_2D( hist, idx0, idx1 ) \ ((float*)(cvPtr2D( (hist)->bins, (idx0), (idx1), 0 )) #define cvGetHistValue_3D( hist, idx0, idx1, idx2 ) \ ((float*)(cvPtr3D( (hist)->bins, (idx0), (idx1), (idx2), 0 )) #define cvGetHistValue_nD( hist, idx ) \ ((float*)(cvPtrND( (hist)->bins, (idx), 0 ))
-
hist
- 直方图. idx0, idx1, idx2, idx3
- 直方块的下标索引. idx
- 下标数组
宏 cvGetHistValue_*D 返回 1D, 2D, 3D 或 N-D 直方图的指定直方块的指针。对稀疏直方图,函数创建一个新的直方块,且设置其为 0,除非它已经存在。
直方图的基本操作:
NormalizeHist
归一化直方图
void cvNormalizeHist( CvHistogram* hist, double factor );
-
hist
- 直方图的指针. factor
- 归一化因子
函数 cvNormalizeHist 通过缩放来归一化直方块,使得所有块的和等于 factor.
ThreshHist
对直方图取阈值
void cvThreshHist( CvHistogram* hist, double threshold );
-
hist
- 直方图的指针. threshold
- 阈值大小
函数 cvThreshHist 清除那些小于指定阈值得直方块
CopyHist
拷贝直方图
void cvCopyHist( const CvHistogram* src, CvHistogram** dst );
-
src
- 输入的直方图 dst
- 输出的直方图指针
函数 cvCopyHist 对直方图作拷贝。如果第二个直方图指针 *dst 是 NULL, 则创建一个与 src 同样大小的直方图。否则,两个直方图必须大小和类型一致。然后函数将输入的直方块的值复制到输出的直方图中,并且设置取值范围与 src 的一致。
CalcHist
计算图像image(s) 的直方图
void cvCalcHist( IplImage** image, CvHistogram* hist, int accumulate=0, const CvArr* mask=NULL );
-
image
- 输入图像s (虽然也可以使用 CvMat** ). hist
- 直方图指针 accumulate
- 累计标识。如果设置,则直方图在开始时不被清零。这个特征保证可以为多个图像计算一个单独的直方图,或者在线更新直方图。 mask
- 操作 mask, 确定输入图像的哪个象素被计数
GetMinMaxHistValue
发现最大和最小直方块
void cvGetMinMaxHistValue( const CvHistogram* hist, float* min_value, float* max_value, int* min_idx=NULL, int* max_idx=NULL );
-
hist
- 直方图 min_value
- 直方图最小值的指针 max_value
- 直方图最大值的指针 min_idx
- 数组中最小坐标的指针 max_idx
- 数组中最大坐标的指针
函数 cvGetMinMaxHistValue 发现最大和最小直方块以及它们的位置。任何输出变量都是可选的。在具有同样值几个极值中,返回具有最小下标索引(以字母排列顺序定)的那一个。
/*
*直方图的用法示例
*/
#include "highgui.h"
#include "cv.h"
void doHist1(IplImage* img)
{
IplImage* hsv = cvCreateImage(cvGetSize(img), 8, 3);
IplImage* h_plane = cvCreateImage(cvGetSize(img), 8, 1);
IplImage* s_plane = cvCreateImage(cvGetSize(img), 8, 1);
IplImage* v_plane = cvCreateImage(cvGetSize(img), 8, 1);
IplImage* planes[] = {h_plane, s_plane};
//设置直方图每个灰度级的数目
int h_bins = 30;
int s_bins = 32;
int hist_size[] = {h_bins, s_bins};
float h_ranges[] = {0, 180};
float s_ranges[] = {0, 255};
float* ranges[] = {h_ranges, s_ranges};
int scale = 10;
IplImage* hist_img = cvCreateImage(cvSize(h_bins* scale , s_bins*scale), 8, 3);
CvHistogram* hist;
float max_value = 0;
int h, s;
cvCvtColor(img, hsv, CV_BGR2HSV);
cvCvtPixToPlane(hsv, h_plane, s_plane, v_plane, 0); //cvSplit的一个宏
// 计算图像的直方图
hist = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
cvCalcHist(planes, hist, 0, 0);
//cvNormalizeHist(hist, 1.0);
cvGetMinMaxHistValue(hist, 0, &max_value, 0, 0);
cvZero(hist_img);
//cvNormalizeHist(hist, 1.0);
for(int h = 0; h < h_bins; h++)
{
for(int s = 0; s < s_bins; s++)
{
float bin_val = cvQueryHistValue_2D(hist, h,s);
int intensity = cvRound(bin_val * 255/ max_value);
cvRectangle(
hist_img,
cvPoint(h*scale, s*scale),
cvPoint((h+1)*scale - 1, (s+1)*scale -1),
CV_RGB(intensity, intensity,intensity),
CV_FILLED
);
}
}
cvNamedWindow( "H-S Histogram", 1 );
cvShowImage( "H-S Histogram", hist_img );
cvWaitKey(0);
}
结果: