TLD(Tracking-Learning-Detection)算法学习与源码解析(五)之FerNNClassifier.cpp源码解析

本序列文章的目的是总结一下这段时间所学到的,主要分为以下几部分,本章是第五部分。

算法概述

runtld.cpp源码解析

tld.cpp源码解析

LKTracker(重点)

FerNNClassifier.cpp源码解析(重点)

6 tld_utils.cpp源码解析



方差分类器

代码中是通过积分图来计算一个图的灰度值的方差的。


fern分类器

a先讲解一下一个图的fern特征

这个fern分类器分为10个小的分类器(10棵树),

每棵树有13个节点,一个图被这13个节点处理后会变为13位的二进制码(比如0011001100010)。

10棵树处理后,就会产生1013位的二进制码。就是该图的fern特征。


b上面提高一个图会被一棵树的13个节点处理,那么这个特征抽取过程是怎么样的呢?下面讲解。

   A分类器的初始化

一棵树的初始化工作,根据一个patch的大小,随机均匀产生点对的集合,就是13个点对。所谓点对,就是两个点,比如(11)和(15),表示两个像素在图像的位置,这些点以后用来定位像素。

10棵树的初始化就是重复上面的操作了。

   B计算一个patch(指定大小的图)fern特征

先看看一个patch如何被一棵树处理。

一棵树其实就是13个点对的集合,每个点对处理一次,比如这个点对(24)和(56),把这两个点在patch的像素找出来比较大小,返回1或者013个点对就得到131或者0了。这样一个patch就被一棵树处理为一个13位的二进制码了。

一个patchfern特征就是被10棵树处理后的1013位二进制码了。


c训练过程

  A先验概率:每个fern特征在每棵树下都对应有一个先验概率。

比如计算fern特征在第i棵树的先验概率:

idx=fern[i],则

posteriors[i][idx]= ((float)(pCounter[i][idx]))/(pCounter[i][idx]+nCounter[i][idx]);

其中,

pCounter[i][idx]对于第i棵树,含有idx特征的正样本的数量

nCounter[i][idx]对于第i棵树,含有idx特征的负样本的数量

   B训练的过程,就是根据新的正负样本,不断更新pCounternCounterposteriors的值。

如果每一个新进来的样本,要通过之前的先验概率的和对它进行权重的判断,要跟某个阈值比较,符合条件才用新来的样本来更新posteriors


d分类

其实就是对fern特征进行权值衡量,判断大于还是小于某个阈值。



Nnc分类器

所有的patch要处理为15*15的大小。

训练:每进来一个新的样本patch,先将这个patch将之前保存的所有正的负的patch做比较,算出一个相似度的值,如果这个相似度跟某个阈值比较符合条件,就将这个新样本添加到库中。

那么这个相似度是怎么算的呢?

先说说一个patch和另一个patch的相似度怎么算。

代码中用的是库函数,算法是CV_TM_CCORR_NORMED(网上翻译为归一化相关匹配法)。

http://www.cnblogs.com/xrwang/archive/2010/02/05/MatchTemplate.html


我看论文提到的是

Similaritybetween two patches pi , pj is defined as

S(pi, pj ) = 0.5(NCC(pi , pj ) + 1),

whereNCC is a Normalized Correlation Coefficient.

NormalizedCorrelation Coefficient网上翻译为归一化相关系数


这里先不管究竟哪种算法好。

总之,一个pacth跟库中所有正样本比较,算出最大相似度,也跟所有负样本算出最大相似度。

两者结合算出相对相似度和保守相似读。(具体细节看代码)




/*
 * FerNNClassifier.cpp
 *
 *  Created on: Jun 14, 2011
 *      Author: alantrrs
 */

#include <FerNNClassifier.h>

using namespace cv;
using namespace std;

void FerNNClassifier::read(const FileNode& file){
  ///Classifier Parameters
  valid = (float)file["valid"];
  ncc_thesame = (float)file["ncc_thesame"];
  nstructs = (int)file["num_trees"];//树木的数量,为10
  structSize = (int)file["num_features"];//每个树的节点数量,为13
  thr_fern = (float)file["thr_fern"];
  thr_nn = (float)file["thr_nn"];
  thr_nn_valid = (float)file["thr_nn_valid"];
}

/**
 * 随机产生一个patch的颜色(点位置)比较集
 * 先验概率初始化
 */
void FerNNClassifier::prepare(const vector<Size>& scales){
  acum = 0;
  //Initialize test locations for features
  int totalFeatures = nstructs*structSize;
  features = vector<vector<Feature> >(scales.size(),vector<Feature> (totalFeatures));
  RNG& rng = theRNG();
  float x1f,x2f,y1f,y2f;
  int x1, x2, y1, y2;
  for (int i=0;i<totalFeatures;i++){
      x1f = (float)rng;
      y1f = (float)rng;
      x2f = (float)rng;
      y2f = (float)rng;
      for (int s=0;s<scales.size();s++){
          x1 = x1f * scales[s].width;
          y1 = y1f * scales[s].height;
          x2 = x2f * scales[s].width;
          y2 = y2f * scales[s].height;
          features[s][i] = Feature(x1, y1, x2, y2);//所谓像素比较,就是指两个点的位置。随机产生的,产生后不改变
      }

  }
  //Thresholds
  thrN = 0.5*nstructs;

  //Initialize Posteriors
  for (int i = 0; i<nstructs; i++) {
      posteriors.push_back(vector<float>(pow(2.0,structSize), 0));
      pCounter.push_back(vector<int>(pow(2.0,structSize), 0));
      nCounter.push_back(vector<int>(pow(2.0,structSize), 0));
  }
}
/**
 * 按照预先算好的像素比较集,从image得到具体像素来计算像素比较,作为特征输出。一个图,一个缩放尺度,对应10棵树的特征
 * image:图像矩阵,存放像素信息
 * scale_idx:缩放尺寸
 * fern:算出特征值放这里
 */
void FerNNClassifier::getFeatures(const cv::Mat& image,const int& scale_idx, vector<int>& fern){
  int leaf;
  for (int t=0;t<nstructs;t++){
      leaf=0;
      for (int f=0; f<structSize; f++){
          leaf = (leaf << 1) + features[scale_idx][t*nstructs+f](image);//feature结构体存储的是两个点的位置
          //返回的patch图像片在(y1,x1)和(y2, x2)点的像素比较值,返回0或者1
          //然后leaf就记录了这13位的二进制代码,作为特征
      }
      fern[t]=leaf;//fern是存储10个13位二进制码的数组,表示一个patch的特征
  }
}
/**
 * 算出某些(13个)特征在所有(13棵)树的先验概率和
 */
float FerNNClassifier::measure_forest(vector<int> fern) {
  float votes = 0;
  for (int i = 0; i < nstructs; i++) {
      votes += posteriors[i][fern[i]];//一个棵对一个特征有一定的权值,就是10个概率相加
  }
  return votes;
}
/*
 * 如果正样本或者负样本的数量变化了,重现计算这个特征的先验概率
 * pCounter[i][idx]对于第i棵树,含有idx特征的正样本的数量
 * nCounter[i][idx]对于第i棵树,含有idx特征的负样本的数量
 */
void FerNNClassifier::update(const vector<int>& fern, int C, int N) {
  int idx;
  for (int i = 0; i < nstructs; i++) {
      idx = fern[i];
      (C==1) ? pCounter[i][idx] += N : nCounter[i][idx] += N;
     // pCounter[i][idx] 在第i棵树上,含有特征idx的正样本的数量
      if (pCounter[i][idx]==0) {
          posteriors[i][idx] = 0;
      } else {
          posteriors[i][idx] = ((float)(pCounter[i][idx]))/(pCounter[i][idx] + nCounter[i][idx]);
      }
  }
}
/*
 * 输入已经一些打好标签(标志着是正还是负样本)的特征值,来更新先验概率(训练的本质?)
 */
void FerNNClassifier::trainF(const vector<std::pair<vector<int>,int> >& ferns,int resample){
  // Conf = function(2,X,Y,Margin,Bootstrap,Idx)
  //                 0 1 2 3      4         5
  //  double *X     = mxGetPr(prhs[1]); -> ferns[i].first
  //  int numX      = mxGetN(prhs[1]);  -> ferns.size()
  //  double *Y     = mxGetPr(prhs[2]); ->ferns[i].second
  //  double thrP   = *mxGetPr(prhs[3]) * nTREES; ->threshold*nstructs
  //  int bootstrap = (int) *mxGetPr(prhs[4]); ->resample
  thrP = thr_fern*nstructs;                                                          // int step = numX / 10;
  //for (int j = 0; j < resample; j++) {                      // for (int j = 0; j < bootstrap; j++) {
      for (int i = 0; i < ferns.size(); i++){               //   for (int i = 0; i < step; i++) {
                                                            //     for (int k = 0; k < 10; k++) {
                                                            //       int I = k*step + i;//box index
                                                            //       double *x = X+nTREES*I; //tree index
          if(ferns[i].second==1){                           //       if (Y[I] == 1) {
              if(measure_forest(ferns[i].first)<=thrP)      //         if (measure_forest(x) <= thrP)
                update(ferns[i].first,1,1);                 //             update(x,1,1);
          }else{                                            //        }else{
              if (measure_forest(ferns[i].first) >= thrN)   //         if (measure_forest(x) >= thrN)
                update(ferns[i].first,0,1);                 //             update(x,0,1);
          }
      }
  //}
}

void FerNNClassifier::trainNN(const vector<cv::Mat>& nn_examples){
  float conf,dummy;
  vector<int> y(nn_examples.size(),0);
  y[0]=1;//只有一个等于1啊,什么意思呢 ?只有第一个正样本?
  vector<int> isin;
  for (int i=0;i<nn_examples.size();i++){                          //  For each example
      NNConf(nn_examples[i],isin,conf,dummy);                      //  Measure Relative similarity
      if (y[i]==1 && conf<=thr_nn){                                //    if y(i) == 1 && conf1 <= tld.model.thr_nn % 0.65
          if (isin[1]<0){                                          //      if isnan(isin(2))
              pEx = vector<Mat>(1,nn_examples[i]);                 //        tld.pex = x(:,i);
              continue;                                            //        continue;
          }                                                        //      end
          //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
          pEx.push_back(nn_examples[i]);
      }                                                            //    end
      if(y[i]==0 && conf>0.5)                                      //  if y(i) == 0 && conf1 > 0.5
        nEx.push_back(nn_examples[i]);                             //    tld.nex = [tld.nex x(:,i)];

  }                                                                 //  end
  acum++;
  printf("%d. Trained NN examples: %d positive %d negative\n",acum,(int)pEx.size(),(int)nEx.size());
}                                                                  //  end

/**
 * 这是nn分类起的核心,分类的根据就是根据相似度的值来分的
 * rsconf 相似度
 * csconf 保守相似度
 */
void FerNNClassifier::NNConf(const Mat& example, vector<int>& isin,float& rsconf,float& csconf){
  /*Inputs:
   * -NN Patch
   * Outputs:
   * -Relative Similarity (rsconf), Conservative Similarity (csconf), In pos. set|Id pos set|In neg. set (isin)
   */
  isin=vector<int>(3,-1);
  if (pEx.empty()){ //if isempty(tld.pex) % IF positive examples in the model are not defined THEN everything is negative
      rsconf = 0; //    conf1 = zeros(1,size(x,2));
      csconf=0;
      return;
  }
  if (nEx.empty()){ //if isempty(tld.nex) % IF negative examples in the model are not defined THEN everything is positive
      rsconf = 1;   //    conf1 = ones(1,size(x,2));
      csconf=1;
      return;
  }
  Mat ncc(1,1,CV_32F);
  float nccP,csmaxP,maxP=0;
  bool anyP=false;
  int maxPidx,validatedPart = ceil(pEx.size()*valid);
  float nccN, maxN=0;
  bool anyN=false;
  for (int i=0;i<pEx.size();i++){
      matchTemplate(pEx[i],example,ncc,CV_TM_CCORR_NORMED);      // measure NCC to positive examples 归一化相关匹配法
      nccP=(((float*)ncc.data)[0]+1)*0.5;
      if (nccP>ncc_thesame)
        anyP=true;
      if(nccP > maxP){
          maxP=nccP;
          maxPidx = i;
          if(i<validatedPart)
            csmaxP=maxP;
      }
  }
  for (int i=0;i<nEx.size();i++){
      matchTemplate(nEx[i],example,ncc,CV_TM_CCORR_NORMED);     //measure NCC to negative examples
      nccN=(((float*)ncc.data)[0]+1)*0.5;
      if (nccN>ncc_thesame)
        anyN=true;
      if(nccN > maxN)
        maxN=nccN;
  }
  //set isin
  if (anyP) isin[0]=1;  //if he query patch is highly correlated with any positive patch in the model then it is considered to be one of them
  isin[1]=maxPidx;      //get the index of the maximall correlated positive patch
  if (anyN) isin[2]=1;  //if  the query patch is highly correlated with any negative patch in the model then it is considered to be one of them
  //Measure Relative Similarity
  float dN=1-maxN;
  float dP=1-maxP;
  rsconf = (float)dN/(dN+dP);
  //Measure Conservative Similarity
  dP = 1 - csmaxP;
  csconf =(float)dN / (dN + dP);
}
/**
 * 根据新的负样本,重新计算fern特征的阈值 和 nn分类器的阈值
 * 阈值要比所有的负样本的自信度要大
 */
void FerNNClassifier::evaluateTh(const vector<pair<vector<int>,int> >& nXT,const vector<cv::Mat>& nExT){
float fconf;
  for (int i=0;i<nXT.size();i++){
    fconf = (float) measure_forest(nXT[i].first)/nstructs;
    if (fconf>thr_fern)
      thr_fern=fconf;
}
  vector <int> isin;
  float conf,dummy;
  for (int i=0;i<nExT.size();i++){
      NNConf(nExT[i],isin,conf,dummy);
      if (conf>thr_nn)
        thr_nn=conf;
  }
  if (thr_nn>thr_nn_valid)
    thr_nn_valid = thr_nn;
}

void FerNNClassifier::show(){
  Mat examples((int)pEx.size()*pEx[0].rows,pEx[0].cols,CV_8U);
  double minval;
  Mat ex(pEx[0].rows,pEx[0].cols,pEx[0].type());
  for (int i=0;i<pEx.size();i++){
    minMaxLoc(pEx[i],&minval);
    pEx[i].copyTo(ex);
    ex = ex-minval;
    Mat tmp = examples.rowRange(Range(i*pEx[i].rows,(i+1)*pEx[i].rows));
    ex.convertTo(tmp,CV_8U);
  }
  imshow("Examples",examples);
}



注:

原作者是用matlab实现的,我分析的源码是其他大神用c++和opencv实现的,源码可以从

https://github.com/arthurv/OpenTLD或者https://github.com/alantrrs/OpenTLD下载

本序列参考了zouxy09同学的序列文章,在此表示感谢

http://blog.csdn.net/zouxy09/article/details/7893011


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值