收入囊中
- meanshift图像聚类
- meanshift object detect
葵花宝典
今天有点累,理论就讲少点吧T_T
meanshift中文是均值飘逸,就是给定一个点,然后会移动到概率密度最大的地方。
对于图像,什么是概率密度最大?
我们可以定义很多要素:
距离
RGB
HSV
下面我有个例子,就是用距离(x,y)和HSV(h,s,v)作图像聚类的。
于是我们有5个要素,当前点与其他点的距离,HSV越接近,则概率密度越高。
假定我们有一点(m,n),如何选择下一个点呢,如何在一个矩形中找到概率密度最高的那个点?
top,down,left,right是我们选的矩形,(m,n)是中心
for(s=top;s<=down;s++){ for(t=left;t<=right;t++) { ws=(s-m)*(s-m)+(t-n)*(t-n);//spatial information ws/=(hs*hs); ws=exp(-ws); wr=(data[s*step+t*channels]-data[m*step+n*channels])*(data[s*step+t*channels]-data[m*step+n*channels]); wr+=(data[s*step+t*channels+1]-data[m*step+n*channels+1])*(data[s*step+t*channels+1]-data[m*step+n*channels+1]); wr+=(data[s*step+t*channels+2]-data[m*step+n*channels+2])*(data[s*step+t*channels+2]-data[m*step+n*channels+2]); wr/=(hr*hr); if(wr>1) wr=0.; else wr=exp(-wr); sumw+=wr*ws; for(k=0;k<5;k++)//try y[1][k]+=oridata[s*width+t][k]*wr*ws; } } for(k=0;k<5;k++) //try y[1][k]/=sumw; //下一个要到的点 m=(int)(y[1][0]+0.5); n=(int)(y[1][1]+0.5);
假设我们有了反向投影图,再用meanshift,不就能找到图像中最接近当前区域的位置了么~~
图像聚类
用meanshift可以实现图像聚类,因为本次重点是mean shift与back project的结合,所以关于聚类我就直接贴上代码了
算法超级easy,就是循环图像,对点作mean shift,每个点最大迭代100次,一直到点不动(收敛)为止,然后用vector记录下移动过程,将收敛点的颜色赋值给这些点,并记录哪些点已经赋值过了,下次就不用循环直接continue.
hs,hr是寻找的矩形大小,比EPSLON小就收敛。
note:是用x,y和HSV
//#include <stdafx.h> #include <cxcore.h> #include <cv.h> #include <highgui.h> #include <stdio.h> #include <vector> #include <utility> using namespace cv; using namespace std; #define hs 25 #define hr 25 #define maxstep 100 #define EPSLON 0.01 #define width 1920 #define height 1080 float oridata[height*width][5];//try int visited[height][width]; int main(int argc,char **argv) { IplImage *oriImg,*luvImg,*fltImg,*afterImg; char *filename = argv[1]; oriImg=cvLoadImage(filename,1); if(!oriImg){ printf("cannot load the file.\n"); return -1; } luvImg=cvCreateImage(cvSize(width,height),oriImg->depth,oriImg->nChannels); fltImg=cvCreateImage(cvSize(width,height),oriImg->depth,oriImg->nChannels); afterImg=cvCreateImage(cvSize(width,height),oriImg->depth,oriImg->nChannels); cvCvtColor(oriImg,luvImg,CV_RGB2Luv); uchar *data,*newdata; int channels, step,depth; depth=luvImg->depth; step=luvImg->widthStep; channels=luvImg->nChannels; data=(uchar *) luvImg->imageData; newdata=(uchar *) fltImg->imageData; int i,j,s,t,k,m,n,index; int top,down,left,right; float ws,wr; float mhlength; float sumw; float y[2][5];//try, otherwise the second is 5 for(i=0;i<height;i++) { for(j=0;j<width;j++) { oridata[i*width+j][0]=i; oridata[i*width+j][1]=j; visited[i][j]=0; oridata[i*width+j][2]=data[i*step+j*channels]; oridata[i*width+j][3]=data[i*step+j*channels+1]; oridata[i*width+j][4]=data[i*step+j*channels+2]; } } for(i=0;i<height;i++) { for(j=0;j<width;j++) { if(visited[i][j]) continue; m=i;n=j;//当前的中心点 for(k=0;k<5;k++)//try y[0][k]=oridata[i*width+j][k];//向量的初始值 vector<pair<int,int> >vss; for(index=0;index<maxstep;index++)//对当前的结点而言,最多迭代100次 { pair<int,int>newone; newone = make_pair(m,n); vss.push_back(newone); for(k=0;k<5;k++)//try y[1][k]=0; mhlength=0.; sumw=0.; top=m-hs; down=m+hs; left=n-hs; right=n+hs; if(top<0) top =0; if(down>height-1) down=height-1; if(left<0) left=0; if(right>width-1) right=width-1; for(s=top;s<=down;s++) { for(t=left;t<=right;t++) { ws=(s-m)*(s-m)+(t-n)*(t-n);//spatial information ws/=(hs*hs); ws=exp(-ws); //ws=1-ws+(ws*ws)/2-(ws*ws*ws)/6+(ws*ws*ws*ws)/24-(ws*ws*ws*ws*ws)/120; wr=(data[s*step+t*channels]-data[m*step+n*channels])*(data[s*step+t*channels]-data[m*step+n*channels]); wr+=(data[s*step+t*channels+1]-data[m*step+n*channels+1])*(data[s*step+t*channels+1]-data[m*step+n*channels+1]); wr+=(data[s*step+t*channels+2]-data[m*step+n*channels+2])*(data[s*step+t*channels+2]-data[m*step+n*channels+2]); wr/=(hr*hr); if(wr>1) wr=0.; else wr=exp(-wr); sumw+=wr*ws; for(k=0;k<5;k++)//try y[1][k]+=oridata[s*width+t][k]*wr*ws; } } for(k=0;k<5;k++) //try y[1][k]/=sumw; //下一个要到的点 m=(int)(y[1][0]+0.5); n=(int)(y[1][1]+0.5); if(visited[m][n]) break; if(m<hs||m>height-hs||n<hs||n>width-hs) break; else{ for(k=0;k<5;k++)//try { mhlength+=(y[1][k]-y[0][k])*(y[1][k]-y[0][k]); y[0][k]=y[1][k]; } mhlength=sqrt(mhlength); if(mhlength<EPSLON)//找到极值点 break; } }//邻域处理结束 for(int ii = 0;ii < vss.size();ii++) { int row = vss[ii].first; int line = vss[ii].second; newdata[row*step+line*channels]=int(y[1][2]+0.5);//try newdata[row*step+line*channels+1]=int(y[1][3]+0.5); newdata[row*step+line*channels+2]=int(y[1][4]+0.5); visited[row][line]=1; } } } cvCvtColor(fltImg,afterImg,CV_Luv2RGB); cvNamedWindow("ori",1); cvNamedWindow("filtered",1); cvShowImage("ori",oriImg); cvShowImage("filtered",afterImg); cvWaitKey(0); cvReleaseImage(&oriImg); cvDestroyWindow("image"); cvReleaseImage(&afterImg); cvDestroyWindow("filtered"); return 0; }
初识API
-
C++: int meanShift(InputArray probImage,
Rect& window, TermCriteria criteria)
-
- probImage – 反向投影矩阵
- window – 一开始的窗口,就是自己用矩形圈起来的地方
- criteria – Stop criteria for the iterative search algorithm.
返回值:收敛时的迭代次数
TermCriteria::TermCriteria
The constructors.
- C++: TermCriteria::TermCriteria()
-
C++: TermCriteria::TermCriteria(int type,
int maxCount, double epsilon)
-
C++: TermCriteria::TermCriteria(const
CvTermCriteria& criteria) -
- type – The type of termination criteria: TermCriteria::COUNT, TermCriteria::EPS or TermCriteria::COUNT +TermCriteria::EPS.
- maxCount – The maximum number of iterations or elements to compute.
- epsilon – The desired accuracy or change in parameters at which the iterative algorithm stops.
- criteria – Termination criteria in the deprecated CvTermCriteria format.
举个例子:假设我们对(110,260)坐标处宽35长40的方框感兴趣,最大迭代10次,差值(精确度)为0.01
result是我们的反向投影矩阵,那就可以如下调用
Rect rect(110,260,35,40);
TermCriteria criteria(TermCriteria::MAX_ITER,10,0.01);
meanShift(result,rect,criteria);
荷枪实弹
下面我附上完整代码,并有详细注释
#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/video/tracking.hpp" #include <iostream> using namespace cv; using namespace std; class ColorHistogram { private: int histSize[3]; float hranges[2]; const float* ranges[3]; int channels[3]; public: ColorHistogram() { // Prepare arguments for a color histogram histSize[0]= histSize[1]= histSize[2]= 256; hranges[0]= 0.0; // BRG range hranges[1]= 255.0; ranges[0]= hranges; // all channels have the same range ranges[1]= hranges; ranges[2]= hranges; channels[0]= 0; // the three channels channels[1]= 1; channels[2]= 2; } //这里获取HSV的第一个通道的直方图 Mat getHueHistogram(const cv::Mat &image,int minSaturation=0) { Mat hist; //色彩空间转换 Mat hsv; cvtColor(image, hsv, CV_BGR2HSV); // mask,要注意哦 Mat mask; if (minSaturation>0) { // 三通道分开 vector<Mat> v; split(hsv,v); // 去除低饱和的值 threshold(v[1],mask,minSaturation,255,THRESH_BINARY); } hranges[0]= 0.0; //hue的range是[0,180] hranges[1]= 180.0; channels[0]= 0;calcHist(&hsv,1,channels,mask,hist,1,histSize,ranges);//注意,mask不为0的地方计算在内,也就是高饱和的Hue计算在内 return hist; } }; class ContentFinder { private: float hranges[2]; const float* ranges[3]; int channels[3]; float threshold; Mat histogram; public: ContentFinder() : threshold(-1.0f) { hranges[0]= 0.0; // range [0,255] hranges[1]= 255.0; channels[0]= 0; // the three channels channels[1]= 1; channels[2]= 2; ranges[0]= hranges; // all channels have same range ranges[1]= hranges; ranges[2]= hranges; } // Sets the reference histogram void setHistogram(const Mat& h) { histogram= h; normalize(histogram,histogram,1.0); } cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels, int dim) { cv::Mat result; hranges[0]= minValue; hranges[1]= maxValue; ranges[0]= hranges; // all channels have same range ranges[1]= hranges; ranges[2]= hranges; calcBackProject(&image,1,channels,histogram,result,ranges,255.0); return result; } }; int main( int, char** argv ) { Mat image= cv::imread("baboon1.jpg"); // Baboon‘s face ROI Mat imageROI= image(cv::Rect(110,260,35,40)); // 获得Hue直方图 int minSat=65; ColorHistogram hc; Mat colorhist = hc.getHueHistogram(imageROI,minSat); ContentFinder finder; finder.setHistogram(colorhist); image= cv::imread("baboon3.jpg"); // 色彩空间转换 Mat hsv; cvtColor(image, hsv, CV_BGR2HSV); vector<Mat> v; split(hsv,v); //除去低饱和度 threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY); // 获得反向投影 int ch[1]={0}; Mat result= finder.find(hsv,0.0f,180.0f,ch,1); // 除去低饱和度的点 bitwise_and(result,v[1],result); Rect rect(110,260,35,40); rectangle(image, rect, Scalar(0,0,255)); TermCriteria criteria(TermCriteria::MAX_ITER,10,0.01); meanShift(result,rect,criteria); rectangle(image, rect, cv::Scalar(0,255,0)); namedWindow("result"); imshow("result",image); waitKey(0); return 0; }看下效果
举一反三
camshift算法是对meanshift算法的改进,它先用mean shift找到物体,再调整窗的大小,然后可以旋转窗找到最合适的旋转角度
计算机视觉讨论群:162501053
转载请注明:http://blog.csdn.net/abcd1992719g
OpenCV2马拉松第11圈——meanshift与直方图反向投影
时间: 2024-08-27 19:01:25