TLD(Tracking-Learning-Detection)学习与源码理解之(七)

本文转自http://blog.csdn.net/zouxy09/article/details/7893090

下面是自己在看论文和这些大牛的分析过程中,对代码进行了一些理解,但是由于自己接触图像处理和机器视觉没多久,另外由于自己编程能力比较弱,所以分析过程可能会有不少的错误,希望各位不吝指正。而且,因为编程很多地方不懂,所以注释得非常乱,还海涵。

 

FerNNClassifier.h

  1. /* 
  2.  * FerNNClassifier.h 
  3.  * 
  4.  *  Created on: Jun 14, 2011 
  5.  *      Author: alantrrs 
  6.  */  
  7.   
  8. #include <opencv2/opencv.hpp>   
  9. #include <stdio.h>   
  10. class FerNNClassifier{  
  11. private:  
  12.   //下面这些参数通过程序开始运行时读入parameters.yml文件进行初始化  
  13.   float thr_fern;  
  14.   int structSize;  
  15.   int nstructs;  
  16.   float valid;  
  17.   float ncc_thesame;  
  18.   float thr_nn;  
  19.   int acum;  
  20. public:  
  21.   //Parameters   
  22.   float thr_nn_valid;  
  23.   
  24.   void read(const cv::FileNode& file);  
  25.   void prepare(const std::vector<cv::Size>& scales);  
  26.   void getFeatures(const cv::Mat& image,const int& scale_idx,std::vector<int>& fern);  
  27.   void update(const std::vector<int>& fern, int C, int N);  
  28.   float measure_forest(std::vector<int> fern);  
  29.   void trainF(const std::vector<std::pair<std::vector<int>,int> >& ferns,int resample);  
  30.   void trainNN(const std::vector<cv::Mat>& nn_examples);  
  31.   void NNConf(const cv::Mat& example,std::vector<int>& isin,float& rsconf,float& csconf);  
  32.   void evaluateTh(const std::vector<std::pair<std::vector<int>,int> >& nXT,const std::vector<cv::Mat>& nExT);  
  33.   void show();  
  34.   //Ferns Members   
  35.   int getNumStructs(){return nstructs;}  
  36.   float getFernTh(){return thr_fern;}  
  37.   float getNNTh(){return thr_nn;}  
  38.     
  39.   struct Feature   //特征结构体  
  40.       {  
  41.           uchar x1, y1, x2, y2;  
  42.           Feature() : x1(0), y1(0), x2(0), y2(0) {}  
  43.           Feature(int _x1, int _y1, int _x2, int _y2)  
  44.           : x1((uchar)_x1), y1((uchar)_y1), x2((uchar)_x2), y2((uchar)_y2)  
  45.           {}  
  46.           bool operator ()(const cv::Mat& patch) const  
  47.           {   
  48.             //二维单通道元素可以用Mat::at(i, j)访问,i是行序号,j是列序号  
  49.             //返回的patch图像片在(y1,x1)和(y2, x2)点的像素比较值,返回0或者1  
  50.             return patch.at<uchar>(y1,x1) > patch.at<uchar>(y2, x2);   
  51.           }  
  52.       };  
  53.   //Ferns(蕨类植物:有根、茎、叶之分,不具花)features 特征组?  
  54.   std::vector<std::vector<Feature> > features; //Ferns features (one std::vector for each scale)  
  55.   std::vector< std::vector<int> > nCounter; //negative counter  
  56.   std::vector< std::vector<int> > pCounter; //positive counter  
  57.   std::vector< std::vector<float> > posteriors; //Ferns posteriors  
  58.   float thrN; //Negative threshold  
  59.   float thrP;  //Positive thershold  
  60.     
  61.   //NN Members   
  62.   std::vector<cv::Mat> pEx; //NN positive examples  
  63.   std::vector<cv::Mat> nEx; //NN negative examples  
  64. };  
/*
 * FerNNClassifier.h
 *
 *  Created on: Jun 14, 2011
 *      Author: alantrrs
 */

#include <opencv2/opencv.hpp>
#include <stdio.h>
class FerNNClassifier{
private:
  //下面这些参数通过程序开始运行时读入parameters.yml文件进行初始化
  float thr_fern;
  int structSize;
  int nstructs;
  float valid;
  float ncc_thesame;
  float thr_nn;
  int acum;
public:
  //Parameters
  float thr_nn_valid;

  void read(const cv::FileNode& file);
  void prepare(const std::vector<cv::Size>& scales);
  void getFeatures(const cv::Mat& image,const int& scale_idx,std::vector<int>& fern);
  void update(const std::vector<int>& fern, int C, int N);
  float measure_forest(std::vector<int> fern);
  void trainF(const std::vector<std::pair<std::vector<int>,int> >& ferns,int resample);
  void trainNN(const std::vector<cv::Mat>& nn_examples);
  void NNConf(const cv::Mat& example,std::vector<int>& isin,float& rsconf,float& csconf);
  void evaluateTh(const std::vector<std::pair<std::vector<int>,int> >& nXT,const std::vector<cv::Mat>& nExT);
  void show();
  //Ferns Members
  int getNumStructs(){return nstructs;}
  float getFernTh(){return thr_fern;}
  float getNNTh(){return thr_nn;}
  
  struct Feature   //特征结构体
      {
          uchar x1, y1, x2, y2;
          Feature() : x1(0), y1(0), x2(0), y2(0) {}
          Feature(int _x1, int _y1, int _x2, int _y2)
          : x1((uchar)_x1), y1((uchar)_y1), x2((uchar)_x2), y2((uchar)_y2)
          {}
          bool operator ()(const cv::Mat& patch) const
          { 
		    //二维单通道元素可以用Mat::at(i, j)访问,i是行序号,j是列序号
			//返回的patch图像片在(y1,x1)和(y2, x2)点的像素比较值,返回0或者1
			return patch.at<uchar>(y1,x1) > patch.at<uchar>(y2, x2); 
		  }
      };
  //Ferns(蕨类植物:有根、茎、叶之分,不具花)features 特征组?
  std::vector<std::vector<Feature> > features; //Ferns features (one std::vector for each scale)
  std::vector< std::vector<int> > nCounter; //negative counter
  std::vector< std::vector<int> > pCounter; //positive counter
  std::vector< std::vector<float> > posteriors; //Ferns posteriors
  float thrN; //Negative threshold
  float thrP;  //Positive thershold
  
  //NN Members
  std::vector<cv::Mat> pEx; //NN positive examples
  std::vector<cv::Mat> nEx; //NN negative examples
};


 

 

FerNNClassifier.cpp

  1. /* 
  2.  * FerNNClassifier.cpp 
  3.  * 
  4.  *  Created on: Jun 14, 2011 
  5.  *      Author: alantrrs 
  6.  */  
  7.   
  8. #include <FerNNClassifier.h>   
  9.   
  10. using namespace cv;  
  11. using namespace std;  
  12.   
  13. void FerNNClassifier::read(const FileNode& file){  
  14.   ///Classifier Parameters   
  15.   //下面这些参数通过程序开始运行时读入parameters.yml文件进行初始化  
  16.   valid = (float)file["valid"];  
  17.   ncc_thesame = (float)file["ncc_thesame"];  
  18.   nstructs = (int)file["num_trees"];   //树木(由一个特征组构建,每组特征代表图像块的不同视图表示)的个数  
  19.   structSize = (int)file["num_features"];  //每棵树的特征个数,也即每棵树的节点个数;树上每一个特征都作为一个决策节点  
  20.   thr_fern = (float)file["thr_fern"];  
  21.   thr_nn = (float)file["thr_nn"];  
  22.   thr_nn_valid = (float)file["thr_nn_valid"];  
  23. }  
  24.   
  25. void FerNNClassifier::prepare(const vector<Size>& scales){  
  26.   acum = 0;  
  27.   //Initialize test locations for features  
  28.   int totalFeatures = nstructs * structSize;  
  29.   //二维向量  包含全部尺度(scales)的扫描窗口,每个尺度包含totalFeatures个特征  
  30.   features = vector<vector<Feature> >(scales.size(), vector<Feature> (totalFeatures));  
  31.    
  32.   //opencv中自带的一个随机数发生器的类RNG   
  33.   RNG& rng = theRNG();  
  34.     
  35.   float x1f,x2f,y1f,y2f;  
  36.   int x1, x2, y1, y2;  
  37.   //集合分类器基于n个基本分类器,每个分类器都是基于一个pixel comparisons(像素比较集)的;  
  38.   //pixel comparisons的产生方法:先用一个归一化的patch去离散化像素空间,产生所有可能的垂直和水平的pixel comparisons  
  39.   //然后我们把这些pixel comparisons随机分配给n个分类器,每个分类器得到完全不同的pixel comparisons(特征集合),  
  40.   //这样,所有分类器的特征组统一起来就可以覆盖整个patch了   
  41.     
  42.   //用随机数去填充每一个尺度扫描窗口的特征   
  43.   for (int i=0;i<totalFeatures;i++){  
  44.       x1f = (float)rng;  
  45.       y1f = (float)rng;  
  46.       x2f = (float)rng;  
  47.       y2f = (float)rng;  
  48.       for (int s=0; s<scales.size(); s++){  
  49.           x1 = x1f * scales[s].width;  
  50.           y1 = y1f * scales[s].height;  
  51.           x2 = x2f * scales[s].width;  
  52.           y2 = y2f * scales[s].height;  
  53.           //第s种尺度的第i个特征  两个随机分配的像素点坐标  
  54.           features[s][i] = Feature(x1, y1, x2, y2);  
  55.       }  
  56.   }  
  57.   //Thresholds   
  58.   thrN = 0.5 * nstructs;  
  59.   
  60.   //Initialize Posteriors  初始化后验概率   
  61.   //后验概率指每一个分类器对传入的图像片进行像素对比,每一个像素对比得到0或者1,所有的特征13个comparison对比,  
  62.   //连成一个13位的二进制代码x,然后索引到一个记录了后验概率的数组P(y|x),y为0或者1(二分类),也就是出现x的  
  63.   //基础上,该图像片为y的概率是多少对n个基本分类器的后验概率做平均,大于0.5则判定其含有目标  
  64.   for (int i = 0; i<nstructs; i++) {  
  65.   //每一个每类器维护一个后验概率的分布,这个分布有2^d个条目(entries),这里d是像素比较pixel comparisons  
  66.   //的个数,这里是structSize,即13个comparison,所以会产生2^13即8,192个可能的code,每一个code对应一个后验概率  
  67.   //后验概率P(y|x)= #p/(#p+#n) ,#p和#n分别是正和负图像片的数目,也就是下面的pCounter和nCounter  
  68.   //初始化时,每个后验概率都得初始化为0;运行时候以下面方式更新:已知类别标签的样本(训练样本)通过n个分类器  
  69.   //进行分类,如果分类结果错误,那么响应的#p和#n就会更新,这样P(y|x)也相应更新了  
  70.       posteriors.push_back(vector<float>(pow(2.0,structSize), 0));  
  71.       pCounter.push_back(vector<int>(pow(2.0,structSize), 0));  
  72.       nCounter.push_back(vector<int>(pow(2.0,structSize), 0));  
  73.   }  
  74. }  
  75.   
  76. //该函数得到输入的image的用于树的节点,也就是特征组的特征(13位的二进制代码)  
  77. void FerNNClassifier::getFeatures(const cv::Mat& image, const int& scale_idx, vector<int>& fern){  
  78.   int leaf;  //叶子  树的最终节点  
  79.   //每一个每类器维护一个后验概率的分布,这个分布有2^d个条目(entries),这里d是像素比较pixel comparisons  
  80.   //的个数,这里是structSize,即13个comparison,所以会产生2^13即8,192个可能的code,每一个code对应一个后验概率  
  81.   for (int t=0; t<nstructs; t++){  //nstructs 表示树的个数 10  
  82.       leaf=0;  
  83.       for (int f=0; f<structSize; f++){  //表示每棵树特征的个数 13  
  84.         //struct Feature 特征结构体有一个运算符重载 bool operator ()(const cv::Mat& patch) const  
  85.         //返回的patch图像片在(y1,x1)和(y2, x2)点的像素比较值,返回0或者1  
  86.         //然后leaf就记录了这13位的二进制代码,作为特征   
  87.           leaf = (leaf << 1) + features[scale_idx][t*nstructs+f](image);  
  88.       }  
  89.       fern[t] = leaf;   
  90.   }  
  91. }  
  92.   
  93. float FerNNClassifier::measure_forest(vector<int> fern) {  
  94.   float votes = 0;  
  95.   for (int i = 0; i < nstructs; i++) {  
  96.      // 后验概率posteriors[i][idx] = ((float)(pCounter[i][idx]))/(pCounter[i][idx] + nCounter[i][idx]);  
  97.       votes += posteriors[i][fern[i]];   //每棵树的每个特征值对应的后验概率累加值 作投票值??  
  98.   }  
  99.   return votes;  
  100. }  
  101.   
  102. //更新正负样本数,同时更新后验概率   
  103. void FerNNClassifier::update(const vector<int>& fern, int C, int N) {  
  104.   int idx;  
  105.   for (int i = 0; i < nstructs; i++) {  
  106.       idx = fern[i];  
  107.       (C==1) ? pCounter[i][idx] += N : nCounter[i][idx] += N;  
  108.       if (pCounter[i][idx]==0) {  
  109.           posteriors[i][idx] = 0;  
  110.       } else {  
  111.           posteriors[i][idx] = ((float)(pCounter[i][idx]))/(pCounter[i][idx] + nCounter[i][idx]);  
  112.       }  
  113.   }  
  114. }  
  115.   
  116. //训练集合分类器(n个基本分类器集合)   
  117. void FerNNClassifier::trainF(const vector<std::pair<vector<int>,int> >& ferns,int resample){  
  118.   // Conf = function(2,X,Y,Margin,Bootstrap,Idx)  
  119.   //                 0 1 2 3      4         5  
  120.   //  double *X     = mxGetPr(prhs[1]); -> ferns[i].first  
  121.   //  int numX      = mxGetN(prhs[1]);  -> ferns.size()  
  122.   //  double *Y     = mxGetPr(prhs[2]); ->ferns[i].second  
  123.   //  double thrP   = *mxGetPr(prhs[3]) * nTREES; ->threshold*nstructs  
  124.   //  int bootstrap = (int) *mxGetPr(prhs[4]); ->resample  
  125.     
  126.   //thr_fern: 0.6 thrP定义为Positive thershold  
  127.   thrP = thr_fern * nstructs;                                    // int step = numX / 10;  
  128.   //for (int j = 0; j < resample; j++) {                      // for (int j = 0; j < bootstrap; j++) {  
  129.       for (int i = 0; i < ferns.size(); i++){               //   for (int i = 0; i < step; i++) {  
  130.                                                             //     for (int k = 0; k < 10; k++) {  
  131.                                                             //       int I = k*step + i;//box index  
  132.                                                             //       double *x = X+nTREES*I; //tree index  
  133.           if(ferns[i].second==1){    //为1表示正样本        //       if (Y[I] == 1) {  
  134.               //measure_forest函数返回所有树的所有特征值对应的后验概率累加值  
  135.               //该累加值如果小于正样本阈值,也就是是输入的是正样本,却被分类成负样本了  
  136.               //出现分类错误,所以就把该样本添加到正样本库,同时更新后验概率  
  137.               if(measure_forest(ferns[i].first) <= thrP)      //         if (measure_forest(x) <= thrP)  
  138.               更新正样本数,同时更新后验概率   
  139.                 update(ferns[i].first, 1, 1);                 //             update(x,1,1);  
  140.           }else{                                            //        }else{  
  141.               if (measure_forest(ferns[i].first) >= thrN)   //         if (measure_forest(x) >= thrN)  
  142.                 update(ferns[i].first, 0, 1);                 //             update(x,0,1);  
  143.           }  
  144.       }  
  145.   //}   
  146. }  
  147.   
  148. //训练最近邻分类器   
  149. void FerNNClassifier::trainNN(const vector<cv::Mat>& nn_examples){  
  150.   float conf, dummy;  
  151.   vector<int> y(nn_examples.size(),0); //vector<T> v3(n, i); v3包含n个值为i的元素。y数组元素初始化为0  
  152.   y[0]=1;  //上面说到调用trainNN这个函数传入的nn_data样本集,只有一个pEx,在nn_data[0]  
  153.   vector<int> isin;  
  154.   for (int i=0; i<nn_examples.size(); i++){                          //  For each example  
  155.       //计算输入图像片与在线模型之间的相关相似度conf  
  156.       NNConf(nn_examples[i], isin, conf, dummy);                      //  Measure Relative similarity  
  157.       //thr_nn: 0.65 阈值   
  158.       //标签是正样本,如果相关相似度小于0.65 ,则认为其不含有前景目标,也就是分类错误了;这时候就把它加到正样本库  
  159.       if (y[i]==1 && conf <= thr_nn){                                //    if y(i) == 1 && conf1 <= tld.model.thr_nn % 0.65  
  160.           if (isin[1]<0){                                          //      if isnan(isin(2))  
  161.               pEx = vector<Mat>(1,nn_examples[i]);                 //        tld.pex = x(:,i);  
  162.               continue;                                            //        continue;  
  163.           }                                                        //      end  
  164.           //pEx.insert(pEx.begin()+isin[1],nn_examples[i]);        //      tld.pex = [tld.pex(:,1:isin(2)) x(:,i) tld.pex(:,isin(2)+1:end)]; % add to model  
  165.           pEx.push_back(nn_examples[i]);  
  166.       }                                                            //    end  
  167.       if(y[i]==0 && conf>0.5)                                      //  if y(i) == 0 && conf1 > 0.5  
  168.         nEx.push_back(nn_examples[i]);                             //    tld.nex = [tld.nex x(:,i)];  
  169.   
  170.   }                                                                 //  end  
  171.   acum++;  
  172.   printf("%d. Trained NN examples: %d positive %d negative\n",acum,(int)pEx.size(),(int)nEx.size());  
  173. }                                                                  //  end  
  174.   
  175.   /*Inputs: 
  176.    * -NN Patch 
  177.    * Outputs: 
  178.    * -Relative Similarity (rsconf)相关相似度, Conservative Similarity (csconf)保守相似度, 
  179.    * In pos. set|Id pos set|In neg. set (isin) 
  180.    */  
  181. void FerNNClassifier::NNConf(const Mat& example, vector<int>& isin,float& rsconf,float& csconf){  
  182.   isin=vector<int>(3,-1);  //vector<T> v3(n, i); v3包含n个值为i的元素。 三个元素都是-1  
  183.   if (pEx.empty()){ //if isempty(tld.pex) % IF positive examples in the model are not defined THEN everything is negative  
  184.       rsconf = 0; //    conf1 = zeros(1,size(x,2));  
  185.       csconf=0;  
  186.       return;  
  187.   }  
  188.   if (nEx.empty()){ //if isempty(tld.nex) % IF negative examples in the model are not defined THEN everything is positive  
  189.       rsconf = 1;   //    conf1 = ones(1,size(x,2));  
  190.       csconf=1;  
  191.       return;  
  192.   }  
  193.   Mat ncc(1,1,CV_32F);  
  194.   float nccP, csmaxP, maxP=0;  
  195.   bool anyP=false;  
  196.   int maxPidx, validatedPart = ceil(pEx.size()*valid);  //ceil返回大于或者等于指定表达式的最小整数  
  197.   float nccN, maxN=0;  
  198.   bool anyN=false;  
  199.   //比较图像片p到在线模型M的距离(相似度),计算正样本最近邻相似度,也就是将输入的图像片与  
  200.   //在线模型中所有的图像片进行匹配,找出最相似的那个图像片,也就是相似度的最大值  
  201.   for (int i=0;i<pEx.size();i++){  
  202.       matchTemplate(pEx[i], example, ncc, CV_TM_CCORR_NORMED);      // measure NCC to positive examples  
  203.       nccP=(((float*)ncc.data)[0]+1)*0.5;  //计算匹配相似度  
  204.       if (nccP>ncc_thesame)  //ncc_thesame: 0.95  
  205.         anyP=true;  
  206.       if(nccP > maxP){  
  207.           maxP=nccP;    //记录最大的相似度以及对应的图像片index索引值  
  208.           maxPidx = i;  
  209.           if(i<validatedPart)  
  210.             csmaxP=maxP;  
  211.       }  
  212.   }  
  213.   //计算负样本最近邻相似度   
  214.   for (int i=0;i<nEx.size();i++){  
  215.       matchTemplate(nEx[i],example,ncc,CV_TM_CCORR_NORMED);     //measure NCC to negative examples  
  216.       nccN=(((float*)ncc.data)[0]+1)*0.5;  
  217.       if (nccN>ncc_thesame)  
  218.         anyN=true;  
  219.       if(nccN > maxN)  
  220.         maxN=nccN;  
  221.   }  
  222.   //set isin   
  223.   //if he query patch is highly correlated with any positive patch in the model then it is considered to be one of them  
  224.   if (anyP) isin[0]=1;    
  225.   isin[1]=maxPidx;      //get the index of the maximall correlated positive patch  
  226.   //if  the query patch is highly correlated with any negative patch in the model then it is considered to be one of them  
  227.   if (anyN) isin[2]=1;   
  228.     
  229.   //Measure Relative Similarity  
  230.   //相关相似度 = 正样本最近邻相似度 / (正样本最近邻相似度 + 负样本最近邻相似度)  
  231.   float dN=1-maxN;  
  232.   float dP=1-maxP;  
  233.   rsconf = (float)dN/(dN+dP);  
  234.     
  235.   //Measure Conservative Similarity  
  236.   dP = 1 - csmaxP;  
  237.   csconf =(float)dN / (dN + dP);  
  238. }  
  239.   
  240. void FerNNClassifier::evaluateTh(const vector<pair<vector<int>,int> >& nXT, const vector<cv::Mat>& nExT){  
  241.   float fconf;  
  242.   for (int i=0;i<nXT.size();i++){  
  243.   //所有基本分类器的后验概率的平均值如果大于thr_fern,则认为含有前景目标  
  244.   //measure_forest返回的是所有后验概率的累加和,nstructs 为树的个数,也就是基本分类器的数目 ??  
  245.     fconf = (float) measure_forest(nXT[i].first)/nstructs;  
  246.     if (fconf>thr_fern)  //thr_fern: 0.6 thrP定义为Positive thershold  
  247.       thr_fern = fconf;  //取这个平均值作为 该集合分类器的 新的阈值,这就是训练??  
  248.   }  
  249.     
  250.   vector <int> isin;  
  251.   float conf, dummy;  
  252.   for (int i=0; i<nExT.size(); i++){  
  253.       NNConf(nExT[i], isin, conf, dummy);  
  254.       if (conf > thr_nn)  
  255.         thr_nn = conf; //取这个最大相关相似度作为 该最近邻分类器的 新的阈值,这就是训练??  
  256.   }  
  257.     
  258.   if (thr_nn > thr_nn_valid)  //thr_nn_valid: 0.7  
  259.     thr_nn_valid = thr_nn;  
  260. }  
  261.   
  262. //把正样本库(在线模型)包含的所有正样本显示在窗口上   
  263. void FerNNClassifier::show(){  
  264.   Mat examples((int)pEx.size()*pEx[0].rows, pEx[0].cols, CV_8U);  
  265.   double minval;  
  266.   Mat ex(pEx[0].rows, pEx[0].cols, pEx[0].type());  
  267.   for (int i=0;i<pEx.size();i++){  
  268.     //minMaxLoc寻找矩阵(一维数组当作向量,用Mat定义)中最小值和最大值的位置.   
  269.     minMaxLoc(pEx[i], &minval); //寻找pEx[i]的最小值  
  270.     pEx[i].copyTo(ex);  
  271.     ex = ex - minval;  //把像素亮度最小的像素重设为0,其他像素按此重设  
  272.     //Mat Mat::rowRange(int startrow, int endrow) const 为指定的行span创建一个新的矩阵头。  
  273.     //Mat Mat::rowRange(const Range& r) const   //Range 结构包含着起始和终止的索引值。  
  274.     Mat tmp = examples.rowRange(Range(i*pEx[i].rows, (i+1)*pEx[i].rows));  
  275.     ex.convertTo(tmp, CV_8U);  
  276.   }  
  277.   imshow("Examples", examples);  
  278. }  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值