TLD源码理解之TLD.cpp

TLD.cpp

[cpp]  view plain copy
  1. /* 
  2.  * TLD.cpp 
  3.  * 
  4.  *  Created on: Jun 9, 2011 
  5.  *      Author: alantrrs 
  6.  */  
  7.   
  8. #include <TLD.h>   
  9. #include <stdio.h>   
  10. using namespace cv;  
  11. using namespace std;  
  12.   
  13.   
  14. TLD::TLD()  
  15. {  
  16. }  
  17. TLD::TLD(const FileNode& file){  
  18.   read(file);  
  19. }  
  20.   
  21. void TLD::read(const FileNode& file){  
  22.   ///Bounding Box Parameters   
  23.   min_win = (int)file["min_win"];  
  24.   ///Genarator Parameters   
  25.   //initial parameters for positive examples  
  26.   patch_size = (int)file["patch_size"];  
  27.   num_closest_init = (int)file["num_closest_init"];  
  28.   num_warps_init = (int)file["num_warps_init"];  
  29.   noise_init = (int)file["noise_init"];  
  30.   angle_init = (float)file["angle_init"];  
  31.   shift_init = (float)file["shift_init"];  
  32.   scale_init = (float)file["scale_init"];  
  33.   //update parameters for positive examples  
  34.   num_closest_update = (int)file["num_closest_update"];  
  35.   num_warps_update = (int)file["num_warps_update"];  
  36.   noise_update = (int)file["noise_update"];  
  37.   angle_update = (float)file["angle_update"];  
  38.   shift_update = (float)file["shift_update"];  
  39.   scale_update = (float)file["scale_update"];  
  40.   //parameters for negative examples   
  41.   bad_overlap = (float)file["overlap"];  
  42.   bad_patches = (int)file["num_patches"];  
  43.   classifier.read(file);  
  44. }  
  45.   
  46. //此函数完成准备工作   
  47. void TLD::init(const Mat& frame1, const Rect& box, FILE* bb_file){  
  48.   //bb_file = fopen("bounding_boxes.txt","w");  
  49.   //Get Bounding Boxes   
  50.   //此函数根据传入的box(目标边界框)在传入的图像frame1中构建全部的扫描窗口,并计算重叠度  
  51.     buildGrid(frame1, box);  
  52.     printf("Created %d bounding boxes\n",(int)grid.size());  //vector的成员size()用于获取向量元素的个数  
  53.       
  54.   ///Preparation   
  55.   //allocation   
  56.   //积分图像,用以计算2bitBP特征(类似于haar特征的计算)   
  57.   //Mat的创建,方式有两种:1.调用create(行,列,类型)2.Mat(行,列,类型(值))。  
  58.   iisum.create(frame1.rows+1, frame1.cols+1, CV_32F);  
  59.   iisqsum.create(frame1.rows+1, frame1.cols+1, CV_64F);  
  60.     
  61.   //Detector data中定义:std::vector<float> dconf;  检测确信度??  
  62.   //vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector  
  63.   //的capacity同时也增加了它的size!reserve是容器预留空间,但在空间内不真正创建元素对象,  
  64.   //所以在没有添加新的对象之前,不能引用容器内的元素。   
  65.   //不管是调用resize还是reserve,二者对容器原有的元素都没有影响。  
  66.   //myVec.reserve( 100 );     // 新元素还没有构造, 此时不能用[]访问元素  
  67.   //myVec.resize( 100 );      // 用元素的默认构造函数构造了100个新的元素,可以直接操作新元素  
  68.   dconf.reserve(100);  
  69.   dbb.reserve(100);  
  70.   bbox_step =7;  
  71.     
  72.   //以下在Detector data中定义的容器都给其分配grid.size()大小(这个是一幅图像中全部的扫描窗口个数)的容量  
  73.   //Detector data中定义TempStruct tmp;    
  74.   //tmp.conf.reserve(grid.size());   
  75.   tmp.conf = vector<float>(grid.size());  
  76.   tmp.patt = vector<vector<int> >(grid.size(), vector<int>(10,0));  
  77.   //tmp.patt.reserve(grid.size());  
  78.   dt.bb.reserve(grid.size());  
  79.   good_boxes.reserve(grid.size());  
  80.   bad_boxes.reserve(grid.size());  
  81.     
  82.   //TLD中定义:cv::Mat pEx;  //positive NN example 大小为15*15图像片  
  83.   pEx.create(patch_size, patch_size, CV_64F);  
  84.     
  85.   //Init Generator   
  86.   //TLD中定义:cv::PatchGenerator generator;  //PatchGenerator类用来对图像区域进行仿射变换  
  87.   /* 
  88.   cv::PatchGenerator::PatchGenerator (     
  89.       double     _backgroundMin, 
  90.       double     _backgroundMax, 
  91.       double     _noiseRange, 
  92.       bool     _randomBlur = true, 
  93.       double     _lambdaMin = 0.6, 
  94.       double     _lambdaMax = 1.5, 
  95.       double     _thetaMin = -CV_PI, 
  96.       double     _thetaMax = CV_PI, 
  97.       double     _phiMin = -CV_PI, 
  98.       double     _phiMax = CV_PI  
  99.    )  
  100.    一般的用法是先初始化一个PatchGenerator的实例,然后RNG一个随机因子,再调用()运算符产生一个变换后的正样本。 
  101.   */  
  102.   generator = PatchGenerator (0,0,noise_init,true,1-scale_init,1+scale_init,-angle_init*CV_PI/180,  
  103.                                 angle_init*CV_PI/180,-angle_init*CV_PI/180,angle_init*CV_PI/180);  
  104.     
  105.   //此函数根据传入的box(目标边界框),在整帧图像中的全部窗口中寻找与该box距离最小(即最相似,  
  106.   //重叠度最大)的num_closest_init个窗口,然后把这些窗口 归入good_boxes容器  
  107.   //同时,把重叠度小于0.2的,归入 bad_boxes 容器  
  108.   //首先根据overlap的比例信息选出重复区域比例大于60%并且前num_closet_init= 10个的最接近box的RectBox,  
  109.   //相当于对RectBox进行筛选。并通过BBhull函数得到这些RectBox的最大边界。  
  110.   getOverlappingBoxes(box, num_closest_init);  
  111.   printf("Found %d good boxes, %d bad boxes\n",(int)good_boxes.size(),(int)bad_boxes.size());  
  112.   printf("Best Box: %d %d %d %d\n",best_box.x, best_box.y, best_box.width, best_box.height);  
  113.   printf("Bounding box hull: %d %d %d %d\n", bbhull.x, bbhull.y, bbhull.width, bbhull.height);  
  114.     
  115.   //Correct Bounding Box   
  116.   lastbox=best_box;  
  117.   lastconf=1;  
  118.   lastvalid=true;  
  119.   //Print   
  120.   fprintf(bb_file,"%d,%d,%d,%d,%f\n",lastbox.x,lastbox.y,lastbox.br().x,lastbox.br().y,lastconf);  
  121.     
  122.   //Prepare Classifier 准备分类器   
  123.   //scales容器里是所有扫描窗口的尺度,由buildGrid()函数初始化  
  124.   classifier.prepare(scales);  
  125.     
  126.   ///Generate Data   
  127.   // Generate positive data   
  128.   generatePositiveData(frame1, num_warps_init);  
  129.     
  130.   // Set variance threshold   
  131.   Scalar stdev, mean;  
  132.   //统计best_box的均值和标准差   
  133.   例如需要提取图像A的某个ROI(感兴趣区域,由矩形框)的话,用Mat类的B=img(ROI)即可提取  
  134.   //frame1(best_box)就表示在frame1中提取best_box区域(目标区域)的图像片  
  135.   meanStdDev(frame1(best_box), mean, stdev);  
  136.     
  137.   //利用积分图像去计算每个待检测窗口的方差   
  138.   //cvIntegral( const CvArr* image, CvArr* sum, CvArr* sqsum=NULL, CvArr* tilted_sum=NULL );  
  139.   //计算积分图像,输入图像,sum积分图像, W+1×H+1,sqsum对象素值平方的积分图像,tilted_sum旋转45度的积分图像  
  140.   //利用积分图像,可以计算在某象素的上-右方的或者旋转的矩形区域中进行求和、求均值以及标准方差的计算,  
  141.   //并且保证运算的复杂度为O(1)。     
  142.   integral(frame1, iisum, iisqsum);  
  143.   //级联分类器模块一:方差检测模块,利用积分图计算每个待检测窗口的方差,方差大于var阈值(目标patch方差的50%)的,  
  144.   //则认为其含有前景目标方差;var 为标准差的平方   
  145.   var = pow(stdev.val[0],2) * 0.5; //getVar(best_box,iisum,iisqsum);  
  146.   cout << "variance: " << var << endl;  
  147.     
  148.   //check variance   
  149.   //getVar函数通过积分图像计算输入的best_box的方差  
  150.   double vr =  getVar(best_box, iisum, iisqsum)*0.5;  
  151.   cout << "check variance: " << vr << endl;  
  152.     
  153.   // Generate negative data   
  154.   generateNegativeData(frame1);  
  155.     
  156.   //Split Negative Ferns into Training and Testing sets (they are already shuffled)  
  157.   //将负样本放进 训练和测试集   
  158.   int half = (int)nX.size()*0.5f;  
  159.   //vector::assign函数将区间[start, end)中的值赋值给当前的vector.  
  160.   //将一半的负样本集 作为 测试集   
  161.   nXT.assign(nX.begin()+half, nX.end());  //nXT; //negative data to Test  
  162.   //然后将剩下的一半作为训练集   
  163.   nX.resize(half);  
  164.     
  165.   ///Split Negative NN Examples into Training and Testing sets  
  166.   half = (int)nEx.size()*0.5f;  
  167.   nExT.assign(nEx.begin()+half,nEx.end());  
  168.   nEx.resize(half);  
  169.     
  170.   //Merge Negative Data with Positive Data and shuffle it  
  171.   //将负样本和正样本合并,然后打乱   
  172.   vector<pair<vector<int>,int> > ferns_data(nX.size()+pX.size());  
  173.   vector<int> idx = index_shuffle(0, ferns_data.size());  
  174.   int a=0;  
  175.   for (int i=0;i<pX.size();i++){  
  176.       ferns_data[idx[a]] = pX[i];  
  177.       a++;  
  178.   }  
  179.   for (int i=0;i<nX.size();i++){  
  180.       ferns_data[idx[a]] = nX[i];  
  181.       a++;  
  182.   }  
  183.     
  184.   //Data already have been shuffled, just putting it in the same vector  
  185.   vector<cv::Mat> nn_data(nEx.size()+1);  
  186.   nn_data[0] = pEx;  
  187.   for (int i=0;i<nEx.size();i++){  
  188.       nn_data[i+1]= nEx[i];  
  189.   }  
  190.     
  191.   ///Training     
  192.   //训练 集合分类器(森林) 和 最近邻分类器    
  193.   classifier.trainF(ferns_data, 2); //bootstrap = 2  
  194.   classifier.trainNN(nn_data);  
  195.     
  196.   ///Threshold Evaluation on testing sets   
  197.   //用样本在上面得到的 集合分类器(森林) 和 最近邻分类器 中分类,评价得到最好的阈值  
  198.   classifier.evaluateTh(nXT, nExT);  
  199. }  
  200.   
  201. /* Generate Positive data 
  202.  * Inputs: 
  203.  * - good_boxes (bbP) 
  204.  * - best_box (bbP0) 
  205.  * - frame (im0) 
  206.  * Outputs: 
  207.  * - Positive fern features (pX) 
  208.  * - Positive NN examples (pEx) 
  209.  */  
  210. void TLD::generatePositiveData(const Mat& frame, int num_warps){  
  211.     /* 
  212.     CvScalar定义可存放1—4个数值的数值,常用来存储像素,其结构体如下: 
  213.     typedef struct CvScalar 
  214.     { 
  215.         double val[4]; 
  216.     }CvScalar; 
  217.     如果使用的图像是1通道的,则s.val[0]中存储数据 
  218.     如果使用的图像是3通道的,则s.val[0],s.val[1],s.val[2]中存储数据 
  219.     */  
  220.   Scalar mean;   //均值   
  221.   Scalar stdev;   //标准差   
  222.     
  223.   //此函数将frame图像best_box区域的图像片归一化为均值为0的15*15大小的patch,存在pEx正样本中  
  224.   getPattern(frame(best_box), pEx, mean, stdev);  
  225.     
  226.   //Get Fern features on warped patches   
  227.   Mat img;  
  228.   Mat warped;  
  229.   //void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0,   
  230.   //                                    int borderType=BORDER_DEFAULT ) ;  
  231.   //功能:对输入的图像src进行高斯滤波后用dst输出。  
  232.   //src和dst当然分别是输入图像和输出图像。Ksize为高斯滤波器模板大小,sigmaX和sigmaY分别为高斯滤  
  233.   //波在横向和竖向的滤波系数。borderType为边缘扩展点插值类型。  
  234.   //用9*9高斯核模糊输入帧,存入img  去噪??   
  235.   GaussianBlur(frame, img, Size(9,9), 1.5);  
  236.     
  237.   //在img图像中截取bbhull信息(bbhull是包含了位置和大小的矩形框)的图像赋给warped  
  238.   //例如需要提取图像A的某个ROI(感兴趣区域,由矩形框)的话,用Mat类的B=img(ROI)即可提取  
  239.   warped = img(bbhull);  
  240.   RNG& rng = theRNG();  //生成一个随机数   
  241.   Point2f pt(bbhull.x + (bbhull.width-1)*0.5f, bbhull.y+(bbhull.height-1)*0.5f);  //取矩形框中心的坐标  int i(2)  
  242.     
  243.   //nstructs树木(由一个特征组构建,每组特征代表图像块的不同视图表示)的个数  
  244.   //fern[nstructs] nstructs棵树的森林的数组??   
  245.   vector<int> fern(classifier.getNumStructs());  
  246.   pX.clear();  
  247.   Mat patch;  
  248.   
  249.   //pX为处理后的RectBox最大边界处理后的像素信息,pEx最近邻的RectBox的Pattern,bbP0为最近邻的RectBox。  
  250.   if (pX.capacity() < num_warps * good_boxes.size())  
  251.     pX.reserve(num_warps * good_boxes.size());  //pX正样本个数为 仿射变换个数 * good_box的个数,故需分配至少这么大的空间  
  252.   int idx;  
  253.   for (int i=0; i< num_warps; i++){  
  254.      if (i>0)  
  255.      //PatchGenerator类用来对图像区域进行仿射变换,先RNG一个随机因子,再调用()运算符产生一个变换后的正样本。  
  256.        generator(frame, pt, warped, bbhull.size(), rng);  
  257.        for (int b=0; b < good_boxes.size(); b++){  
  258.          idx = good_boxes[b];  //good_boxes容器保存的是 grid 的索引  
  259.          patch = img(grid[idx]);  //把img的 grid[idx] 区域(也就是bounding box重叠度高的)这一块图像片提取出来  
  260.          //getFeatures函数得到输入的patch的用于树的节点,也就是特征组的特征fern(13位的二进制代码)  
  261.          classifier.getFeatures(patch, grid[idx].sidx, fern);  //grid[idx].sidx 对应的尺度索引  
  262.          pX.push_back(make_pair(fern, 1));   //positive ferns <features, labels=1>  正样本  
  263.      }  
  264.   }  
  265.   printf("Positive examples generated: ferns:%d NN:1\n",(int)pX.size());  
  266. }  
  267.   
  268. //先对最接近box的RectBox区域得到其patch ,然后将像素信息转换为Pattern,  
  269. //具体的说就是归一化RectBox对应的patch的size(放缩至patch_size = 15*15),将2维的矩阵变成一维的向量信息,  
  270. //然后将向量信息均值设为0,调整为zero mean and unit variance(ZMUV)  
  271. //Output: resized Zero-Mean patch  
  272. void TLD::getPattern(const Mat& img, Mat& pattern, Scalar& mean, Scalar& stdev){  
  273.   //将img放缩至patch_size = 15*15,存到pattern中  
  274.   resize(img, pattern, Size(patch_size, patch_size));  
  275.     
  276.   //计算pattern这个矩阵的均值和标准差   
  277.   //Computes a mean value and a standard deviation of matrix elements.  
  278.   meanStdDev(pattern, mean, stdev);  
  279.   pattern.convertTo(pattern, CV_32F);  
  280.     
  281.   //opencv中Mat的运算符有重载, Mat可以 + Mat; + Scalar; + int / float / double 都可以  
  282.   //将矩阵所有元素减去其均值,也就是把patch的均值设为零   
  283.   pattern = pattern - mean.val[0];  
  284. }  
  285.   
  286. /* Inputs: 
  287.  * - Image 
  288.  * - bad_boxes (Boxes far from the bounding box) 
  289.  * - variance (pEx variance) 
  290.  * Outputs 
  291.  * - Negative fern features (nX) 
  292.  * - Negative NN examples (nEx) 
  293.  */  
  294. void TLD::generateNegativeData(const Mat& frame){  
  295.   //由于之前重叠度小于0.2的,都归入 bad_boxes了,所以数量挺多,下面的函数用于打乱顺序,也就是为了  
  296.   //后面随机选择bad_boxes   
  297.   random_shuffle(bad_boxes.begin(), bad_boxes.end());//Random shuffle bad_boxes indexes  
  298.   int idx;  
  299.   //Get Fern Features of the boxes with big variance (calculated using integral images)  
  300.   int a=0;  
  301.   //int num = std::min((int)bad_boxes.size(),(int)bad_patches*100); //limits the size of bad_boxes to try  
  302.   printf("negative data generation started.\n");  
  303.   vector<int> fern(classifier.getNumStructs());  
  304.   nX.reserve(bad_boxes.size());  
  305.   Mat patch;  
  306.   for (int j=0;j<bad_boxes.size();j++){  //把方差较大的bad_boxes加入负样本  
  307.       idx = bad_boxes[j];  
  308.           if (getVar(grid[idx],iisum,iisqsum)<var*0.5f)  
  309.             continue;  
  310.       patch =  frame(grid[idx]);  
  311.       classifier.getFeatures(patch, grid[idx].sidx, fern);  
  312.       nX.push_back(make_pair(fern, 0)); //得到负样本  
  313.       a++;  
  314.   }  
  315.   printf("Negative examples generated: ferns: %d ", a);  
  316.     
  317.   //random_shuffle(bad_boxes.begin(),bad_boxes.begin()+bad_patches);//Randomly selects 'bad_patches' and get the patterns for NN;  
  318.   Scalar dum1, dum2;  
  319.   //bad_patches = (int)file["num_patches"]; 在参数文件中 num_patches = 100  
  320.   nEx=vector<Mat>(bad_patches);  
  321.   for (int i=0;i<bad_patches;i++){  
  322.       idx=bad_boxes[i];  
  323.       patch = frame(grid[idx]);  
  324.       //具体的说就是归一化RectBox对应的patch的size(放缩至patch_size = 15*15)  
  325.       //由于负样本不需要均值和方差,所以就定义dum,将其舍弃  
  326.       getPattern(patch,nEx[i],dum1,dum2);  
  327.   }  
  328.   printf("NN: %d\n",(int)nEx.size());  
  329. }  
  330.   
  331. //该函数通过积分图像计算输入的box的方差   
  332. double TLD::getVar(const BoundingBox& box, const Mat& sum, const Mat& sqsum){  
  333.   double brs = sum.at<int>(box.y+box.height, box.x+box.width);  
  334.   double bls = sum.at<int>(box.y+box.height, box.x);  
  335.   double trs = sum.at<int>(box.y,box.x + box.width);  
  336.   double tls = sum.at<int>(box.y,box.x);  
  337.   double brsq = sqsum.at<double>(box.y+box.height,box.x+box.width);  
  338.   double blsq = sqsum.at<double>(box.y+box.height,box.x);  
  339.   double trsq = sqsum.at<double>(box.y,box.x+box.width);  
  340.   double tlsq = sqsum.at<double>(box.y,box.x);  
  341.     
  342.   double mean = (brs+tls-trs-bls)/((double)box.area());  
  343.   double sqmean = (brsq+tlsq-trsq-blsq)/((double)box.area());  
  344.   //方差=E(X^2)-(EX)^2   EX表示均值   
  345.   return sqmean-mean*mean;  
  346. }  
  347.   
  348. 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){  
  349.   vector<BoundingBox> cbb;  
  350.   vector<float> cconf;  
  351.   int confident_detections=0;  
  352.   int didx; //detection index  
  353.     
  354.   ///Track  跟踪模块   
  355.   if(lastboxfound && tl){   //tl: train and learn  
  356.       //跟踪   
  357.       track(img1, img2, points1, points2);  
  358.   }  
  359.   else{  
  360.       tracked = false;  
  361.   }  
  362.     
  363.   ///Detect   检测模块   
  364.   detect(img2);  
  365.     
  366.   ///Integration   综合模块   
  367.   //TLD只跟踪单目标,所以综合模块综合跟踪器跟踪到的单个目标和检测器检测到的多个目标,然后只输出保守相似度最大的一个目标  
  368.   if (tracked){  
  369.       bbnext=tbb;  
  370.       lastconf=tconf;   //表示相关相似度的阈值   
  371.       lastvalid=tvalid;  //表示保守相似度的阈值  
  372.       printf("Tracked\n");  
  373.       if(detected){                                               //   if Detected  
  374.           //通过 重叠度 对检测器检测到的目标bounding box进行聚类,每个类其重叠度小于0.5  
  375.           clusterConf(dbb, dconf, cbb, cconf);                       //   cluster detections  
  376.           printf("Found %d clusters\n",(int)cbb.size());  
  377.           for (int i=0;i<cbb.size();i++){  
  378.               //找到与跟踪器跟踪到的box距离比较远的类(检测器检测到的box),而且它的相关相似度比跟踪器的要大  
  379.               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  
  380.                   confident_detections++;  //记录满足上述条件,也就是可信度比较高的目标box的个数  
  381.                   didx=i; //detection index  
  382.               }  
  383.           }  
  384.           //如果只有一个满足上述条件的box,那么就用这个目标box来重新初始化跟踪器(也就是用检测器的结果去纠正跟踪器)  
  385.           if (confident_detections==1){                                //if there is ONE such a cluster, re-initialize the tracker  
  386.               printf("Found a better match..reinitializing tracking\n");  
  387.               bbnext=cbb[didx];  
  388.               lastconf=cconf[didx];  
  389.               lastvalid=false;  
  390.           }  
  391.           else {  
  392.               printf("%d confident cluster was found\n", confident_detections);  
  393.               int cx=0,cy=0,cw=0,ch=0;  
  394.               int close_detections=0;  
  395.               for (int i=0;i<dbb.size();i++){  
  396.                   //找到检测器检测到的box与跟踪器预测到的box距离很近(重叠度大于0.7)的box,对其坐标和大小进行累加  
  397.                   if(bbOverlap(tbb,dbb[i])>0.7){                     // Get mean of close detections  
  398.                       cx += dbb[i].x;  
  399.                       cy +=dbb[i].y;  
  400.                       cw += dbb[i].width;  
  401.                       ch += dbb[i].height;  
  402.                       close_detections++;   //记录最近邻box的个数  
  403.                       printf("weighted detection: %d %d %d %d\n",dbb[i].x,dbb[i].y,dbb[i].width,dbb[i].height);  
  404.                   }  
  405.               }  
  406.               if (close_detections>0){  
  407.                   //对与跟踪器预测到的box距离很近的box 和 跟踪器本身预测到的box 进行坐标与大小的平均作为最终的  
  408.                   //目标bounding box,但是跟踪器的权值较大  
  409.                   bbnext.x = cvRound((float)(10*tbb.x+cx)/(float)(10+close_detections));   // weighted average trackers trajectory with the close detections  
  410.                   bbnext.y = cvRound((float)(10*tbb.y+cy)/(float)(10+close_detections));  
  411.                   bbnext.width = cvRound((float)(10*tbb.width+cw)/(float)(10+close_detections));  
  412.                   bbnext.height =  cvRound((float)(10*tbb.height+ch)/(float)(10+close_detections));  
  413.                   printf("Tracker bb: %d %d %d %d\n",tbb.x,tbb.y,tbb.width,tbb.height);  
  414.                   printf("Average bb: %d %d %d %d\n",bbnext.x,bbnext.y,bbnext.width,bbnext.height);  
  415.                   printf("Weighting %d close detection(s) with tracker..\n",close_detections);  
  416.               }  
  417.               else{  
  418.                 printf("%d close detections were found\n",close_detections);  
  419.   
  420.               }  
  421.           }  
  422.       }  
  423.   }  
  424.   else{                                       //   If NOT tracking  
  425.       printf("Not tracking..\n");  
  426.       lastboxfound = false;  
  427.       lastvalid = false;  
  428.       //如果跟踪器没有跟踪到目标,但是检测器检测到了一些可能的目标box,那么同样对其进行聚类,但只是简单的  
  429.       //将聚类的cbb[0]作为新的跟踪目标box(不比较相似度了??还是里面已经排好序了??),重新初始化跟踪器  
  430.       if(detected){                           //  and detector is defined  
  431.           clusterConf(dbb,dconf,cbb,cconf);   //  cluster detections  
  432.           printf("Found %d clusters\n",(int)cbb.size());  
  433.           if (cconf.size()==1){  
  434.               bbnext=cbb[0];  
  435.               lastconf=cconf[0];  
  436.               printf("Confident detection..reinitializing tracker\n");  
  437.               lastboxfound = true;  
  438.           }  
  439.       }  
  440.   }  
  441.   lastbox=bbnext;  
  442.   if (lastboxfound)  
  443.     fprintf(bb_file,"%d,%d,%d,%d,%f\n",lastbox.x,lastbox.y,lastbox.br().x,lastbox.br().y,lastconf);  
  444.   else  
  445.     fprintf(bb_file,"NaN,NaN,NaN,NaN,NaN\n");  
  446.       
  447.   ///learn 学习模块   
  448.   if (lastvalid && tl)  
  449.     learn(img2);  
  450. }  
  451.   
  452. /*Inputs: 
  453. * -current frame(img2), last frame(img1), last Bbox(bbox_f[0]). 
  454. *Outputs: 
  455. *- Confidence(tconf), Predicted bounding box(tbb), Validity(tvalid), points2 (for display purposes only) 
  456. */  
  457. void TLD::track(const Mat& img1, const Mat& img2, vector<Point2f>& points1, vector<Point2f>& points2){  
  458.     
  459.   //Generate points   
  460.   //网格均匀撒点(均匀采样),在lastbox中共产生最多10*10=100个特征点,存于points1  
  461.   bbPoints(points1, lastbox);  
  462.   if (points1.size()<1){  
  463.       printf("BB= %d %d %d %d, Points not generated\n",lastbox.x,lastbox.y,lastbox.width,lastbox.height);  
  464.       tvalid=false;  
  465.       tracked=false;  
  466.       return;  
  467.   }  
  468.   vector<Point2f> points = points1;  
  469.     
  470.   //Frame-to-frame tracking with forward-backward error cheking  
  471.   //trackf2f函数完成:跟踪、计算FB error和匹配相似度sim,然后筛选出 FB_error[i] <= median(FB_error) 和   
  472.   //sim_error[i] > median(sim_error) 的特征点(跟踪结果不好的特征点),剩下的是不到50%的特征点  
  473.   tracked = tracker.trackf2f(img1, img2, points, points2);  
  474.   if (tracked){  
  475.       //Bounding box prediction  
  476.       //利用剩下的这不到一半的跟踪点输入来预测bounding box在当前帧的位置和大小 tbb  
  477.       bbPredict(points, points2, lastbox, tbb);  
  478.       //跟踪失败检测:如果FB error的中值大于10个像素(经验值),或者预测到的当前box的位置移出图像,则  
  479.       //认为跟踪错误,此时不返回bounding box;Rect::br()返回的是右下角的坐标  
  480.       //getFB()返回的是FB error的中值   
  481.       if (tracker.getFB()>10 || tbb.x>img2.cols ||  tbb.y>img2.rows || tbb.br().x < 1 || tbb.br().y <1){  
  482.           tvalid =false//too unstable prediction or bounding box out of image  
  483.           tracked = false;  
  484.           printf("Too unstable predictions FB error=%f\n", tracker.getFB());  
  485.           return;  
  486.       }  
  487.         
  488.       //Estimate Confidence and Validity   
  489.       //评估跟踪确信度和有效性   
  490.       Mat pattern;  
  491.       Scalar mean, stdev;  
  492.       BoundingBox bb;  
  493.       bb.x = max(tbb.x,0);  
  494.       bb.y = max(tbb.y,0);  
  495.       bb.width = min(min(img2.cols-tbb.x,tbb.width), min(tbb.width, tbb.br().x));  
  496.       bb.height = min(min(img2.rows-tbb.y,tbb.height),min(tbb.height,tbb.br().y));  
  497.       //归一化img2(bb)对应的patch的size(放缩至patch_size = 15*15),存入pattern  
  498.       getPattern(img2(bb),pattern,mean,stdev);  
  499.       vector<int> isin;  
  500.       float dummy;  
  501.       //计算图像片pattern到在线模型M的保守相似度  
  502.       classifier.NNConf(pattern,isin,dummy,tconf); //Conservative Similarity  
  503.       tvalid = lastvalid;  
  504.       //保守相似度大于阈值,则评估跟踪有效   
  505.       if (tconf>classifier.thr_nn_valid){  
  506.           tvalid =true;  
  507.       }  
  508.   }  
  509.   else  
  510.     printf("No points tracked\n");  
  511.   

  1.   
  2. //网格均匀撒点,box共10*10=100个特征点   
  3. void TLD::bbPoints(vector<cv::Point2f>& points, const BoundingBox& bb){  
  4.   int max_pts=10;  
  5.   int margin_h=0; //采样边界  
  6.   int margin_v=0;  
  7.   //网格均匀撒点   
  8.   int stepx = ceil((bb.width-2*margin_h)/max_pts);  //ceil返回大于或者等于指定表达式的最小整数  
  9.   int stepy = ceil((bb.height-2*margin_v)/max_pts);  
  10.   //网格均匀撒点,box共10*10=100个特征点   
  11.   for (int y=bb.y+margin_v; y<bb.y+bb.height-margin_v; y+=stepy){  
  12.       for (int x=bb.x+margin_h;x<bb.x+bb.width-margin_h;x+=stepx){  
  13.           points.push_back(Point2f(x,y));  
  14.       }  
  15.   }  
  16.   
  17. //利用剩下的这不到一半的跟踪点输入来预测bounding box在当前帧的位置和大小  
  18. void TLD::bbPredict(const vector<cv::Point2f>& points1,const vector<cv::Point2f>& points2,  
  19.                     const BoundingBox& bb1,BoundingBox& bb2)    {  
  20.   int npoints = (int)points1.size();  
  21.   vector<float> xoff(npoints);  //位移  
  22.   vector<float> yoff(npoints);  
  23.   printf("tracked points : %d\n", npoints);  
  24.   for (int i=0;i<npoints;i++){   //计算每个特征点在两帧之间的位移  
  25.       xoff[i]=points2[i].x - points1[i].x;  
  26.       yoff[i]=points2[i].y - points1[i].y;  
  27.   }  
  28.   float dx = median(xoff);   //计算位移的中值  
  29.   float dy = median(yoff);  
  30.   float s;  
  31.   //计算bounding box尺度scale的变化:通过计算 当前特征点相互间的距离 与 先前(上一帧)特征点相互间的距离 的  
  32.   //比值,以比值的中值作为尺度的变化因子   
  33.   if (npoints>1){  
  34.       vector<float> d;  
  35.       d.reserve(npoints*(npoints-1)/2);  //等差数列求和:1+2+...+(npoints-1)  
  36.       for (int i=0;i<npoints;i++){  
  37.           for (int j=i+1;j<npoints;j++){  
  38.           //计算 当前特征点相互间的距离 与 先前(上一帧)特征点相互间的距离 的比值(位移用绝对值)  
  39.               d.push_back(norm(points2[i]-points2[j])/norm(points1[i]-points1[j]));  
  40.           }  
  41.       }  
  42.       s = median(d);  
  43.   }  
  44.   else {  
  45.       s = 1.0;  
  46.   }  
  47.   
  48.   float s1 = 0.5*(s-1)*bb1.width;  
  49.   float s2 = 0.5*(s-1)*bb1.height;  
  50.   printf("s= %f s1= %f s2= %f \n", s, s1, s2);  
  51.     
  52.   //得到当前bounding box的位置与大小信息   
  53.   //当前box的x坐标 = 前一帧box的x坐标 + 全部特征点位移的中值(可理解为box移动近似的位移) - 当前box宽的一半  
  54.   bb2.x = round( bb1.x + dx - s1);  
  55.   bb2.y = round( bb1.y + dy -s2);  
  56.   bb2.width = round(bb1.width*s);  
  57.   bb2.height = round(bb1.height*s);  
  58.   printf("predicted bb: %d %d %d %d\n",bb2.x,bb2.y,bb2.br().x,bb2.br().y);  
  59. }  
  60.   
  61. void TLD::detect(const cv::Mat& frame){  
  62.   //cleaning   
  63.   dbb.clear();  
  64.   dconf.clear();  
  65.   dt.bb.clear();  
  66.   //GetTickCount返回从操作系统启动到现在所经过的时间  
  67.   double t = (double)getTickCount();  
  68.   Mat img(frame.rows, frame.cols, CV_8U);  
  69.   integral(frame,iisum,iisqsum);   //计算frame的积分图   
  70.   GaussianBlur(frame,img,Size(9,9),1.5);  //高斯模糊,去噪?  
  71.   int numtrees = classifier.getNumStructs();  
  72.   float fern_th = classifier.getFernTh(); //getFernTh()返回thr_fern; 集合分类器的分类阈值  
  73.   vector <int> ferns(10);  
  74.   float conf;  
  75.   int a=0;  
  76.   Mat patch;  
  77.   //级联分类器模块一:方差检测模块,利用积分图计算每个待检测窗口的方差,方差大于var阈值(目标patch方差的50%)的,  
  78.   //则认为其含有前景目标   
  79.   for (int i=0; i<grid.size(); i++){  //FIXME: BottleNeck 瓶颈  
  80.       if (getVar(grid[i],iisum,iisqsum) >= var){  //计算每一个扫描窗口的方差  
  81.           a++;  
  82.           //级联分类器模块二:集合分类器检测模块  
  83.           patch = img(grid[i]);  
  84.           classifier.getFeatures(patch,grid[i].sidx,ferns); //得到该patch特征(13位的二进制代码)  
  85.           conf = classifier.measure_forest(ferns);  //计算该特征值对应的后验概率累加值  
  86.           tmp.conf[i]=conf;   //Detector data中定义TempStruct tmp;   
  87.           tmp.patt[i]=ferns;  
  88.           //如果集合分类器的后验概率的平均值大于阈值fern_th(由训练得到),就认为含有前景目标  
  89.           if (conf > numtrees*fern_th){    
  90.               dt.bb.push_back(i);  //将通过以上两个检测模块的扫描窗口记录在detect structure中  
  91.           }  
  92.       }  
  93.       else  
  94.         tmp.conf[i]=0.0;  
  95.   }  
  96.   int detections = dt.bb.size();  
  97.   printf("%d Bounding boxes passed the variance filter\n",a);  
  98.   printf("%d Initial detection from Fern Classifier\n", detections);  
  99.     
  100.   //如果通过以上两个检测模块的扫描窗口数大于100个,则只取后验概率大的前100个  
  101.   if (detections>100){   //CComparator(tmp.conf)指定比较方式???  
  102.       nth_element(dt.bb.begin(), dt.bb.begin()+100, dt.bb.end(), CComparator(tmp.conf));  
  103.       dt.bb.resize(100);  
  104.       detections=100;  
  105.   }  
  106. //  for (int i=0;i<detections;i++){  
  107. //        drawBox(img,grid[dt.bb[i]]);   
  108. //    }   
  109. //  imshow("detections",img);   
  110.   if (detections==0){  
  111.         detected=false;  
  112.         return;  
  113.       }  
  114.   printf("Fern detector made %d detections ",detections);  
  115.     
  116.   //两次使用getTickCount(),然后再除以getTickFrequency(),计算出来的是以秒s为单位的时间(opencv 2.0 以前是ms)  
  117.   t=(double)getTickCount()-t;    
  118.   printf("in %gms\n", t*1000/getTickFrequency());  //打印以上代码运行使用的毫秒数  
  119.     
  120.   //  Initialize detection structure  
  121.   dt.patt = vector<vector<int> >(detections,vector<int>(10,0));        //  Corresponding codes of the Ensemble Classifier  
  122.   dt.conf1 = vector<float>(detections);                                //  Relative Similarity (for final nearest neighbour classifier)  
  123.   dt.conf2 =vector<float>(detections);                                 //  Conservative Similarity (for integration with tracker)  
  124.   dt.isin = vector<vector<int> >(detections,vector<int>(3,-1));        //  Detected (isin=1) or rejected (isin=0) by nearest neighbour classifier  
  125.   dt.patch = vector<Mat>(detections,Mat(patch_size,patch_size,CV_32F));//  Corresponding patches  
  126.   int idx;  
  127.   Scalar mean, stdev;  
  128.   float nn_th = classifier.getNNTh();  
  129.   //级联分类器模块三:最近邻分类器检测模块   
  130.   for (int i=0;i<detections;i++){                                         //  for every remaining detection  
  131.       idx=dt.bb[i];                                                       //  Get the detected bounding box index  
  132.       patch = frame(grid[idx]);  
  133.       getPattern(patch,dt.patch[i],mean,stdev);                //  Get pattern within bounding box  
  134.       //计算图像片pattern到在线模型M的相关相似度和保守相似度  
  135.       classifier.NNConf(dt.patch[i],dt.isin[i],dt.conf1[i],dt.conf2[i]);  //  Evaluate nearest neighbour classifier  
  136.       dt.patt[i]=tmp.patt[idx];  
  137.       //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]);  
  138.       //相关相似度大于阈值,则认为含有前景目标   
  139.       if (dt.conf1[i]>nn_th){                                               //  idx = dt.conf1 > tld.model.thr_nn; % get all indexes that made it through the nearest neighbour  
  140.           dbb.push_back(grid[idx]);                                         //  BB    = dt.bb(:,idx); % bounding boxes  
  141.           dconf.push_back(dt.conf2[i]);                                     //  Conf  = dt.conf2(:,idx); % conservative confidences  
  142.       }  
  143.   }  
  144.   //打印检测到的可能存在目标的扫描窗口数(可以通过三个级联检测器的)  
  145.   if (dbb.size()>0){  
  146.       printf("Found %d NN matches\n",(int)dbb.size());  
  147.       detected=true;  
  148.   }  
  149.   else{  
  150.       printf("No NN matches found.\n");  
  151.       detected=false;  
  152.   }  
  153. }  
  154.   
  155. //作者已经用python脚本../datasets/evaluate_vis.py来完成算法评估功能,具体见README  
  156. void TLD::evaluate(){  
  157. }  
  158.   
  159. void TLD::learn(const Mat& img){  
  160.   printf("[Learning] ");  
  161.     
  162.   ///Check consistency   
  163.   //检测一致性   
  164.   BoundingBox bb;  
  165.   bb.x = max(lastbox.x,0);  
  166.   bb.y = max(lastbox.y,0);  
  167.   bb.width = min(min(img.cols-lastbox.x,lastbox.width),min(lastbox.width,lastbox.br().x));  
  168.   bb.height = min(min(img.rows-lastbox.y,lastbox.height),min(lastbox.height,lastbox.br().y));  
  169.   Scalar mean, stdev;  
  170.   Mat pattern;  
  171.   //归一化img(bb)对应的patch的size(放缩至patch_size = 15*15),存入pattern  
  172.   getPattern(img(bb), pattern, mean, stdev);  
  173.   vector<int> isin;  
  174.   float dummy, conf;  
  175.   //计算输入图像片(跟踪器的目标box)与在线模型之间的相关相似度conf   
  176.   classifier.NNConf(pattern,isin,conf,dummy);  
  177.   if (conf<0.5) {   //如果相似度太小了,就不训练  
  178.       printf("Fast change..not training\n");  
  179.       lastvalid =false;  
  180.       return;  
  181.   }  
  182.   if (pow(stdev.val[0], 2)< var){  //如果方差太小了,也不训练  
  183.       printf("Low variance..not training\n");  
  184.       lastvalid=false;  
  185.       return;  
  186.   }  
  187.   if(isin[2]==1){   //如果被被识别为负样本,也不训练  
  188.       printf("Patch in negative data..not traing");  
  189.       lastvalid=false;  
  190.       return;  
  191.   }  
  192.     
  193.   /// Data generation  样本产生   
  194.   for (int i=0;i<grid.size();i++){   //计算所有的扫描窗口与目标box的重叠度  
  195.       grid[i].overlap = bbOverlap(lastbox, grid[i]);  
  196.   }  
  197.   //集合分类器   
  198.   vector<pair<vector<int>,int> > fern_examples;  
  199.   good_boxes.clear();    
  200.   bad_boxes.clear();  
  201.   //此函数根据传入的lastbox,在整帧图像中的全部窗口中寻找与该lastbox距离最小(即最相似,  
  202.   //重叠度最大)的num_closest_update个窗口,然后把这些窗口 归入good_boxes容器(只是把网格数组的索引存入)  
  203.   //同时,把重叠度小于0.2的,归入 bad_boxes 容器   
  204.   getOverlappingBoxes(lastbox, num_closest_update);  
  205.   if (good_boxes.size()>0)  
  206.     generatePositiveData(img, num_warps_update);  //用仿射模型产生正样本(类似于第一帧的方法,但只产生10*10=100个)  
  207.   else{  
  208.     lastvalid = false;  
  209.     printf("No good boxes..Not training");  
  210.     return;  
  211.   }  
  212.   fern_examples.reserve(pX.size() + bad_boxes.size());  
  213.   fern_examples.assign(pX.begin(), pX.end());  
  214.   int idx;  
  215.   for (int i=0;i<bad_boxes.size();i++){  
  216.       idx=bad_boxes[i];  
  217.       if (tmp.conf[idx]>=1){   //加入负样本,相似度大于1??相似度不是出于0和1之间吗?  
  218.           fern_examples.push_back(make_pair(tmp.patt[idx],0));  
  219.       }  
  220.   }  
  221.   //最近邻分类器   
  222.   vector<Mat> nn_examples;  
  223.   nn_examples.reserve(dt.bb.size()+1);  
  224.   nn_examples.push_back(pEx);  
  225.   for (int i=0;i<dt.bb.size();i++){  
  226.       idx = dt.bb[i];  
  227.       if (bbOverlap(lastbox,grid[idx]) < bad_overlap)  
  228.         nn_examples.push_back(dt.patch[i]);  
  229.   }  
  230.     
  231.   /// Classifiers update  分类器训练   
  232.   classifier.trainF(fern_examples,2);  
  233.   classifier.trainNN(nn_examples);  
  234.   classifier.show(); //把正样本库(在线模型)包含的所有正样本显示在窗口上  
  235. }  
  236.  
  237.   
  238. //检测器采用扫描窗口的策略   
  239. //此函数根据传入的box(目标边界框)在传入的图像中构建全部的扫描窗口,并计算每个窗口与box的重叠度  
  240. void TLD::buildGrid(const cv::Mat& img, const cv::Rect& box){  
  241.   const float SHIFT = 0.1;  //扫描窗口步长为 宽高的 10%  
  242.   //尺度缩放系数为1.2 (0.16151*1.2=0.19381),共21种尺度变换  
  243.   const float SCALES[] = {0.16151,0.19381,0.23257,0.27908,0.33490,0.40188,0.48225,  
  244.                           0.57870,0.69444,0.83333,1,1.20000,1.44000,1.72800,  
  245.                           2.07360,2.48832,2.98598,3.58318,4.29982,5.15978,6.19174};  
  246.   int width, height, min_bb_side;  
  247.   //Rect bbox;   
  248.   BoundingBox bbox;  
  249.   Size scale;  
  250.   int sc=0;  
  251.     
  252.   for (int s=0; s < 21; s++){  
  253.     width = round(box.width*SCALES[s]);  
  254.     height = round(box.height*SCALES[s]);  
  255.     min_bb_side = min(height,width);  //bounding box最短的边  
  256.     //由于图像片(min_win 为15x15像素)是在bounding box中采样得到的,所以box必须比min_win要大  
  257.     //另外,输入的图像肯定得比 bounding box 要大了  
  258.     if (min_bb_side < min_win || width > img.cols || height > img.rows)  
  259.       continue;  
  260.     scale.width = width;  
  261.     scale.height = height;  
  262.     //push_back在vector类中作用为在vector尾部加入一个数据  
  263.     //scales在类TLD中定义:std::vector<cv::Size> scales;  
  264.     scales.push_back(scale);  //把该尺度的窗口存入scales容器,避免在扫描时计算,加快检测速度  
  265.     for (int y=1; y<img.rows-height; y+=round(SHIFT*min_bb_side)){  //按步长移动窗口  
  266.       for (int x=1; x<img.cols-width; x+=round(SHIFT*min_bb_side)){  
  267.         bbox.x = x;  
  268.         bbox.y = y;  
  269.         bbox.width = width;  
  270.         bbox.height = height;  
  271.         //判断传入的bounding box(目标边界框)与 传入图像中的此时窗口的 重叠度,  
  272.         //以此来确定该图像窗口是否含有目标   
  273.         bbox.overlap = bbOverlap(bbox, BoundingBox(box));  
  274.         bbox.sidx = sc;  //属于第几个尺度   
  275.         //grid在类TLD中定义:std::vector<BoundingBox> grid;  
  276.         //把本位置和本尺度的扫描窗口存入grid容器   
  277.         grid.push_back(bbox);  
  278.       }  
  279.     }  
  280.     sc++;  
  281.   }  
  282. }  
  283.   
  284. //此函数计算两个bounding box 的重叠度   
  285. //重叠度定义为 两个box的交集 与 它们的并集 的比   
  286. float TLD::bbOverlap(const BoundingBox& box1, const BoundingBox& box2){  
  287.   //先判断坐标,假如它们都没有重叠的地方,就直接返回0   
  288.   if (box1.x > box2.x + box2.width) { return 0.0; }  
  289.   if (box1.y > box2.y + box2.height) { return 0.0; }  
  290.   if (box1.x + box1.width < box2.x) { return 0.0; }  
  291.   if (box1.y + box1.height < box2.y) { return 0.0; }  
  292.   
  293.   float colInt =  min(box1.x + box1.width, box2.x + box2.width) - max(box1.x, box2.x);  
  294.   float rowInt =  min(box1.y + box1.height, box2.y + box2.height) - max(box1.y, box2.y);  
  295.   
  296.   float intersection = colInt * rowInt;  
  297.   float area1 = box1.width * box1.height;  
  298.   float area2 = box2.width * box2.height;  
  299.   return intersection / (area1 + area2 - intersection);  

    1. //此函数根据传入的box1(目标边界框),在整帧图像中的全部窗口中寻找与该box1距离最小(即最相似,  
    2. //重叠度最大)的num_closest个窗口,然后把这些窗口 归入good_boxes容器(只是把网格数组的索引存入)  
    3. //同时,把重叠度小于0.2的,归入 bad_boxes 容器   
    4. void TLD::getOverlappingBoxes(const cv::Rect& box1,int num_closest){  
    5.   float max_overlap = 0;  
    6.   for (int i=0;i<grid.size();i++){  
    7.       if (grid[i].overlap > max_overlap) {  //找出重叠度最大的box  
    8.           max_overlap = grid[i].overlap;  
    9.           best_box = grid[i];         
    10.       }  
    11.       if (grid[i].overlap > 0.6){   //重叠度大于0.6的,归入 good_boxes  
    12.           good_boxes.push_back(i);  
    13.       }  
    14.       else if (grid[i].overlap < bad_overlap){  //重叠度小于0.2的,归入 bad_boxes  
    15.           bad_boxes.push_back(i);  
    16.       }  
    17.   }  
    18.   //Get the best num_closest (10) boxes and puts them in good_boxes  
    19.   if (good_boxes.size()>num_closest){  
    20.   //STL中的nth_element()方法找出一个数列中排名第n(下面为第num_closest)的那个数。这个函数运行后  
    21.   //在good_boxes[num_closest]前面num_closest个数都比他大,也就是找到最好的num_closest个box了  
    22.     std::nth_element(good_boxes.begin(), good_boxes.begin() + num_closest, good_boxes.end(), OComparator(grid));  
    23.     //重新压缩good_boxes为num_closest大小   
    24.     good_boxes.resize(num_closest);  
    25.   }  
    26.   //获取good_boxes 的 Hull壳,也就是窗口的边框  
    27.   getBBHull();  
    28. }  
    29.   
    30. //此函数获取good_boxes 的 Hull壳,也就是窗口(图像)的边框 bounding box  
    31. void TLD::getBBHull(){  
    32.   int x1=INT_MAX, x2=0;  //INT_MAX 最大的整形数  
    33.   int y1=INT_MAX, y2=0;  
    34.   int idx;  
    35.   for (int i=0;i<good_boxes.size();i++){  
    36.       idx= good_boxes[i];  
    37.       x1=min(grid[idx].x,x1);   //防止出现负数??  
    38.       y1=min(grid[idx].y,y1);  
    39.       x2=max(grid[idx].x + grid[idx].width,x2);  
    40.       y2=max(grid[idx].y + grid[idx].height,y2);  
    41.   }  
    42.   bbhull.x = x1;  
    43.   bbhull.y = y1;  
    44.   bbhull.width = x2-x1;  
    45.   bbhull.height = y2 -y1;  
    46. }   
    47. //如果两个box的重叠度小于0.5,返回false,否则返回true  
    48. bool bbcomp(const BoundingBox& b1,const BoundingBox& b2){  
    49.   TLD t;  
    50.     if (t.bbOverlap(b1,b2)<0.5)  
    51.       return false;  
    52.     else  
    53.       return true;  
    54. }  
    55.   
    56. int TLD::clusterBB(const vector<BoundingBox>& dbb,vector<int>& indexes){  
    57.   //FIXME: Conditional jump or move depends on uninitialised value(s)  
    58.   const int c = dbb.size();  
    59.   //1. Build proximity matrix   
    60.   Mat D(c,c,CV_32F);  
    61.   float d;  
    62.   for (int i=0;i<c;i++){  
    63.       for (int j=i+1;j<c;j++){  
    64.         d = 1-bbOverlap(dbb[i],dbb[j]);  
    65.         D.at<float>(i,j) = d;  
    66.         D.at<float>(j,i) = d;  
    67.       }  
    68.   }  
    69.   //2. Initialize disjoint clustering  
    70.  float L[c-1]; //Level  
    71.  int nodes[c-1][2];  
    72.  int belongs[c];  
    73.  int m=c;  
    74.  for (int i=0;i<c;i++){  
    75.     belongs[i]=i;  
    76.  }  
    77.  for (int it=0;it<c-1;it++){  
    78.  //3. Find nearest neighbor   
    79.      float min_d = 1;  
    80.      int node_a, node_b;  
    81.      for (int i=0;i<D.rows;i++){  
    82.          for (int j=i+1;j<D.cols;j++){  
    83.              if (D.at<float>(i,j)<min_d && belongs[i]!=belongs[j]){  
    84.                  min_d = D.at<float>(i,j);  
    85.                  node_a = i;  
    86.                  node_b = j;  
    87.              }  
    88.          }  
    89.      }  
    90.      if (min_d>0.5){  
    91.          int max_idx =0;  
    92.          bool visited;  
    93.          for (int j=0;j<c;j++){  
    94.              visited = false;  
    95.              for(int i=0;i<2*c-1;i++){  
    96.                  if (belongs[j]==i){  
    97.                      indexes[j]=max_idx;  
    98.                      visited = true;  
    99.                  }  
    100.              }  
    101.              if (visited)  
    102.                max_idx++;  
    103.          }  
    104.          return max_idx;  
    105.      }  
    106.   
    107.  //4. Merge clusters and assign level  
    108.      L[m]=min_d;  
    109.      nodes[it][0] = belongs[node_a];  
    110.      nodes[it][1] = belongs[node_b];  
    111.      for (int k=0;k<c;k++){  
    112.          if (belongs[k]==belongs[node_a] || belongs[k]==belongs[node_b])  
    113.            belongs[k]=m;  
    114.      }  
    115.      m++;  
    116.  }  
    117.  return 1;  
    118.   
    119. }   
    120. //对检测器检测到的目标bounding box进行聚类   
    121. //聚类(Cluster)分析是由若干模式(Pattern)组成的,通常,模式是一个度量(Measurement)的向量,或者是多维空间中的  
    122. //一个点。聚类分析以相似性为基础,在一个聚类中的模式之间比不在同一聚类中的模式之间具有更多的相似性。  
    123. void TLD::clusterConf(const vector<BoundingBox>& dbb,const vector<float>& dconf,vector<BoundingBox>& cbb,vector<float>& cconf){  
    124.   int numbb =dbb.size();  
    125.   vector<int> T;  
    126.   float space_thr = 0.5;  
    127.   int c=1;    //记录 聚类的类个数  
    128.   switch (numbb){  //检测到的含有目标的bounding box个数  
    129.   case 1:  
    130.     cbb=vector<BoundingBox>(1,dbb[0]);  //如果只检测到一个,那么这个就是检测器检测到的目标  
    131.     cconf=vector<float>(1,dconf[0]);  
    132.     return;  
    133.     break;  
    134.   case 2:  
    135.     T =vector<int>(2,0);  
    136.     //此函数计算两个bounding box 的重叠度  
    137.     if (1 - bbOverlap(dbb[0],dbb[1]) > space_thr){  //如果只检测到两个box,但他们的重叠度小于0.5  
    138.       T[1]=1;  
    139.       c=2;  //重叠度小于0.5的box,属于不同的类   
    140.     }  
    141.     break;  
    142.   default:  //检测到的box数目大于2个,则筛选出重叠度大于0.5的  
    143.     T = vector<int>(numbb, 0);  
    144.     //stable_partition()重新排列元素,使得满足指定条件的元素排在不满足条件的元素前面。它维持着两组元素的顺序关系。  
    145.     //STL partition就是把一个区间中的元素按照某个条件分成两类。返回第二类子集的起点  
    146.     //bbcomp()函数判断两个box的重叠度小于0.5,返回false,否则返回true (分界点是重叠度:0.5)  
    147.     //partition() 将dbb划分为两个子集,将满足两个box的重叠度小于0.5的元素移动到序列的前面,为一个子集,重叠度大于0.5的,  
    148.     //放在序列后面,为第二个子集,但两个子集的大小不知道,返回第二类子集的起点  
    149.     c = partition(dbb, T, (*bbcomp));   //重叠度小于0.5的box,属于不同的类,所以c是不同的类别个数  
    150.     //c = clusterBB(dbb,T);   
    151.     break;  
    152.   }  
    153.   cconf=vector<float>(c);   
    154.   cbb=vector<BoundingBox>(c);  
    155.   printf("Cluster indexes: ");  
    156.   BoundingBox bx;  
    157.   for (int i=0;i<c;i++){   //类别个数  
    158.       float cnf=0;  
    159.       int N=0,mx=0,my=0,mw=0,mh=0;  
    160.       for (int j=0;j<T.size();j++){  //检测到的bounding box个数  
    161.           if (T[j]==i){   //将聚类为同一个类别的box的坐标和大小进行累加  
    162.               printf("%d ",i);  
    163.               cnf=cnf+dconf[j];  
    164.               mx=mx+dbb[j].x;  
    165.               my=my+dbb[j].y;  
    166.               mw=mw+dbb[j].width;  
    167.               mh=mh+dbb[j].height;  
    168.               N++;  
    169.           }  
    170.       }  
    171.       if (N>0){   //然后求该类的box的坐标和大小的平均值,将平均值作为该类的box的代表  
    172.           cconf[i]=cnf/N;  
    173.           bx.x=cvRound(mx/N);  
    174.           bx.y=cvRound(my/N);  
    175.           bx.width=cvRound(mw/N);  
    176.           bx.height=cvRound(mh/N);  
    177.           cbb[i]=bx;  //返回的是聚类,每一个类都有一个代表的bounding box  
    178.       }  
    179.   }  
    180.   printf("\n");  
    181. }




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值