最近在研究Meanshift跟踪算法,遇到一些概念,比如颜色直方图和反向投影,有些不理解,故百度搜之,并小结,希望能印象深刻。
(一)颜色直方图
颜色特征是图像检索中应用最为广泛的视觉特征,主要原因在于颜色往往和图像中所包含的物体或场景十分相关。此外,与其他的视觉特征相比,颜色特征对图像本身的尺寸、方向、视角的依赖性较小,从而具有较高的鲁棒性。
首先,我们需要合适的颜色空间来描述颜色特征;其次,我们要采用一定的量化方法将颜色特征表达为向量的形式;最后,还要定义一种相似度(距离)标准用来衡量图像之间在颜色上的相似性。
颜色直方图所描述的是不同色彩在整幅图像中所占的比例,而并不关心每种色彩所处的空间位置,即无法描述图像中的对象或物体。颜色直方图特别适于描述那些难以进行自动分割的图像。颜色直方图是基于不同的颜色空间和坐标系。最常用的颜色空间是RGB颜色空间,原因在于大部分的数字图像都是用这种颜色空间表达的。然而,RGB空间结构,并不符合人们对颜色相似性的主观判断。因此,有人提出了基于HSV空间、Luv空间和Lab空间的颜色直方图,因为它们更接近于人们对颜色的主观认识。其中HSV空间是直方图最常用的颜色空间。它的三个分量分别代表色彩(Hue)、饱和度(S)和值(V)。
计算颜色直方图需要将颜色空间划分为若干个小的颜色区间,每个小区间成为直方图的一个bin。这个过程成为颜色量化(color quantization)。然后,通过计算颜色落在每个小区间内的像素数量可以得到颜色直方图。颜色量化有许多种方法,例如向量量化、聚类方法或者神经网络方法。
OpenCV中常用的直方图操作
1)归一化直方图
cvNormalizeHist(CvHistogram * hist,double factor); factor通常设为1
2)阈值函数
cvThreshHist(CvHistogram * hist,double factor);factor是一个开关阈值
3)复制直方图
void cvCopyHist(const CvHistogram * src,CvHistogram ** dst);
4)获得直方图的最小最大值
void cvGetMinMaxHistValue(const CvHistogram * hist,float * min_value, float * max_value,int * min_idx=NULL,int * max_idx=NULL);
5)计算直方图
void cvCalcHist(IplImage ** image,CvHistogram * hist,int accumulate=0,const CvArr * mask=NULL);
6)对比两个直方图
double cvCompareHist(const CvHIstogram *hist1,const CvHistogram *hist2,int method);
7)创建直方图
CvHistogram * cvCreateHist(int dims,int *size,int type, float ** ranges=NULL,int uniform=1);
举例:
int h_bins=30,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};
CvHistogram * hist;
hist=cvCreateHist(2,hist_size,CV_HIST_ARRAY,ranges,1);
8)在使用直方图之前给rangs设置数值
void cvSetHistBinRanges(CvHistogram * hist,float ** ranges,int uniform=1);
注意:
I)uniform 说明直方图是否有均匀的bin,如果设置为非零值,则直方图是均匀的,也可以设置为NULL,意味着rangs是未知的。
II)这个范围的用处是确定何时计算直方图或决定反向映射
9)释放直方图
如果想重用直方图,可以对其进行清零操作(即设置所有bins为0),或者使用通常的释放函数释放直方图
void cvClearHist(CvHistogram * hist);
void cvReleaseHist(CvHistogram ** hist);
10)根据已给出的数据创建直方图
CvHistogram * cvMakeHistHeaderForArray(int dims,int * sizes,CvHistogram * hist,float * data,float ** ranges=NULL);
11)访问直方图
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);
double cvQueryHistValue_nD(CvHistogram * hist,int * idxN);
12)反向投影
http://blog.163.com/chenliangren@126/blog/static/168305659201072761518425/
反向投影是一种记录像素点或者像素块如何适应直方图模型中分布的方式。
void cvCalcBackProject(IplImage ** image,CvArr * back_project,const CvHistogram * hist);
void cvCalcBackProjectPatch(IplImage ** image,CvArr * dst,CvSize patch_size,CvHistogram * hist ,int method,float factor);
应用:
I)如果我们有一个颜色直方图,可以利用反向投影在图像中找到该区域。
II)我们可以用函数cvCalcBackProject()计算一个像素是否是一个已知目标的一部分
III)也可以用函数cvCalcBackProjectPatch()计算一块区域是否包含已知的目标
13)画直方图
http://hi.baidu.com/devisdu/blog/item/2f73ff2d1f15403f349bf712.html
(二)反向投影图
反向投影图,用输入图像的某一位置上像素值(多维或灰度)对应在直方图的一个bin上的值来代替该像素值,所以得到的反向投影图是单通的。用统计学术语,输出图像象素点的值是观测数组在某个分布(直方图)下的概率。图像的反向投影图,简单理解,就是一张概率密度图。反向投影图上的像素点表征着一种概率,如果这个点越亮,就说明这个点属于物体的概率越大。
例子:
Image=
0 1 2 3
4 5 6 7
8 9 10 11
8 9 14 15
Histogram=
4 4 6 2(3)反向投影图
Back_Projection=
4 4 4 4
4 4 4 4
6 6 6 6
6 6 2 2
例如位置(0,0)上的像素值为0,对应的bin为[0,3),所以反向直方图在该位置上的值这个bin的值4。
用到的函数:
calcBackProject。
calcBackProject的输入为图像及其直方图,输出与待跟踪图像大小相同,每一个像素点表示该点为目标区域的概率。这个点越亮,该点属于物体的概率越大。
测试代码如下: #include <iostream> using namespace std; #include <iomanip> #include <highgui.h> #include <cv.h> int main() { uchar data[]={0,1,2,3,4,5,6,7,8,9,10,11,8,9,14,15}; //图像数据 CvMat mat=cvMat(4,4,CV_8UC1,data); IplImage g_img; //灰度图 cvGetImage(&mat,&g_img); //打印图像数据 cout<<"Image="<<endl; for(int i=0;i<g_img.height;i++) { uchar* ptr=(uchar*)(g_img.imageData+i*g_img.widthStep); for(int j=0;j<g_img.width;j++) { cout<<setw(3)<<setprecision(2)<<(int)ptr[j]<<" "; } cout<<endl; } //计算图像直方图 IplImage* imgs[]={&g_img}; int g_bin=4; int size[]={g_bin}; float g_ranges[]={0,4,8,12,16}; float* ranges[]={g_ranges}; CvHistogram* hist=cvCreateHist(1,size,CV_HIST_ARRAY,ranges,0); cvCalcHist(imgs,hist); //打印图像直方图数据 cout<<"Histogram="<<endl; for(int i=0;i<g_bin;i++) { cout<<setw(3)<<setprecision(2)<<*cvGetHistValue_1D(hist,i)<<" "; } cout<<endl; //计算反向投影图 IplImage* back_project=cvCreateImage(cvGetSize(&g_img),g_img.depth,1); cvCalcBackProject(imgs,back_project,hist); //打印反向投影图数据 cout<<"Back_Projection="<<endl; for(int i=0;i<back_project->height;i++) { uchar* ptr=(uchar*)(back_project->imageData+i*back_project->widthStep); for(int j=0;j<back_project->width;j++) { cout<<setw(3)<<setprecision(2)<<(int)ptr[j]<<" "; } cout<<endl; } }
反向投影的作用:反向投影用于在输入图像(通常较大)中查找特定图像(通常较小或者仅1个像素,以下将其称为模板图像)最匹配的点或者区域,也就是定位模板图像出现在输入图像的位置。