TLD(Tracking-Learning-Detection)算法学习与源码解析(三)之 tld.cpp源码解析
2014-03-05 17:52
453 查看
本序列文章的目的是总结一下这段时间所学到的,主要分为以下几部分,本章是第三部分。
1 算法概述
2 runtld.cpp源码解析
3 tld.cpp源码解析
4 LKTracker(重点)
5 FerNNClassifier.cpp源码解析(重点)
6 tld_utils.cpp源码解析
tld的算法在tld.cpp这个文件体现,其中用到了跟踪模块和分类器,后两者在另外的文件实现。
这个文件,围绕下面这两个函数看,其他函数都是被这两个函数调用。
TLD::init这个函数是处理第一帧的图像
TLD::processFrame是处理以后每一帧图像
看懂了init再看processFrame。
注:
原作者是用matlab实现的,我分析的源码是其他大神用c++和opencv实现的,源码可以从
https://github.com/arthurv/OpenTLD或者https://github.com/alantrrs/OpenTLD下载
本序列参考了zouxy09同学的序列文章,在此表示感谢
http://blog.csdn.net/zouxy09/article/details/7893011
1 算法概述
2 runtld.cpp源码解析
3 tld.cpp源码解析
4 LKTracker(重点)
5 FerNNClassifier.cpp源码解析(重点)
6 tld_utils.cpp源码解析
tld的算法在tld.cpp这个文件体现,其中用到了跟踪模块和分类器,后两者在另外的文件实现。
这个文件,围绕下面这两个函数看,其他函数都是被这两个函数调用。
TLD::init这个函数是处理第一帧的图像
TLD::processFrame是处理以后每一帧图像
看懂了init再看processFrame。
/* * TLD.cpp * * Created on: Jun 9, 2011 * Author: alantrrs */ #include <TLD.h> #include <stdio.h> using namespace cv; using namespace std; TLD::TLD() { } TLD::TLD(const FileNode& file){ read(file); } /** *初始化相关参数 */ void TLD::read(const FileNode& file){ ///Bounding Box Parameters min_win = (int)file["min_win"]; ///Genarator Parameters //initial parameters for positive examples patch_size = (int)file["patch_size"]; num_closest_init = (int)file["num_closest_init"]; num_warps_init = (int)file["num_warps_init"]; noise_init = (int)file["noise_init"]; angle_init = (float)file["angle_init"]; shift_init = (float)file["shift_init"]; scale_init = (float)file["scale_init"]; //update parameters for positive examples num_closest_update = (int)file["num_closest_update"]; num_warps_update = (int)file["num_warps_update"]; noise_update = (int)file["noise_update"]; angle_update = (float)file["angle_update"]; shift_update = (float)file["shift_update"]; scale_update = (float)file["scale_update"]; //parameters for negative examples bad_overlap = (float)file["overlap"]; bad_patches = (int)file["num_patches"]; classifier.read(file); } /** *根据第一帧图像来初始化TLD *处理以后的图像跟第一帧差不多,只不过第一帧少了纠正的过程 */ void TLD::init(const Mat& frame1,const Rect& box,FILE* bb_file){ //bb_file = fopen("bounding_boxes.txt","w"); //Get Bounding Boxes buildGrid(frame1,box);//在图片frame1算出所有的扫描窗口,并计算出这些扫描窗口与box的重叠度。 //当然还将frame1缩放成不同尺寸 printf("Created %d bounding boxes\n",(int)grid.size()); ///Preparation //allocation iisum.create(frame1.rows+1,frame1.cols+1,CV_32F); iisqsum.create(frame1.rows+1,frame1.cols+1,CV_64F); dconf.reserve(100); dbb.reserve(100); bbox_step =7; //tmp.conf.reserve(grid.size()); tmp.conf = vector<float>(grid.size()); tmp.patt = vector<vector<int> >(grid.size(),vector<int>(10,0)); //tmp.patt.reserve(grid.size()); dt.bb.reserve(grid.size()); good_boxes.reserve(grid.size()); bad_boxes.reserve(grid.size()); pEx.create(patch_size,patch_size,CV_64F); //Init Generator generator = PatchGenerator (0,0,noise_init,true,1-scale_init,1+scale_init,-angle_init*CV_PI/180,angle_init*CV_PI/180,-angle_init*CV_PI/180,angle_init*CV_PI/180); getOverlappingBoxes(box,num_closest_init);//根据重叠读度找出好的窗口和坏的窗口 printf("Found %d good boxes, %d bad boxes\n",(int)good_boxes.size(),(int)bad_boxes.size()); printf("Best Box: %d %d %d %d\n",best_box.x,best_box.y,best_box.width,best_box.height); printf("Bounding box hull: %d %d %d %d\n",bbhull.x,bbhull.y,bbhull.width,bbhull.height); //Correct Bounding Box lastbox=best_box; lastconf=1; lastvalid=true; //Print fprintf(bb_file,"%d,%d,%d,%d,%f\n",lastbox.x,lastbox.y,lastbox.br().x,lastbox.br().y,lastconf); //Prepare Classifier classifier.prepare(scales);//prepare这个函数主要是初始化集合分类器模块 ///Generate Data // Generate positive data generatePositiveData(frame1,num_warps_init);//产生正样本fern特征和nn特征 //每个good_box都进行20次这种几何变换,那么10个box将产生200个仿射变换的bounding box,作为正样本 // Set variance threshold Scalar stdev, mean; meanStdDev(frame1(best_box),mean,stdev); integral(frame1,iisum,iisqsum);//计算积分图,为了以后算方差使用 var = pow(stdev.val[0],2)*0.5; //getVar(best_box,iisum,iisqsum); cout << "variance: " << var << endl; //check variance double vr = getVar(best_box,iisum,iisqsum)*0.5; cout << "check variance: " << vr << endl; // Generate negative data generateNegativeData(frame1);//产生负样本fern特征和nn特征 //Split Negative Ferns into Training and Testing sets (they are already shuffled)把负样本的fern特征分为训练集和测试集 int half = (int)nX.size()*0.5f; nXT.assign(nX.begin()+half,nX.end()); nX.resize(half); ///Split Negative NN Examples into Training and Testing sets把负样本的nn特征分为训练集和测试集 half = (int)nEx.size()*0.5f; nExT.assign(nEx.begin()+half,nEx.end()); nEx.resize(half); //Merge Negative Data with Positive Data and shuffle it把正负样本的数据放到一起 vector<pair<vector<int>,int> > ferns_data(nX.size()+pX.size()); vector<int> idx = index_shuffle(0,ferns_data.size()); int a=0; for (int i=0;i<pX.size();i++){ ferns_data[idx[a]] = pX[i]; a++; } for (int i=0;i<nX.size();i++){ ferns_data[idx[a]] = nX[i]; a++; } //Data already have been shuffled, just putting it in the same vector vector<cv::Mat> nn_data(nEx.size()+1); nn_data[0] = pEx; for (int i=0;i<nEx.size();i++){ nn_data[i+1]= nEx[i]; } ///Training classifier.trainF(ferns_data,2); //bootstrap = 2 训练 fern分类器(森林) classifier.trainNN(nn_data);//训练 最近邻(nnc)分类器 ///Threshold Evaluation on testing sets classifier.evaluateTh(nXT,nExT);//重新计算阈值 } /* Generate Positive data产生正样本的fern特征和nn特征 * Inputs: * - good_boxes (bbP) * - best_box (bbP0) * - frame (im0) * Outputs: * - Positive fern features (pX) * - Positive NN examples (pEx) */ void TLD::generatePositiveData(const Mat& frame, int num_warps){ Scalar mean; Scalar stdev; getPattern(frame(best_box),pEx,mean,stdev);//把均值化好的放到pEx,其实nn分类器的样本就这么一个 //frame(best_box)看意思是从整个frame找出best_box对应的patch //原来box的类继承了 Rect //Get Fern features on warped patches Mat img; Mat warped; GaussianBlur(frame,img,Size(9,9),1.5);//高斯滤波保存到img warped = img(bbhull);//算出包裹所有good_box对应的patch RNG& rng = theRNG(); Point2f pt(bbhull.x+(bbhull.width-1)*0.5f,bbhull.y+(bbhull.height-1)*0.5f); vector<int> fern(classifier.getNumStructs()); pX.clear(); Mat patch; if (pX.capacity()<num_warps*good_boxes.size()) pX.reserve(num_warps*good_boxes.size());//限制正fern特征的数量 int idx; for (int i=0;i<num_warps;i++){//总觉得这段代码有问题,img是大图,warped小图 if (i>0) generator(frame,pt,warped,bbhull.size(),rng);//对图像区域进行仿射变换 for (int b=0;b<good_boxes.size();b++){ idx=good_boxes[b]; patch = img(grid[idx]);//warped引用对img的局部引用,warped变了,img也变 classifier.getFeatures(patch,grid[idx].sidx,fern);//函数得到输入的patch的特征fern(13位的二进制代码) pX.push_back(make_pair(fern,1)); } } printf("Positive examples generated: ferns:%d NN:1\n",(int)pX.size()); } /** * 算出期望mean和标准差,把img均值化为0,缩放为15*15 */ void TLD::getPattern(const Mat& img, Mat& pattern,Scalar& mean,Scalar& stdev){ //Output: resized Zero-Mean patch resize(img,pattern,Size(patch_size,patch_size)); meanStdDev(pattern,mean,stdev); pattern.convertTo(pattern,CV_32F); pattern = pattern-mean.val[0]; } /* 产生负样本的fern特征和nn特征 * Inputs: * - Image * - bad_boxes (Boxes far from the bounding box) * - variance (pEx variance) * Outputs * - Negative fern features (nX) * - Negative NN examples (nEx) */ void TLD::generateNegativeData(const Mat& frame){ random_shuffle(bad_boxes.begin(),bad_boxes.end());//Random shuffle bad_boxes indexes int idx; //Get Fern Features of the boxes with big variance (calculated using integral images) int a=0; //int num = std::min((int)bad_boxes.size(),(int)bad_patches*100); //limits the size of bad_boxes to try printf("negative data generation started.\n"); vector<int> fern(classifier.getNumStructs()); nX.reserve(bad_boxes.size()); Mat patch; for (int j=0;j<bad_boxes.size();j++){ idx = bad_boxes[j]; if (getVar(grid[idx],iisum,iisqsum)<var*0.5f)//去掉方差较小的负样本 continue; patch = frame(grid[idx]); classifier.getFeatures(patch,grid[idx].sidx,fern); nX.push_back(make_pair(fern,0)); a++; } printf("Negative examples generated: ferns: %d ",a); //random_shuffle(bad_boxes.begin(),bad_boxes.begin()+bad_patches);//Randomly selects 'bad_patches' and get the patterns for NN; Scalar dum1, dum2; nEx=vector<Mat>(bad_patches); for (int i=0;i<bad_patches;i++){ idx=bad_boxes[i]; patch = frame(grid[idx]); getPattern(patch,nEx[i],dum1,dum2); } printf("NN: %d\n",(int)nEx.size()); } /** * 该函数通过积分图像计算输入的box的方差 */ double TLD::getVar(const BoundingBox& box,const Mat& sum,const Mat& sqsum){ double brs = sum.at<int>(box.y+box.height,box.x+box.width); double bls = sum.at<int>(box.y+box.height,box.x); double trs = sum.at<int>(box.y,box.x+box.width); double tls = sum.at<int>(box.y,box.x); double brsq = sqsum.at<double>(box.y+box.height,box.x+box.width); double blsq = sqsum.at<double>(box.y+box.height,box.x); double trsq = sqsum.at<double>(box.y,box.x+box.width); double tlsq = sqsum.at<double>(box.y,box.x); double mean = (brs+tls-trs-bls)/((double)box.area()); double sqmean = (brsq+tlsq-trsq-blsq)/((double)box.area()); return sqmean-mean*mean; } /** * 输出: * points1:按照固定步长在某个box产生的网格点 * points2:由img1中points1在img2跟踪预测到的 * bbnext:预测到的box */ void TLD::processFrame(const cv::Mat& img1,const cv::Mat& img2,vector<Point2f>& points1,vector<Point2f>& points2,BoundingBox& bbnext,bool& lastboxfound, bool tl, FILE* bb_file){ vector<BoundingBox> cbb; vector<float> cconf; int confident_detections=0; int didx; //detection index ///Track if(lastboxfound && tl){ track(img1,img2,points1,points2);//跟踪 } else{ tracked = false; } ///Detect detect(img2);//检测,可能会检测到多个目标窗口 ///Integration if (tracked){//如果跟踪到了 bbnext=tbb; lastconf=tconf; lastvalid=tvalid; printf("Tracked\n"); if(detected){ // if Detected clusterConf(dbb,dconf,cbb,cconf); // cluster detections //dbb 聚类前的boxes,cbb聚类后 //通过 重叠度 对检测器检测到的目标bounding box进行聚类,每个类其重叠度小于0.5 //这个聚类算法细节还没看,先知道作用就好。(*^__^*) 嘻嘻……,肯定比我之前写的聚类算法好,到时再看 printf("Found %d clusters\n",(int)cbb.size()); for (int i=0;i<cbb.size();i++){ if (bbOverlap(tbb,cbb[i])<0.5 && cconf[i]>tconf){ // Get index of a clusters that is far from tracker and are more confident than the tracker //找到与跟踪器跟踪到的box距离比较远的类(检测器检测到的box),而且它的相关相似度比跟踪器的要大 confident_detections++; didx=i; //detection index } } if (confident_detections==1){ //if there is ONE such a cluster, re-initialize the tracker //如果只有一个满足上述条件的box,那么就用这个目标box来重新初始化跟踪器(也就是用检测器的结果去纠正跟踪器) printf("Found a better match..reinitializing tracking\n"); bbnext=cbb[didx]; lastconf=cconf[didx]; lastvalid=false; } else { printf("%d confident cluster was found\n",confident_detections); int cx=0,cy=0,cw=0,ch=0; int close_detections=0; for (int i=0;i<dbb.size();i++){ if(bbOverlap(tbb,dbb[i])>0.7){ // Get mean of close detections cx += dbb[i].x; cy +=dbb[i].y; cw += dbb[i].width; ch += dbb[i].height; close_detections++; printf("weighted detection: %d %d %d %d\n",dbb[i].x,dbb[i].y,dbb[i].width,dbb[i].height); } } if (close_detections>0){ bbnext.x = cvRound((float)(10*tbb.x+cx)/(float)(10+close_detections)); // weighted average trackers trajectory with the close detections bbnext.y = cvRound((float)(10*tbb.y+cy)/(float)(10+close_detections)); bbnext.width = cvRound((float)(10*tbb.width+cw)/(float)(10+close_detections)); bbnext.height = cvRound((float)(10*tbb.height+ch)/(float)(10+close_detections)); printf("Tracker bb: %d %d %d %d\n",tbb.x,tbb.y,tbb.width,tbb.height); printf("Average bb: %d %d %d %d\n",bbnext.x,bbnext.y,bbnext.width,bbnext.height); printf("Weighting %d close detection(s) with tracker..\n",close_detections); } else{ printf("%d close detections were found\n",close_detections); } } } } else{ // If NOT tracking printf("Not tracking..\n"); lastboxfound = false; lastvalid = false; if(detected){ // and detector is defined clusterConf(dbb,dconf,cbb,cconf); // cluster detections printf("Found %d clusters\n",(int)cbb.size()); if (cconf.size()==1){ bbnext=cbb[0]; lastconf=cconf[0]; printf("Confident detection..reinitializing tracker\n"); lastboxfound = true; } } } lastbox=bbnext; if (lastboxfound) fprintf(bb_file,"%d,%d,%d,%d,%f\n",lastbox.x,lastbox.y,lastbox.br().x,lastbox.br().y,lastconf); else fprintf(bb_file,"NaN,NaN,NaN,NaN,NaN\n"); if (lastvalid && tl) learn(img2); } /** * 跟踪找出预测的box和自信度,跟踪到的点等 */ void TLD::track(const Mat& img1, const Mat& img2,vector<Point2f>& points1,vector<Point2f>& points2){ /*Inputs: * -current frame(img2), last frame(img1), last Bbox(bbox_f[0]). *Outputs: *- Confidence(tconf), Predicted bounding box(tbb),Validity(tvalid), points2 (for display purposes only) */ //Generate points bbPoints(points1,lastbox);//在box中,按照固定步长产生一些点放到points1中 if (points1.size()<1){ printf("BB= %d %d %d %d, Points not generated\n",lastbox.x,lastbox.y,lastbox.width,lastbox.height); tvalid=false; tracked=false; return; } vector<Point2f> points = points1; //Frame-to-frame tracking with forward-backward error cheking tracked = tracker.trackf2f(img1,img2,points,points2);//在一张图中跟踪另一张图中的一些点,根据points找出points2 if (tracked){ //Bounding box prediction bbPredict(points,points2,lastbox,tbb);//预测到的box放到tbb存储 if (tracker.getFB()>10 || tbb.x>img2.cols || tbb.y>img2.rows || tbb.br().x < 1 || tbb.br().y <1){ tvalid =false; //too unstable prediction or bounding box out of image tracked = false; printf("Too unstable predictions FB error=%f\n",tracker.getFB()); return; } //Estimate Confidence and Validity Mat pattern; Scalar mean, stdev; BoundingBox bb; bb.x = max(tbb.x,0); bb.y = max(tbb.y,0); bb.width = min(min(img2.cols-tbb.x,tbb.width),min(tbb.width,tbb.br().x)); bb.height = min(min(img2.rows-tbb.y,tbb.height),min(tbb.height,tbb.br().y)); getPattern(img2(bb),pattern,mean,stdev); vector<int> isin; float dummy; classifier.NNConf(pattern,isin,dummy,tconf); //Conservative Similarity tvalid = lastvalid; if (tconf>classifier.thr_nn_valid){ tvalid =true; } } else printf("No points tracked\n"); } /** * 在box中,按照固定步长产生一些点放到points中 * 最多10*10个? */ void TLD::bbPoints(vector<cv::Point2f>& points,const BoundingBox& bb){ int max_pts=10; int margin_h=0; int margin_v=0; int stepx = ceil((bb.width-2*margin_h)/max_pts); int stepy = ceil((bb.height-2*margin_v)/max_pts); for (int y=bb.y+margin_v;y<bb.y+bb.height-margin_v;y+=stepy){ for (int x=bb.x+margin_h;x<bb.x+bb.width-margin_h;x+=stepx){ points.push_back(Point2f(x,y)); } } } //预测在某个box中 void TLD::bbPredict(const vector<cv::Point2f>& points1,const vector<cv::Point2f>& points2, const BoundingBox& bb1,BoundingBox& bb2) { int npoints = (int)points1.size(); vector<float> xoff(npoints); vector<float> yoff(npoints); printf("tracked points : %d\n",npoints); for (int i=0;i<npoints;i++){ xoff[i]=points2[i].x-points1[i].x; yoff[i]=points2[i].y-points1[i].y; } float dx = median(xoff); float dy = median(yoff); float s; if (npoints>1){ vector<float> d; d.reserve(npoints*(npoints-1)/2); for (int i=0;i<npoints;i++){ for (int j=i+1;j<npoints;j++){ d.push_back(norm(points2[i]-points2[j])/norm(points1[i]-points1[j])); } } s = median(d); } else { s = 1.0; } float s1 = 0.5*(s-1)*bb1.width; float s2 = 0.5*(s-1)*bb1.height; printf("s= %f s1= %f s2= %f \n",s,s1,s2); bb2.x = round( bb1.x + dx -s1); bb2.y = round( bb1.y + dy -s2); bb2.width = round(bb1.width*s); bb2.height = round(bb1.height*s); printf("predicted bb: %d %d %d %d\n",bb2.x,bb2.y,bb2.br().x,bb2.br().y); } /** * 扫描所有窗口,通过分类器进行预测,可能会找出多个目标窗口 */ void TLD::detect(const cv::Mat& frame){ //cleaning dbb.clear(); dconf.clear(); dt.bb.clear(); double t = (double)getTickCount(); Mat img(frame.rows,frame.cols,CV_8U); integral(frame,iisum,iisqsum); GaussianBlur(frame,img,Size(9,9),1.5); int numtrees = classifier.getNumStructs(); float fern_th = classifier.getFernTh(); vector <int> ferns(10); float conf; int a=0; Mat patch; for (int i=0;i<grid.size();i++){//FIXME: BottleNeck if (getVar(grid[i],iisum,iisqsum)>=var){ a++; patch = img(grid[i]); classifier.getFeatures(patch,grid[i].sidx,ferns); conf = classifier.measure_forest(ferns); tmp.conf[i]=conf; tmp.patt[i]=ferns; if (conf>numtrees*fern_th){ dt.bb.push_back(i); } } else tmp.conf[i]=0.0; } int detections = dt.bb.size(); printf("%d Bounding boxes passed the variance filter\n",a); printf("%d Initial detection from Fern Classifier\n",detections); if (detections>100){ nth_element(dt.bb.begin(),dt.bb.begin()+100,dt.bb.end(),CComparator(tmp.conf)); dt.bb.resize(100); detections=100; } // for (int i=0;i<detections;i++){ // drawBox(img,grid[dt.bb[i]]); // } // imshow("detections",img); if (detections==0){ detected=false; return; } printf("Fern detector made %d detections ",detections); t=(double)getTickCount()-t; printf("in %gms\n", t*1000/getTickFrequency()); // Initialize detection structure dt.patt = vector<vector<int> >(detections,vector<int>(10,0)); // Corresponding codes of the Ensemble Classifier dt.conf1 = vector<float>(detections); // Relative Similarity (for final nearest neighbour classifier) dt.conf2 =vector<float>(detections); // Conservative Similarity (for integration with tracker) dt.isin = vector<vector<int> >(detections,vector<int>(3,-1)); // Detected (isin=1) or rejected (isin=0) by nearest neighbour classifier dt.patch = vector<Mat>(detections,Mat(patch_size,patch_size,CV_32F));// Corresponding patches int idx; Scalar mean, stdev; float nn_th = classifier.getNNTh(); for (int i=0;i<detections;i++){ // for every remaining detection idx=dt.bb[i]; // Get the detected bounding box index patch = frame(grid[idx]); getPattern(patch,dt.patch[i],mean,stdev); // Get pattern within bounding box classifier.NNConf(dt.patch[i],dt.isin[i],dt.conf1[i],dt.conf2[i]); // Evaluate nearest neighbour classifier dt.patt[i]=tmp.patt[idx]; //printf("Testing feature %d, conf:%f isin:(%d|%d|%d)\n",i,dt.conf1[i],dt.isin[i][0],dt.isin[i][1],dt.isin[i][2]); if (dt.conf1[i]>nn_th){ // idx = dt.conf1 > tld.model.thr_nn; % get all indexes that made it through the nearest neighbour dbb.push_back(grid[idx]); // BB = dt.bb(:,idx); % bounding boxes dconf.push_back(dt.conf2[i]); // Conf = dt.conf2(:,idx); % conservative confidences } } // end if (dbb.size()>0){ printf("Found %d NN matches\n",(int)dbb.size()); detected=true; } else{ printf("No NN matches found.\n"); detected=false; } } void TLD::evaluate(){ } /** * 根据跟踪模块的结果来优化检测模块 */ void TLD::learn(const Mat& img){ printf("[Learning] "); ///Check consistency BoundingBox bb; bb.x = max(lastbox.x,0); bb.y = max(lastbox.y,0); bb.width = min(min(img.cols-lastbox.x,lastbox.width),min(lastbox.width,lastbox.br().x)); bb.height = min(min(img.rows-lastbox.y,lastbox.height),min(lastbox.height,lastbox.br().y)); Scalar mean, stdev; Mat pattern; getPattern(img(bb),pattern,mean,stdev);//将图片均值化为0,缩放为15*15 vector<int> isin; float dummy, conf; classifier.NNConf(pattern,isin,conf,dummy);//计算相似度 if (conf<0.5) { printf("Fast change..not training\n"); lastvalid =false; return; } if (pow(stdev.val[0],2)<var){ printf("Low variance..not training\n"); lastvalid=false; return; } if(isin[2]==1){ printf("Patch in negative data..not traing"); lastvalid=false; return; } /// Data generation for (int i=0;i<grid.size();i++){ grid[i].overlap = bbOverlap(lastbox,grid[i]);//计算跟目标窗口的重叠度 } vector<pair<vector<int>,int> > fern_examples; good_boxes.clear(); bad_boxes.clear(); getOverlappingBoxes(lastbox,num_closest_update);//找出跟目标窗口比较近的box if (good_boxes.size()>0) generatePositiveData(img,num_warps_update);//产生正样本数据 else{ lastvalid = false; printf("No good boxes..Not training"); return; } fern_examples.reserve(pX.size()+bad_boxes.size()); fern_examples.assign(pX.begin(),pX.end()); int idx; for (int i=0;i<bad_boxes.size();i++){ idx=bad_boxes[i]; if (tmp.conf[idx]>=1){ fern_examples.push_back(make_pair(tmp.patt[idx],0)); } } vector<Mat> nn_examples; nn_examples.reserve(dt.bb.size()+1); nn_examples.push_back(pEx); for (int i=0;i<dt.bb.size();i++){ idx = dt.bb[i]; if (bbOverlap(lastbox,grid[idx]) < bad_overlap) nn_examples.push_back(dt.patch[i]); } /// Classifiers update classifier.trainF(fern_examples,2); classifier.trainNN(nn_examples); classifier.show(); } /** * 在图片img算出所有的扫描窗口,并计算出这些扫描窗口与box的重叠度。 */ void TLD::buildGrid(const cv::Mat& img, const cv::Rect& box){ const float SHIFT = 0.1; const float SCALES[] = {0.16151,0.19381,0.23257,0.27908,0.33490,0.40188,0.48225, 0.57870,0.69444,0.83333,1,1.20000,1.44000,1.72800, 2.07360,2.48832,2.98598,3.58318,4.29982,5.15978,6.19174};//缩放成21种不同的size的img int width, height, min_bb_side; //Rect bbox; BoundingBox bbox; Size scale; int sc=0; for (int s=0;s<21;s++){ width = round(box.width*SCALES[s]); height = round(box.height*SCALES[s]); min_bb_side = min(height,width); if (min_bb_side < min_win || width > img.cols || height > img.rows) continue; scale.width = width; scale.height = height; scales.push_back(scale); for (int y=1;y<img.rows-height;y+=round(SHIFT*min_bb_side)){ for (int x=1;x<img.cols-width;x+=round(SHIFT*min_bb_side)){ bbox.x = x; bbox.y = y; bbox.width = width; bbox.height = height; bbox.overlap = bbOverlap(bbox,BoundingBox(box)); bbox.sidx = sc; grid.push_back(bbox); } } sc++; } } /** * 根据物理位置算出两个box的重叠度 */ float TLD::bbOverlap(const BoundingBox& box1,const BoundingBox& box2){ if (box1.x > box2.x+box2.width) { return 0.0; } if (box1.y > box2.y+box2.height) { return 0.0; } if (box1.x+box1.width < box2.x) { return 0.0; } if (box1.y+box1.height < box2.y) { return 0.0; } float colInt = min(box1.x+box1.width,box2.x+box2.width) - max(box1.x, box2.x); float rowInt = min(box1.y+box1.height,box2.y+box2.height) - max(box1.y,box2.y); float intersection = colInt * rowInt; float area1 = box1.width*box1.height; float area2 = box2.width*box2.height; return intersection / (area1 + area2 - intersection); } /** * 根据max_overlap把box分类为best,good,bad * good只保留最优的num_closest个 */ void TLD::getOverlappingBoxes(const cv::Rect& box1,int num_closest){ float max_overlap = 0; for (int i=0;i<grid.size();i++){ if (grid[i].overlap > max_overlap) { max_overlap = grid[i].overlap; best_box = grid[i]; } if (grid[i].overlap > 0.6){ good_boxes.push_back(i); } else if (grid[i].overlap < bad_overlap){ bad_boxes.push_back(i); } } //Get the best num_closest (10) boxes and puts them in good_boxes if (good_boxes.size()>num_closest){ std::nth_element(good_boxes.begin(),good_boxes.begin()+num_closest,good_boxes.end(),OComparator(grid)); good_boxes.resize(num_closest); } getBBHull(); } /** * 找出包含所有good_boxes的最小的矩形 */ void TLD::getBBHull(){ int x1=INT_MAX, x2=0; int y1=INT_MAX, y2=0; int idx; for (int i=0;i<good_boxes.size();i++){ idx= good_boxes[i]; x1=min(grid[idx].x,x1); y1=min(grid[idx].y,y1); x2=max(grid[idx].x+grid[idx].width,x2); y2=max(grid[idx].y+grid[idx].height,y2); } bbhull.x = x1; bbhull.y = y1; bbhull.width = x2-x1; bbhull.height = y2 -y1; } bool bbcomp(const BoundingBox& b1,const BoundingBox& b2){ TLD t; if (t.bbOverlap(b1,b2)<0.5) return false; else return true; } int TLD::clusterBB(const vector<BoundingBox>& dbb,vector<int>& indexes){ //FIXME: Conditional jump or move depends on uninitialised value(s) const int c = dbb.size(); //1. Build proximity matrix Mat D(c,c,CV_32F); float d; for (int i=0;i<c;i++){ for (int j=i+1;j<c;j++){ d = 1-bbOverlap(dbb[i],dbb[j]); D.at<float>(i,j) = d; D.at<float>(j,i) = d; } } //2. Initialize disjoint clustering float L[c-1]; //Level int nodes[c-1][2]; int belongs[c]; int m=c; for (int i=0;i<c;i++){ belongs[i]=i; } for (int it=0;it<c-1;it++){ //3. Find nearest neighbor float min_d = 1; int node_a, node_b; for (int i=0;i<D.rows;i++){ for (int j=i+1;j<D.cols;j++){ if (D.at<float>(i,j)<min_d && belongs[i]!=belongs[j]){ min_d = D.at<float>(i,j); node_a = i; node_b = j; } } } if (min_d>0.5){ int max_idx =0; bool visited; for (int j=0;j<c;j++){ visited = false; for(int i=0;i<2*c-1;i++){ if (belongs[j]==i){ indexes[j]=max_idx; visited = true; } } if (visited) max_idx++; } return max_idx; } //4. Merge clusters and assign level L[m]=min_d; nodes[it][0] = belongs[node_a]; nodes[it][1] = belongs[node_b]; for (int k=0;k<c;k++){ if (belongs[k]==belongs[node_a] || belongs[k]==belongs[node_b]) belongs[k]=m; } m++; } return 1; } /** * dbb聚类前的boxes * cbb聚类后的boxes */ void TLD::clusterConf(const vector<BoundingBox>& dbb,const vector<float>& dconf,vector<BoundingBox>& cbb,vector<float>& cconf){ int numbb =dbb.size(); vector<int> T; float space_thr = 0.5; int c=1; switch (numbb){ case 1: cbb=vector<BoundingBox>(1,dbb[0]); cconf=vector<float>(1,dconf[0]); return; break; case 2: T =vector<int>(2,0); if (1-bbOverlap(dbb[0],dbb[1])>space_thr){ T[1]=1; c=2; } break; default: T = vector<int>(numbb,0); c = partition(dbb,T,(*bbcomp)); //c = clusterBB(dbb,T); break; } cconf=vector<float>(c); cbb=vector<BoundingBox>(c); printf("Cluster indexes: "); BoundingBox bx; for (int i=0;i<c;i++){ float cnf=0; int N=0,mx=0,my=0,mw=0,mh=0; for (int j=0;j<T.size();j++){ if (T[j]==i){ printf("%d ",i); cnf=cnf+dconf[j]; mx=mx+dbb[j].x; my=my+dbb[j].y; mw=mw+dbb[j].width; mh=mh+dbb[j].height; N++; } } if (N>0){ cconf[i]=cnf/N; bx.x=cvRound(mx/N); bx.y=cvRound(my/N); bx.width=cvRound(mw/N); bx.height=cvRound(mh/N); cbb[i]=bx; } } printf("\n"); }
注:
原作者是用matlab实现的,我分析的源码是其他大神用c++和opencv实现的,源码可以从
https://github.com/arthurv/OpenTLD或者https://github.com/alantrrs/OpenTLD下载
本序列参考了zouxy09同学的序列文章,在此表示感谢
http://blog.csdn.net/zouxy09/article/details/7893011
相关文章推荐
- TLD(Tracking-Learning-Detection)算法学习与源码解析(二)之runtld.cpp源码解析
- TLD(Tracking-Learning-Detection)算法学习与源码解析(五)之FerNNClassifier.cpp源码解析
- TLD(Tracking-Learning-Detection)算法学习与源码解析(一)之算法概述
- TLD(Tracking-Learning-Detection)算法学习与源码解析(四)之LKTracker源码分析
- TLD(Tracking-Learning-Detection)学习与源码理解之(五)
- TLD(Tracking-Learning-Detection)学习与源码理解之(四)
- Tracking-Learning-Detection TLD解析三 - Learning学习(跟踪与检测的协调与更新)
- TLD参考--TLD(Tracking-Learning-Detection)学习与源码理解之(一)
- TLD(Tracking-Learning-Detection)学习与源码理解之(一)
- TLD(Tracking-Learning-Detection)学习与源码理解之(六)
- TLD(Tracking-Learning-Detection)学习与源码理解之
- TLD(Tracking-Learning-Detection)学习与源码理解之(二)
- TLD(Tracking-Learning-Detection)学习与源码理解之(七)
- TLD(Tracking-Learning-Detection)学习与源码理解之
- TLD(Tracking-Learning-Detection)学习与源码理解之(六)
- TLD(Tracking-Learning-Detection)学习与源码理解之(build)
- TLD(Tracking-Learning-Detection)学习与源码理解之(三)
- TLD(Tracking-Learning-Detection)学习与源码理解之(四)
- TLD(Tracking-Learning-Detection)学习与源码理解之(七)
- TLD(Tracking-Learning-Detection)学习与源码理解之(五)