压缩跟踪Compressive Tracking源码理解
在前面一个介绍《Real-Time Compressive Tracking》这个paper的感知跟踪算法的博文中,我说过后面会学习下它的C++源码,但是当时因为有些事,所以就没有看了。今天,上到博客,看到一朋友在这个博文中评论说,有个地方不太明白。然后,觉得该履行自己的承诺,去学习学习源码了。所以刚才就花了几个小时去看了C++的源码,做了详细的注释。希望对大家有点帮助。在这也感谢这位朋友。当然,因为自己也刚刚接触这个领域,所以也有很多地方我也看不懂或者理解错了,也渴望大家的指导。
下面是这个算法的工程网站:里面包含了上面这篇论文、Matlab和C++版本的代码,还有测试数据、demo等。
http://www4.comp.polyu.edu.hk/~cslzhang/CT/CT.htm
之前自己学习这个《Real-Time Compressive Tracking》介绍的感知跟踪算法:
http://blog.csdn.net/zouxy09/article/details/8118360
非常感谢Kaihua等的paper《Real-Time Compressive Tracking》,非常感谢它的C++代码的编写和贡献者Yang Xian。
这个C++代码编写的非常简洁、清晰和漂亮。另外,经原作者提示,代码注释中不明白的地方(我打问号的地方)可以看本博文的原作者的评论。非常感谢Yang Xian的指导。
好了,废话不多说了。下面是自己注释的源码。因为代码编写的流程非常清晰,所以我就不总结流程了。这个工程包含三个文件:CompressiveTracker.cpp、CompressiveTracker.h和RunTracker.cpp,其中因为RunTracker.cpp和TLD算法中的run_tld.cpp差不多,我这里就不注释了,大家可以参考我之前的:
TLD(Tracking-Learning-Detection)学习与源码理解之(四)
http://blog.csdn.net/zouxy09/article/details/7893032
下面是具体的源码:
CompressiveTracker.h
- /************************************************************************
- * File: CompressiveTracker.h
- * Brief: C++ demo for paper: Kaihua Zhang, Lei Zhang, Ming-Hsuan Yang,"Real-Time Compressive Tracking," ECCV 2012.
- * Version: 1.0
- * Author: Yang Xian
- * Email: yang_xian521@163.com
- * Date: 2012/08/03
- * History:
- * Revised by Kaihua Zhang on 14/8/2012
- * Email: zhkhua@gmail.com
- * Homepage: http://www4.comp.polyu.edu.hk/~cskhzhang/
- ************************************************************************/
- //这是一个比较常用的C/C++杂注,只要在头文件的最开始加入这条杂注,就能够保证头文件只被插入和编译一次
- #pragma once
- #include <opencv2/opencv.hpp>
- #include <vector>
- using std::vector;
- using namespace cv;
- //---------------------------------------------------
- class CompressiveTracker
- {
- public:
- CompressiveTracker(void);
- ~CompressiveTracker(void);
- private:
- int featureMinNumRect;
- int featureMaxNumRect;
- int featureNum; //每个box的harr特征个数(也就是弱分类器个数)
- vector<vector<Rect>> features;
- vector<vector<float>> featuresWeight;
- int rOuterPositive; //在离上一帧跟踪到的目标位置的距离小于rOuterPositive的范围内采集 正样本
- vector<Rect> samplePositiveBox; //采集的正样本box集
- vector<Rect> sampleNegativeBox; //采集的负样本box集
- int rSearchWindow; //扫描窗口的大小,或者说检测box的大小
- Mat imageIntegral; //图像的积分图
- Mat samplePositiveFeatureValue; //采集的正样本的harr特征值
- Mat sampleNegativeFeatureValue; //采集的负样本的harr特征值
- //对每个样本z(m维向量),它的低维表示是v(n维向量,n远小于m)。假定v中的各元素是独立分布的。
- //假定在分类器H(v)中的条件概率p(vi|y=1)和p(vi|y=0)属于高斯分布,并且可以用以下四个参数来描述:
- //分别是描述正负样本的高斯分布的均值u和方差sigma
- vector<float> muPositive;
- vector<float> sigmaPositive;
- vector<float> muNegative;
- vector<float> sigmaNegative;
- float learnRate; //学习速率,控制分类器参数更新的步长
- vector<Rect> detectBox; //需要检测的box
- Mat detectFeatureValue;
- RNG rng; //随机数
- private:
- void HaarFeature(Rect& _objectBox, int _numFeature);
- void sampleRect(Mat& _image, Rect& _objectBox, float _rInner, float _rOuter, int _maxSampleNum, vector<Rect>& _sampleBox);
- void sampleRect(Mat& _image, Rect& _objectBox, float _srw, vector<Rect>& _sampleBox);
- void getFeatureValue(Mat& _imageIntegral, vector<Rect>& _sampleBox, Mat& _sampleFeatureValue);
- void classifierUpdate(Mat& _sampleFeatureValue, vector<float>& _mu, vector<float>& _sigma, float _learnRate);
- void radioClassifier(vector<float>& _muPos, vector<float>& _sigmaPos, vector<float>& _muNeg, vector<float>& _sigmaNeg,
- Mat& _sampleFeatureValue, float& _radioMax, int& _radioMaxIndex);
- public:
- void processFrame(Mat& _frame, Rect& _objectBox);
- void init(Mat& _frame, Rect& _objectBox);
- };
CompressiveTracker.cpp
- #include "CompressiveTracker.h"
- #include <math.h>
- #include <iostream>
- using namespace cv;
- using namespace std;
- //------------------------------------------------
- //构造函数,初始化各参数
- CompressiveTracker::CompressiveTracker(void)
- {
- featureMinNumRect = 2;
- featureMaxNumRect = 4; // number of rectangle from 2 to 4
- featureNum = 50; // number of all weaker classifiers, i.e,feature pool
- rOuterPositive = 4; // radical scope of positive samples
- rSearchWindow = 25; // size of search window
- muPositive = vector<float>(featureNum, 0.0f);
- muNegative = vector<float>(featureNum, 0.0f);
- sigmaPositive = vector<float>(featureNum, 1.0f);
- sigmaNegative = vector<float>(featureNum, 1.0f);
- learnRate = 0.85f; // Learning rate parameter
- }
- CompressiveTracker::~CompressiveTracker(void)
- {
- }
- //通过积分图来计算采集到的每一个样本的harr特征,这个特征通过与featuresWeight来相乘
- //就相当于投影到随机测量矩阵中了,也就是进行稀疏表达了。这里不明白的话,可以看下
- //论文中的图二,就比较直观了。
- //还有一点:实际上这里采用的不属于真正的harr特征,我博客中翻译有误。这里计算的是
- //在box中采样得到的不同矩形框的灰度加权求和(当权重是负数的时候就是灰度差)
- //当为了表述方便,我下面都用harr特征来描述。
- //每一个样本有50个harr特征,每一个harr特征是由2到3个随机选择的矩形框来构成的,
- //对这些矩形框的灰度加权求和作为这一个harr特征的特征值。
- void CompressiveTracker::HaarFeature(Rect& _objectBox, int _numFeature)
- /*Description: compute Haar features
- Arguments:
- -_objectBox: [x y width height] object rectangle
- -_numFeature: total number of features. The default is 50.
- */
- {
- //_numFeature是一个样本box的harr特征个数,共50个。而上面说到,
- //每一个harr特征是由2到3个随机选择的矩形框(vector<Rect>()类型)来构成的。
- features = vector<vector<Rect>>(_numFeature, vector<Rect>());
- //每一个反应特征的矩形框对应于一个权重,实际上就是随机测量矩阵中相应的元素,用它来与对应的特征
- //相乘,表示以权重的程度来感知这个特征。换句话说,featuresWeight就是随机测量矩阵。
- //这个矩阵的元素的赋值看论文中的第二部分。或者也可以参考下我的博文:(呵呵,好像博文也没说清楚)
- //http://blog.csdn.net/zouxy09/article/details/8118360
- featuresWeight = vector<vector<float>>(_numFeature, vector<float>());
- //numRect是每个特征的矩形框个数还是论文中说的随机测量矩阵中的s?还有兼备两种功能?
- //论文中说s取2或者3时,矩阵就满足Johnson-Lindenstrauss推论。
- int numRect;
- Rect rectTemp;
- float weightTemp;
- for (int i=0; i<_numFeature; i++)
- {
- //如何生成服从某个概率分布的随机数(或者说 sample)的问题。
- //比如,你想要从一个服从正态分布的随机变量得到 100 个样本,那么肯定抽到接近其均值的样本的
- //概率要大许多,从而导致抽到的样本很多是集中在那附近的。
- //rng.uniform()返回一个从[ 1,2)范围均匀采样的随机数,即在[ 1,2)内服从均匀分布(取不同值概率相同)
- //那么下面的功能就是得到[2,4)范围的随机数,然后用cvFloor返回不大于参数的最大整数值,那要么是2,要么是3。
- numRect = cvFloor(rng.uniform((double)featureMinNumRect, (double)featureMaxNumRect));
- //int c = 1;
- for (int j=0; j<numRect; j++)
- {
- //我在一个box中随机生成一个矩形框,那和你这个box的x和y坐标就无关了,但我必须保证我选择
- //的这个矩形框不会超出你这个box的范围啊,是吧
- //但这里的3和下面的2是啥意思呢?我就不懂了,个人理解是为了避免这个矩形框太靠近box的边缘了
- //要离边缘最小2个像素,不知道这样理解对不对,恳请指导
- rectTemp.x = cvFloor(rng.uniform(0.0, (double)(_objectBox.width - 3)));
- rectTemp.y = cvFloor(rng.uniform(0.0, (double)(_objectBox.height - 3)));
- //cvCeil 返回不小于参数的最小整数值
- rectTemp.width = cvCeil(rng.uniform(0.0, (double)(_objectBox.width - rectTemp.x - 2)));
- rectTemp.height = cvCeil(rng.uniform(0.0, (double)(_objectBox.height - rectTemp.y - 2)));
- //保存得到的特征模板。注意哦,这里的矩形框是相对于box的相对位置哦,不是针对整幅图像的哦
- features[i].push_back(rectTemp);
- //weightTemp = (float)pow(-1.0, c);
- //pow(-1.0, c)也就是-1的c次方,而c随机地取0或者1,也就是说weightTemp是随机的正或者负。
- //随机测量矩阵中,矩阵元素有三种,sqrt(s)、-sqrt(s)和零。为正和为负的概率是相等的,
- //这就是为什么是[2,4)均匀采样的原因,就是取0或者1概率一样。
- //但是这里为什么是sqrt(s)分之一呢?还有什么时候是0呢?论文中是0的概率不是挺大的吗?
- //没有0元素,哪来的稀疏表达和压缩呢?不懂,恳请指导!(当然稀疏表达的另一个好处
- //就是只需保存非零元素。但这里和这个有关系吗?)
- weightTemp = (float)pow(-1.0, cvFloor(rng.uniform(0.0, 2.0))) / sqrt(float(numRect));
- //保存每一个特征模板对应的权重
- featuresWeight[i].push_back(weightTemp);
- }
- }
- }
- //在上一帧跟踪的目标box的周围采集若干正样本和负样本,来初始化或者更新分类器的
- void CompressiveTracker::sampleRect(Mat& _image, Rect& _objectBox, float _rInner, float _rOuter, int _maxSampleNum, vector<Rect>& _sampleBox)
- /* Description: compute the coordinate of positive and negative sample image templates
- Arguments:
- -_image: processing frame
- -_objectBox: recent object position
- -_rInner: inner sampling radius
- -_rOuter: Outer sampling radius
- -_maxSampleNum: maximal number of sampled images
- -_sampleBox: Storing the rectangle coordinates of the sampled images.
- */
- {
- int rowsz = _image.rows - _objectBox.height - 1;
- int colsz = _image.cols - _objectBox.width - 1;
- //我们是在上一帧跟踪的目标box的周围采集正样本和负样本的,而这个周围是通过以
- //这个目标为中心的两个圆来表示,这两个圆的半径是_rInner和_rOuter。
- //我们在离上一帧跟踪的目标box的小于_rInner距离的范围内采集正样本,
- //在大于_rOuter距离的范围内采集负样本(论文中还有一个上界,但好像
- //这里没有,其实好像也没什么必要噢)
- float inradsq = _rInner*_rInner;
- float outradsq = _rOuter*_rOuter;
- int dist;
- //这四个是为了防止采集的框超出图像范围的,对采集的box的x和y坐标做限制
- int minrow = max(0,(int)_objectBox.y-(int)_rInner);
- int maxrow = min((int)rowsz-1,(int)_objectBox.y+(int)_rInner);
- int mincol = max(0,(int)_objectBox.x-(int)_rInner);
- int maxcol = min((int)colsz-1,(int)_objectBox.x+(int)_rInner);
- int i = 0;
- //分母相当于x能采集的范围乘以y能采集的范围,也就是可以采集的最大box个数,
- //那么_maxSampleNum(我们需要采集的box的最大个数)肯定得小于或者等于它。
- //那这个prob是干嘛的呢?到下面用到它的地方说
- float prob = ((float)(_maxSampleNum))/(maxrow-minrow+1)/(maxcol-mincol+1);
- int r;
- int c;
- _sampleBox.clear();//important
- Rect rec(0,0,0,0);
- for( r=minrow; r<=(int)maxrow; r++ )
- for( c=mincol; c<=(int)maxcol; c++ ){
- //计算生成的box到目标box的距离
- dist = (_objectBox.y-r)*(_objectBox.y-r) + (_objectBox.x-c)*(_objectBox.x-c);
- //后两个条件是保证距离需要在_rInner和_rOuter的范围内
- //那么rng.uniform(0.,1.) < prob 这个是干嘛的呢?
- //连着上面看,如果_maxSampleNum大于那个最大个数,prob就大于1,这样,
- //rng.uniform(0.,1.) < prob这个条件就总能满足,表示在这个范围产生的
- //所以box我都要了(因为我本身想要更多的,但是你给不了我那么多,那么你能给的,我肯定全要了)。
- //那如果你给的太多了,我不要那么多,也就是prob<1,那我就随机地跳几个走好了
- if( rng.uniform(0.,1.) < prob && dist < inradsq && dist >= outradsq ){
- rec.x = c;
- rec.y = r;
- rec.width = _objectBox.width; //没有做尺度不变?至此至终box的大小都没变化
- rec.height= _objectBox.height;
- _sampleBox.push_back(rec);
- i++;
- }
- }
- _sampleBox.resize(i);
- }
- //这个sampleRect的重载函数是用来在上一帧跟踪的目标box的周围(距离小于_srw)采集若干box来待检测。
- //与上面的那个不一样,上面那个是在这一帧已经检测出目标的基础上,采集正负样本来更新分类器的。
- //上面那个属于论文中提到的算法的第四个步骤,这个是第一个步骤。然后过程差不多,没什么好说的了
- void CompressiveTracker::sampleRect(Mat& _image, Rect& _objectBox, float _srw, vector<Rect>& _sampleBox)
- /* Description: Compute the coordinate of samples when detecting the object.*/
- {
- int rowsz = _image.rows - _objectBox.height - 1;
- int colsz = _image.cols - _objectBox.width - 1;
- float inradsq = _srw*_srw;
- int dist;
- int minrow = max(0,(int)_objectBox.y-(int)_srw);
- int maxrow = min((int)rowsz-1,(int)_objectBox.y+(int)_srw);
- int mincol = max(0,(int)_objectBox.x-(int)_srw);
- int maxcol = min((int)colsz-1,(int)_objectBox.x+(int)_srw);
- int i = 0;
- int r;
- int c;
- Rect rec(0,0,0,0);
- _sampleBox.clear();//important
- for( r=minrow; r<=(int)maxrow; r++ )
- for( c=mincol; c<=(int)maxcol; c++ ){
- dist = (_objectBox.y-r)*(_objectBox.y-r) + (_objectBox.x-c)*(_objectBox.x-c);
- if( dist < inradsq ){
- rec.x = c;
- rec.y = r;
- rec.width = _objectBox.width;
- rec.height= _objectBox.height;
- _sampleBox.push_back(rec);
- i++;
- }
- }
- _sampleBox.resize(i);
- }
- // Compute the features of samples
- //通过积分图来计算采集到的每一个样本的harr特征,这个特征通过与featuresWeight来相乘
- //就相当于投影到随机测量矩阵中了,也就是进行稀疏表达了。这里不明白的话,可以看下
- //论文中的图二,就比较直观了。所以这里得到的是:每个样本的稀疏表达后的harr特征。
- //还有一点:实际上这里采用的不属于真正的harr特征,我博客中翻译有误。这里计算的是
- //在box中采样得到的不同矩形框的灰度加权求和
- void CompressiveTracker::getFeatureValue(Mat& _imageIntegral, vector<Rect>& _sampleBox, Mat& _sampleFeatureValue)
- {
- int sampleBoxSize = _sampleBox.size();
- _sampleFeatureValue.create(featureNum, sampleBoxSize, CV_32F);
- float tempValue;
- int xMin;
- int xMax;
- int yMin;
- int yMax;
- for (int i=0; i<featureNum; i++)
- {
- for (int j=0; j<sampleBoxSize; j++)
- {
- tempValue = 0.0f;
- for (size_t k=0; k<features[i].size(); k++)
- {
- //features中保存的特征模板(矩形框)是相对于box的相对位置的,
- //所以需要加上box的坐标才是其在整幅图像中的坐标
- xMin = _sampleBox[j].x + features[i][k].x;
- xMax = _sampleBox[j].x + features[i][k].x + features[i][k].width;
- yMin = _sampleBox[j].y + features[i][k].y;
- yMax = _sampleBox[j].y + features[i][k].y + features[i][k].height;
- //通过积分图来快速计算一个矩形框的像素和,积分图不了解的话,可以看下我的这个博文:
- //http://blog.csdn.net/zouxy09/article/details/7929570
- //那么这里tempValue就是经过稀释矩阵加权后的灰度和了。
- //每一个harr特征是由2到3个矩形框来构成的,对这些矩形框的灰度加权求和
- //作为这一个harr特征的特征值。然后一个样本有50个harr特征
- tempValue += featuresWeight[i][k] *
- (_imageIntegral.at<float>(yMin, xMin) +
- _imageIntegral.at<float>(yMax, xMax) -
- _imageIntegral.at<float>(yMin, xMax) -
- _imageIntegral.at<float>(yMax, xMin));
- }
- _sampleFeatureValue.at<float>(i,j) = tempValue;
- }
- }
- }
- // Update the mean and variance of the gaussian classifier
- //论文中是通过用高斯分布去描述样本的每一个harr特征的概率分布的。高斯分布就可以通过期望和方差
- //两个参数来表征。然后通过正负样本的每一个harr特征高斯概率分布的对数比值,来构建分类器决策
- //该box属于目标还是背景。这里计算新采集到的正负样本的特征的期望和标准差,并用其来更新分类器
- void CompressiveTracker::classifierUpdate(Mat& _sampleFeatureValue, vector<float>& _mu, vector<float>& _sigma, float _learnRate)
- {
- Scalar muTemp;
- Scalar sigmaTemp;
- for (int i=0; i<featureNum; i++)
- {
- //计算所有正样本或者负样本的某个harr特征的期望和标准差
- meanStdDev(_sampleFeatureValue.row(i), muTemp, sigmaTemp);
- //这个模型参数更新的公式见论文的公式6
- _sigma[i] = (float)sqrt( _learnRate*_sigma[i]*_sigma[i] + (1.0f-_learnRate)*sigmaTemp.val[0]*sigmaTemp.val[0]
- + _learnRate*(1.0f-_learnRate)*(_mu[i]-muTemp.val[0])*(_mu[i]-muTemp.val[0])); // equation 6 in paper
- _mu[i] = _mu[i]*_learnRate + (1.0f-_learnRate)*muTemp.val[0]; // equation 6 in paper
- }
- }
- // Compute the ratio classifier
- void CompressiveTracker::radioClassifier(vector<float>& _muPos, vector<float>& _sigmaPos, vector<float>& _muNeg, vector<float>& _sigmaNeg,
- Mat& _sampleFeatureValue, float& _radioMax, int& _radioMaxIndex)
- {
- float sumRadio;
- //FLT_MAX是最大的浮点数的宏定义,那么-FLT_MAX就是最小的浮点数了
- //这个是拿来存放 那么多box中最大的分类分数的
- _radioMax = -FLT_MAX;
- //这个是对应于上面那个,是存放分类分数最大的那个box的
- _radioMaxIndex = 0;
- float pPos;
- float pNeg;
- int sampleBoxNum = _sampleFeatureValue.cols;
- for (int j=0; j<sampleBoxNum; j++) //每帧采样得到的需要检测的box
- {
- sumRadio = 0.0f;
- for (int i=0; i<featureNum; i++) //每个box的需要匹配的特征数
- {
- //计算每个特征的概率,特征分布近似于高斯分布,故将描述该特征的均值和标准差代入高斯模型就可以
- //得到,分别在正样本和负样本的基础上,出现该特征的概率是多少。如果正样本时候的概率大,那么
- //我们就说,这个特征对应的样本是正样本。数学上比较大小,就是减法或者除法了,这里是取对数比值
- pPos = exp( (_sampleFeatureValue.at<float>(i,j)-_muPos[i])*(_sampleFeatureValue.at<float>(i,j)-_muPos[i]) / -(2.0f*_sigmaPos[i]*_sigmaPos[i]+1e-30) ) / (_sigmaPos[i]+1e-30);
- pNeg = exp( (_sampleFeatureValue.at<float>(i,j)-_muNeg[i])*(_sampleFeatureValue.at<float>(i,j)-_muNeg[i]) / -(2.0f*_sigmaNeg[i]*_sigmaNeg[i]+1e-30) ) / (_sigmaNeg[i]+1e-30);
- //paper的方程4:计算分类结果,得到一个分数,这个分数是由一个样本或者box的50个特征(弱分类)
- //进入分类器分类得到的结果总和(强分类?)。表征的是目前这个box的特征属于正样本(目标)的
- //可能性大小。哪个分数最大,自然我就认为你是目标了。(当然,在具体应用中需要加一些策略去
- //改善误跟踪的情况。例如如果最高的分数都达不到一个阈值,那就不存在目标等)
- sumRadio += log(pPos+1e-30) - log(pNeg+1e-30); // equation 4
- }
- if (_radioMax < sumRadio) //拿到最大的分数和相应的box索引
- {
- _radioMax = sumRadio;
- _radioMaxIndex = j;
- }
- }
- }
- //传入第一帧和要跟踪的目标box(由文件读入或者用户鼠标框选),来初始化分类器
- void CompressiveTracker::init(Mat& _frame, Rect& _objectBox)
- {
- // compute feature template
- //计算box的harr特征模板,先存着
- HaarFeature(_objectBox, featureNum);
- // compute sample templates
- //因为这是第一帧,目标box是由由文件读入或者用户鼠标框选的,是已知的,
- //所以我们通过在这个目标box周围,采集正样本和负样本来初始化我们的分类器
- sampleRect(_frame, _objectBox, rOuterPositive, 0, 1000000, samplePositiveBox);
- sampleRect(_frame, _objectBox, rSearchWindow*1.5, rOuterPositive+4.0, 100, sampleNegativeBox);
- //计算积分图,用以快速的计算harr特征
- integral(_frame, imageIntegral, CV_32F);
- //通过上面的积分图,计算我们采样到的正负样本的box的harr特征
- getFeatureValue(imageIntegral, samplePositiveBox, samplePositiveFeatureValue);
- getFeatureValue(imageIntegral, sampleNegativeBox, sampleNegativeFeatureValue);
- //通过上面的正负样本的特征来初始化分类器
- classifierUpdate(samplePositiveFeatureValue, muPositive, sigmaPositive, learnRate);
- classifierUpdate(sampleNegativeFeatureValue, muNegative, sigmaNegative, learnRate);
- }
- //传入上一帧跟踪到的box,来处理新的一帧
- void CompressiveTracker::processFrame(Mat& _frame, Rect& _objectBox)
- {
- // predict
- //在上一帧跟踪到的boxbox周围,采集需要检测的box框
- sampleRect(_frame, _objectBox, rSearchWindow, detectBox);
- //计算这一帧的积分图
- integral(_frame, imageIntegral, CV_32F);
- //用积分图来计算上面采集到的每个box的harr特征
- getFeatureValue(imageIntegral, detectBox, detectFeatureValue);
- int radioMaxIndex;
- float radioMax;
- //对上面的每个box进行匹配分类
- radioClassifier(muPositive, sigmaPositive, muNegative, sigmaNegative, detectFeatureValue, radioMax, radioMaxIndex);
- //得到分数最高的那个目标box
- _objectBox = detectBox[radioMaxIndex];
- // update
- //在新跟踪到的这个目标box的周围,采集正样本和负样本来更新我们的分类器
- sampleRect(_frame, _objectBox, rOuterPositive, 0.0, 1000000, samplePositiveBox);
- sampleRect(_frame, _objectBox, rSearchWindow*1.5, rOuterPositive+4.0, 100, sampleNegativeBox);
- //通过上面的积分图,计算我们采样到的正负样本的box的harr特征
- getFeatureValue(imageIntegral, samplePositiveBox, samplePositiveFeatureValue);
- getFeatureValue(imageIntegral, sampleNegativeBox, sampleNegativeFeatureValue);
- //通过上面的正负样本的特征来更新我们的分类器
- classifierUpdate(samplePositiveFeatureValue, muPositive, sigmaPositive, learnRate);
- classifierUpdate(sampleNegativeFeatureValue, muNegative, sigmaNegative, learnRate);
- }
版权声明:本文为博主原创文章,未经博主允许不得转载。
-
顶
- 11
-
踩
- 0
-
猜你在找
25楼 you_and_007 2015-06-17 11:27发表 [回复]-
-
将工程改为debug模式时,弹出"丢失MSVCP100D.dll",不知道大家有没有遇到。
24楼 pregious 2015-04-13 15:40发表 [回复]-
-
楼主,您好,我编译的时候出现了OpenCV Error:Assertion failed (scn ==3 || scn==4) in unknown function, file ..\..\..\modules\imgproc\src\color.cpp, line 2433
这样的错误,不知道该怎么改正,请问您遇到过这种问题吗?
23楼 ljl02521 2014-12-22 19:49发表 [回复]-
-
要用2008+opencv2.4.2就可以了
22楼 二姐不想去实验室 2014-11-15 22:47发表 [回复]-
-
我最近也在看这篇论文,但是不知道代码里的那个变量是论文中所说的矩阵R????
Re: roamer_nuptgczx 2015-01-31 15:03发表 [回复]-
-
回复moli152_:矩阵R在代码中没有显式给出,实际上就是保存在feature容器里的那个采样模板
21楼 端午过后的猪 2014-08-07 17:13发表 [回复]-
-
楼主,有没有在运行的过程中跟踪框跟踪到边缘的时候出现程序崩溃的状况,在getFeatureValue()里,是不是哪里有越界的问题??求解答呀,弄不明白了。
20楼 Anfanger1 2014-07-29 14:04发表 [回复]-
-
我下载Matlab版本的代码运行测试了,发现确实很实时。但是,我在用自己的一个目标由尺度变化的视频测试,发现算法就失效了。博主有没有什么方法可以解决。另外这种跟踪方法是不是只适合单个目标的跟踪,如果是多个目标,怎么处理呢?
19楼 _Ars 2014-07-09 19:37发表 [回复]-
-
编译时,void readImageSequenceFiles(),为什么这个函数中的量都是未定义的?求指导
Re: histronger 2014-09-13 16:46发表 [回复]-
-
回复u010142673:你好,你的问题解决了吗?我现在也是这样,什么MAX_PATH、WIN32_FIND_DATA这些都是未定义的变量,怎么回事呢?
Re: roamer_nuptgczx 2014-10-15 17:19发表 [回复]-
-
回复yq702563853yq:RunTracker.cpp里面少了头文件#include <Windows.h>
18楼 wang3857287 2014-05-08 14:57发表 [回复]-
-
楼主,我的平台是vs2008+opencv2.3.1,运行程序问题很多,调试多次,没成功,请问这个程序对平台是不是又最低要求呢?求赐教
17楼 Kasim_T 2014-02-25 14:03发表 [回复]-
-
你好~ 这篇论文是关于压缩感知的追踪
但代码貌似是一个MIL的简化版 不知博主怎么看
Re: zhkhua_198345 2014-04-14 17:14发表 [回复]-
-
回复kaxium:你从哪里看出是MIL的简化版?
16楼 onbaby1 2014-01-06 19:37发表 [回复]-
-
//features中保存的特征模板(矩形框)是相对于box的相对位置的,
//所以需要加上box的坐标才是其在整幅图像中的坐标
xMin = _sampleBox[j].x + features[i][k].x;
xMax = _sampleBox[j].x + features[i][k].x + features[i][k].width;
yMin = _sampleBox[j].y + features[i][k].y;
yMax = _sampleBox[j].y + features[i][k].y + features[i][k].height;
这部分的作用能讲解下吗,为什么用样本的坐标加上特征的坐标,什么意思?
Re: zouxy09 2014-01-06 20:24发表 [回复]-
-
回复onbaby1:_sampleBox是采集到的样本的box嘛,然后feature是在这个box内采集小box嘛,那这个小box在图像中的位置,当然是样本box的位置,加上小box相对于样本box的位置了。
就像如果中国的北京作为原点(0,0),而上海的坐标是(10,-20),而广州在以上海为原点的时候的坐标是(-20,-30),那么广州在以北京作为原点的坐标是多少呢?就是两者相加嘛
15楼 lh2008071102 2013-11-28 10:15发表 [回复]-
-
博主您好,非常感谢您对代码的分析!我分别在linux和windows下运行了代码,linux下运行没什么问题,但是windows下总是出来两个图像框,一个空白的一个是摄像头的实时显示,选择初始跟踪框的时候程序经常不能相应鼠标,不知道您有没有遇到类似的问题?openCv不是很熟,不知道哪里有问题
Re: Crossi 2014-08-08 17:28发表 [回复]-
-
回复lh2008071102:请问一下您的Linux下的代码是哪里下载的?还是自己写的?能不能分享一下?感激不尽~.q.q:353625575@qq.com
Re: zouxy09 2013-11-28 15:23发表 [回复]-
-
回复lh2008071102:哦,这个问题我没有遇到过,但之前有个朋友运行TLD的时候遇到过(CT的mian函数文件的代码和TLD差不多),他是直接拷贝我网上注释的代码的,可能是遇到了编码问题,所以会出现这种错误。你试下修改下工程的编码,或者你下载原始的代码回来编译试试。
Re: histronger 2014-09-12 09:51发表 [回复]-
-
回复zouxy09:博主,我也遇到了代码运行时出现两个窗口,没法响应鼠标的问题,可是我确实是用的源码编译的,不是从网页上copy的,一直解决不了,怎么办呢?
Re: roamer_nuptgczx 2015-01-24 15:09发表 [回复]-
-
回复yq702563853yq:我感觉,有可能是你开启了两个名字不同的显示框,仔细看看代码,或者单步调试查找问题
14楼 jia_zhengshen 2013-08-08 16:47发表 [回复]-
-
我也试了试,但是实时性完全保证不了啊,有人说是opencv的tbb,cuda加速没有开,你认同这个观点吗?
13楼 生命的礼物 2013-04-08 19:37发表 [回复]-
-
非常感谢楼主分享对C++代码的理解,楼主功力深厚呀!我还有个不懂的地方,HaarFeature函数定义及引用的时候,形参和实参为什么是_objectBox,而不是_sampleBox呢?_objectBox表示某帧图像的目标位置,我觉得应该是对于任意一个采集的正或负样本都需要通过HaarFeature这一函数进行处理,得到这一样本对应的features 及 featuresWeight 。
Re: zouxy09 2013-04-08 23:04发表 [回复]-
-
回复zhaosong19891209:呵呵,有点久没看了。个人觉得你的理解是对的啊。实际上不管是目标(上一帧目标周围采集的样本)还是样本(新的一帧采集的待匹配的样本)都需要通过HaarFeature这一函数来计算特征的啊。所以不用纠结形参名称是什么。如果硬要说的话,你说的也对啊。是对样本提取特征。只是样本有我上面说的两种。
Re: 生命的礼物 2013-04-09 08:15发表 [回复]-
-
回复zouxy09: 恩恩,其实只是一个参数名而已,呵呵。
Re: roamer_nuptgczx 2015-01-24 19:17发表 [回复]-
-
回复zhaosong19891209:学的第一个跟踪算法就是CT,今天又重新看了一遍代码,对于你这个问题,说说我的理解吧。HaarFeature函数体现了这篇论文的核心思路,它并不是用于计算Haar特征的,而是实现了将高维的类Haar特征压缩成50维。HaarFeature函数定义及引用的时候,形参和实参的确都是_objectBox,而不是_sampleBox,并且在这个函数里给_objectBox传递的实参就是第一帧鼠标选择的那个框!在源码里你会发现CT在对整个视频的跟踪过程中,HaarFeature函数只在初始化时的init函数里调用过一次,HaarFeature函数在鼠标选择的框中抽取一系列的小box(也就是论文里图Fig.2中David脸上的小白框),并把小box的相对坐标及对应的权重分别保存在features和featuresWeight这两个容器里,而且之后一直保持不变,也就是说,从第二帧开始,计算每一个_sampleBox的Haar特征都仅仅只需要调用getFeatureValue这个函数,这个你可以从processFrame函数中清晰地看出,getFeatureValue函数里使用的小box的features及 featuresWeight一直是第一帧初始化时生成的,之后没有更新过。
举个例子,比如现在跟踪人脸,每次计算Haar特征时,对每一个_sampleBox抽取的小box的相对位置和权重都应该和第一帧是相同的(这是相对意义上的,小box的实际位置还要叠加上_sampleBox的坐标),这样才能保证第一帧如果随机抽取到的小box是人脸的鼻子,以后的每一帧都要抽取“假定是鼻子”的那个部位,而不是每次都随机抽取,因为在跟踪过程中,人脸在画面中是可以动的,但是鼻子相对于脸的位置是保持不变的,每一个_sampleBox都应该拿“假定是鼻子”的那个部位的Haar特征和上一帧目标中真实鼻子的Haar特征比较才是合理的,事实上只有唯一一个准确的_sampleBox的“假定是鼻子”的那个部位正好就是鼻子(此时贝叶斯分类器的得分最高),这个_sampleBox即是当前帧跟踪到的目标框!
【写了很多但不知道我表达清楚没有,如果我的理解有误,欢迎大家指出!】
12楼 liuzhanwei2006 2013-03-25 16:28发表 [回复]-
-
楼主的理解很深刻,受益良多。楼主能否分析一下MATLAB代码哪
11楼 xinzailushang 2013-01-24 13:04发表 [回复]-
-
赞~楼主理解的很深,想请教一个问题,目标消失的时候该如何判断呢,我输入的视频图像序列前段视频是有目标的,到后段视频跟踪的目标已经没有了可红色的矩形框还在,不知道该如何解决这个问题?谢谢!
Re: zouxy09 2013-01-24 13:21发表 [回复]-
-
回复xinzailushang:呵呵,首先谢谢鼓励!
这个是在线跟踪漂移和跟丢的问题。个人感觉这个是比较宽泛的问题,目前的策略还是有挺多的,例如运动分析啊、重构误差啊、保存历史模板进行匹配啊等等,至于有没有比较好的推荐,个人暂时没有这方面的经验。望各位前辈指导!呵呵
10楼 zkhua 2013-01-23 16:18发表 [回复]-
-
楼主理解的非常好。多谢楼主了。
9楼 zl201110 2013-01-21 19:53发表 [回复]-
-
程序里哪里用到训练后的结果了
Re: zouxy09 2013-01-22 11:13发表 [回复]-
-
回复zl201110:按上面所说的,在上一帧中,我们就已经训练得到了更新后的高斯模型,那么在新的一帧采集到的样本,提取其特征,然后代入这个高斯模型,就可以得到这个特征属于目标和背景的概率了,具体实现在radioClassifier中
Re: zouxy09 2013-01-22 11:15发表 [回复]-
-
回复zouxy09:这里用到的更新后的参数(来计算概率):
pPos = exp( (_sampleFeatureValue.at<float>(i,j)-_muPos[i])*(_sampleFeatureValue.at<float>(i,j)-_muPos[i]) / -(2.0f*_sigmaPos[i]*_sigmaPos[i]+1e-30) ) / (_sigmaPos[i]+1e-30); ……
8楼 zl201110 2013-01-21 19:52发表 [回复]-
-
贝叶斯训练部分在哪体现了
Re: zouxy09 2013-01-22 11:11发表 [回复]-
-
回复zl201110:论文中贝叶斯是描述某个特征属于目标或者背景的概率,因为目标或者背景的先验概率看做一样,所以计算每个特征的属于目标和背景的后验概率的对数比值就相当于他们的似然函数的比值。另外,某个特征属于目标或者背景的概率模型可以用高斯模型来描述,而高斯分布就可以通过期望和方差两个参数来表征。所以classifierUpdate中通过在跟踪到的结果的周围采集样本来对概率模型参数进行更新就是训练了(论文的公式6 )
Re: zouxy09 2013-01-22 11:15发表 [回复]-
-
回复zouxy09:这里更新了参数:
//这个模型参数更新的公式见论文的公式6
_sigma[i] = (float)sqrt( _learnRate*_sigma[i]*_sigma[i] + (1.0f-_learnRate)*sigmaTemp.val[0]*sigmaTemp.val[0] ……
Re: zl201110 2013-01-22 22:57发表 [回复]-
-
回复zouxy09:太感谢了,可如何判定训练已经适度呢,如何决定训练结束了呢
7楼 QDaQiao 2013-01-06 11:18发表 [回复]-
-
是不是降了维是50维呢
Re: zouxy09 2013-01-06 12:51发表 [回复]-
-
回复QDaQiao:您好,我说说我的个人理解啊。
首先,对于原始的一个样本来说。它的特征我们用一系列的矩形特征(矩阵内所有像素灰度的和)来描述。而矩形特征可位于图像任意位置,大小也可以任意改变,所以很小的检测窗口就含有非常多的矩形特征。每一个矩阵特征(矩阵内所有像素灰度的和)即xi,所有的这些矩阵特征就构成了原始样本图像的特征向量x。这个x的维数很大。(那任意位置任意大小的矩形框内的像素和怎么算啊,就是积分图啊,和论文中的一系列多尺度矩形滤波器进行卷积的功能是一样的,因为卷积模板核的元素全是1)。
好了,原始样本的特征x的维数太大了,我们需要对其降维。降维后每一个样本只有50个特征,也就是v,n=50,每一个特征分量vi是由2到3个随机选择的矩形框的灰度加权求和来得到,也就是vi=rij*xj(对所有j),rij就是随机矩阵R中的第i行的第j个元素,因为每一行只有最多4个非零的元素(这里只挑2个或者3个)。那我们就只需要计算这些非零元素与xj(第j个矩形框内像素的灰度和)的乘积的和就行了。那怎么决定矩阵R的这一行哪些元素非零呢?这就通过随机矩阵的性质,通过论文中说到的生成随机矩阵的方法来随机生成了。这样得到的低维的特征v几乎保留原有图像的高维特征信息x。
不知道自己的理解对不,若有错误,往大家指正。呵呵
Re: Doomcrafter 2014-02-13 13:17发表 [回复]-
-
回复zouxy09:博主对降维的理解我觉得是正确的,不过貌似程序里面关于s的选值和论文交待的不同,有两个问题:
1.RIP矩阵中系数值为√2或√3的前提是s为2或者3,但实验中s的取值为m/4,程序中仍然取的√2或√3,而且为何s等于子窗口的数目有点不解
2.程序中是除以√2或√3,不是乘以,有点奇怪
Re: 小羽毛的心愿 2015-07-27 18:15发表 [回复]-
-
回复Doomcrafter:不是乘以,是除以的原因是,作者乘以了概率,1/2S的概率分别取根号S,以1-1/s的概率取根号S乘以0(即为0)
Re: h_yongqiang 2013-03-06 10:57发表 [回复]-
-
回复zouxy09:说的太好了,不懂的地方经你一说都懂了
Re: QDaQiao 2013-01-06 15:48发表 [回复]-
-
回复zouxy09:清晰了,谢谢谢谢
6楼 QDaQiao 2013-01-04 16:30发表 [回复]-
-
大侠们 文章中的fig.3的横纵坐标是什么,还有那个降维在程序里体现在哪里了呢 本来haar特征的维数就不大啊
Re: zouxy09 2013-01-06 13:14发表 [回复]-
-
回复QDaQiao:它是用高斯分布来描述某帧的所有正样本的某个harr特征的概率分布的。假设这一帧我们采集了100个正样本,每个样本有50个harr特征(就是一个harr特征就是一个数值),那么对于某个harr特征vi来说,100个正样本就有100个这个harr特征vi(也就是100个数了),那我们就可以用一个直方图来统计着100个数的分布,例如有10个样本的特征值vi是10000,有50个样本的特征值是12000,这样就可以建立一个直方图了(横坐标就是特征值vi,纵坐标就是特征值vi属于同一个区间的正样本的数目),唉,可以看到,这个直方图和高斯分布很像,所以就可以通过高斯分布来描述了。那就表示,如果新来一个样本,它对应的特征值是vi,那把vi代入这个高斯分布,就会得到这个特征vi属于正样本或者负样本的概率了。
不知道自己的理解对不,若有错误,往大家指正。呵呵
5楼 liuchiliuchi900316 2012-12-24 20:15发表 [回复]-
-
楼主,您好,感谢您的博文,Compressive Tracking的C++代码比TLD简单了很多很多,但是任有一点不解,就是函数HarrFeature函数中的numRect变量,这个变量的意思应该就是测量矩阵R中一行的非零个数吧?但是论文中的意思这个个数应该是<=4的,但是按照他的定义,个数是2或者3吧?不知道是不是这么理解的
Re: zouxy09 2012-12-25 09:54发表 [回复]-
-
回复liuchiliuchi900316:numRect变量,这个变量的意思应该就是测量矩阵R中一行的非零个数吧?-----恩恩,是的。
论文中说:上式s取2或者3时,矩阵就满足Johnson-Lindenstrauss推论。
呵呵,我觉得您理解的是对的啊。
4楼 chiefmonk 2012-12-06 11:27发表 [回复]-
-
用TLD的dataset测试了一下,发现对剧烈运动的跟踪非常差,完全跟错了,偏差达到几十像素。跟错的视频包括07/08/09,另外对多个行人的视频03后半段也跟飞了
Re: cathyzhaosago 2012-12-07 09:31发表 [回复]-
-
回复chiefmonk:是的,发现对剧烈的运动,比较难以适应,增大搜索范围也会导致跟踪到背景上,需要反复调。
那这么说来,如果想要在实际的监控视频上进行实验,应该是TLD的效果更好一些了?两者用的特征好像差不太多,TLD优势的部分在于半监督学习机制?对TLD了解比较少,你是怎么看的呢?
Re: chiefmonk 2012-12-06 11:35发表 [回复]-
-
回复chiefmonk:把搜索窗口改成50像素,对07_motorcross中摩托车手的白色衣服跟踪好一些,但是经常出现跟踪到了天空的白云上
06_car的跟踪在大搜索窗口下变差了。
另外速度慢了几倍,具体没测试。
初步怀疑是用简单的积分图差做目标特征过于简单
Re: yang_xian521 2012-12-06 12:54发表 [回复]-
-
回复chiefmonk:ct跟踪那段摩托车的视频时,参数要调整,learnrate也要调整,效果会好一些。
3楼 cathyzhaosago 2012-12-06 11:18发表 [回复]-
-
也跑了下这个C++版本,用作者提供的数据集做实验,效果没有很好呢,还是容易drift~想问下您做类似实验了吗?参数如何配置?
2楼 qqtximeng5 2012-12-03 10:21发表 [回复]-
-
你们都是高手!
1楼 yang_xian521 2012-11-22 09:16发表 [回复] [引用] [举报]-
-
没有裁剪的,和matlab代码的模块都能对应上~~注释很详细,赞
Re: zouxy09 2012-11-22 09:26发表 [回复] [引用] [举报]-
-
回复yang_xian521:呵呵,谢谢您的指导,谢谢您的代码啊!不知道您可否解答下我的疑问呢?1、“多尺度的卷积”如果没有裁剪,那在代码的什么地方进行实现呢?2、我对weightTemp权值的理解对吗?为什么没有0元素的呢?或者说什么地方进行了稀疏表达呢?3、为什么是sqrt(s)分之一?我昨晚该为sqrt(s)运行也没有问题哦。渴望得到您的指导!另外,您的博客内容给我的学习帮助非常大,感谢您的无私贡献!
Re: yang_xian521 2012-11-22 09:35发表 [回复] [引用] [举报]-
-
回复zouxy09:客气啦,多尺度的卷积其实就是矩形滤波器,文章是用积分图来做的,我个人的理解,没那么复杂。矩阵一行大概10^5+的特征,只有2~4个元素的权重是1、-1,其他都是0,这就是稀疏。只是写代码和paper又是两回事,如果真的把那个矩形写出来太大了,不利于计算,所以就用了这样的办法实现。sqrt(s)这个问题我和zhang也请教过,他的意思只是归一化,对结果影响不大。看你blog还是很棒的,一起加油
Re: gxiaob 2013-05-31 22:36发表 [回复]-
-
回复yang_xian521:您好!一直不太明白文章中的多尺度卷积和矩形滤波器是什么意思,不知道能够给解释一下,谢谢!
Re: chiefmonk 2012-12-05 22:33发表 [回复]-
-
回复yang_xian521:您好:关于多尺度矩形卷积这部分,我也不理解。积分图只是体现了整个box的一小部分,怎么能体现出尺度呢?另外,从代码看,每帧检测出的矩形大小都是一样的,而如果支持尺度变换的目标检测的话,那每帧的检测矩形应该大小可以变化的
Re: yang_xian521 2012-12-05 22:39发表 [回复]-
-
回复chiefmonk:作者指的尺度应该不是object的尺度,而是box filter的尺度
Re: chiefmonk 2012-12-06 10:03发表 [回复]-
-
回复yang_xian521:这个Box Filter在代码中是HaarFeature函数中生成的矩形吗?这些矩形生成后有大小就再也不变了,如果目标变小了,那这些固定大小的矩形算出的积分图更多的反映了背景的特征,怎么体现出针对目标的尺度变换的特点呢?
Re: yang_xian521 2012-12-06 12:53发表 [回复]-
-
回复chiefmonk:文章没说对目标可以多尺度。。。。。
Re: zouxy09 2012-11-22 09:59发表 [回复] [引用] [举报]-
-
回复yang_xian521:呵呵,非常感谢解惑!多向您学习!很多时候我就是不太清楚paper和编程的差别,看来有时候写代码的境界真的决定了很多啊!呵呵
Re: yang_xian521 2012-11-22 10:03发表 [回复] [引用] [举报]-
-
回复zouxy09:您太谦虚了~
Re: _Ars 2014-07-09 19:38发表 [回复] [引用] [举报]-
-
回复yang_xian521:请问编译时,readImageSequenceFiles()中显示未定义的量有很多,求教
Re: histronger 2014-09-13 16:48发表 [回复] [引用] [举报]-
-
回复u010142673:你好,文件解决了吗?我也是这样,好多变量,比如WIN32_FIND_DATA,都没定义