压缩感知感觉很高大上,理论分析一套一套的,本人数学水平有限,理论介绍就免了,来看看Real-Time Compressive Tracking这篇论文是怎样用压缩感知算法来进行目标跟踪的。感谢《Real-Time Compressive Tracking》的作者Kaihua Zhang、Lei Zhang、Ming-Hsuan Yang,把该论文实验所用的代码也给出来了(c++版、matlab版本应有尽有),业界良心啊,让我等小白,看完之后茅塞顿开。下面是那边论文的网页:
http://www4.comp.polyu.edu.hk/~cslzhang/CT/CT.htm
我也是在看了别人对这篇文章的介绍之后,才发现有这么好的资源,分享果然是个好东西:
http://blog.csdn.net/zouxy09/article/details/8118360
这篇博客对论文的思想和怎样运用压缩感知的工作过程有个详细的介绍。
别人都已经写过了,那我写这篇文章的意义何在呢?主要还是对自己所学知识的一个记录和总结吧,哈哈,我就从代码来看看压缩感知怎样运用于目标跟踪吧。阅读源代码,能够丰富程序设计的思路,我看的是c++版,抛开怎样读入视频或图像序列的细节不说,主要看看作者写的CompressiveTracker类。初看之下,类里面的数据函数一大堆,但真正运用的时候只要调用两个接口函数。一个是init(Mat& _frame, Rect& _objectBox),另一个是processFrame(Mat& _frame, Rect& _objectBox)。其他的函数都是为它们服务的。排版不清晰,还请见谅。接下来我干脆贴代码了:
void CompressiveTracker::init(Mat& _frame, Rect& _objectBox)
{
// compute feature template 用到了Harr特征,该函数取了featureNum种不同的harr模板组合
HaarFeature(_objectBox, featureNum);
// compute sample templates
//获取正样本图片和负样图片本用于训练
sampleRect(_frame, _objectBox, rOuterPositive, 0, 1000000, samplePositiveBox);
sampleRect(_frame, _objectBox, rSearchWindow*1.5, rOuterPositive+4.0, 100, sampleNegativeBox);
//计算积分图
integral(_frame, imageIntegral, CV_32F);
//分别计算正样本图片和负样本图片的harr特征值
getFeatureValue(imageIntegral, samplePositiveBox, samplePositiveFeatureValue);
getFeatureValue(imageIntegral, sampleNegativeBox, sampleNegativeFeatureValue);
//分别计算正样本和负样本中每个特征的平均值和标准差
classifierUpdate(samplePositiveFeatureValue, muPositive, sigmaPositive, learnRate);
classifierUpdate(sampleNegativeFeatureValue, muNegative, sigmaNegative, learnRate);
}
具体来看看init中这几个函数的作用:
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.
*/
{
//初始化features为_numFeature个包含Rect的容器
features = vector
//初始化featuresWeight为_numFeature个包含Rect权重的容器
featuresWeight = vector
int numRect;
Rect rectTemp;
float weightTemp;
for (int i=0; i<_numFeature; i++)
{
//rng调用C++中的RNG来生成随机数,需要注意的是:
// rng.uniform(int x, int y)生成[x,y)的随机数,但是如果是uniform(0,1)只会产生0这个值
//一般用uniform(double, double)来生成随机数
//cvFloor 返回不大于参数的最大整数值,产生2-4个rect
numRect = cvFloor(rng.uniform((double)featureMinNumRect, (double)featureMaxNumRect));
//int c = 1;
for (int j=0; j
//从_objectBox里面随机选取随机大小的rect区域,最大尺寸为(_objectBox.width-2)*(_objectBox.height-2)
rectTemp.x = cvFloor(rng.uniform(0.0, (double)(_objectBox.width - 3)));
rectTemp.y = cvFloor(rng.uniform(0.0, (double)(_objectBox.height - 3)));
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)));
features[i].push_back(rectTemp);
//产生满足RIP条件的随机测量矩阵,这是运用压缩感知所必须的条件
weightTemp = (float)pow(-1.0, cvFloor(rng.uniform(0.0, 2.0))) / sqrt(float(numRect))
featuresWeight[i].push_back(weightTemp);
}
}
}
void CompressiveTracker::sampleRect(Mat& _image, Rect& _objectBox, float _rInner, float _rOuter, int _maxSampleNum, vector
/* 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.
*/
{
//生成rect的x,y的最大坐标值,因为它产生的rece跟objectBox的大小是一样的。
int rowsz = _image.rows - _objectBox.height - 1;
int colsz = _image.cols - _objectBox.width - 1;
//生成的rect的右上角要在以object的右上角为中心,最大边长为rInner,最小边长为router的范围内
float inradsq = _rInner*_rInner;
float outradsq = _rOuter*_rOuter;
int dist;
//大概就是产生一个rect上下左右比_objectBox大_rinner来包围_objectBox,在这个范围内选样本
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;
//设置从一个包围objectBox的rect中随机选取一个rect的概率值
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++ ){
dist = (_objectBox.y-r)*(_objectBox.y-r) + (_objectBox.x-c)*(_objectBox.x-c);
//随机选取满足正样本或负样本的rect
if( rng.uniform(0.,1.)
rec.x = c;
rec.y = r;
rec.width = _objectBox.width;
rec.height= _objectBox.height;
_sampleBox.push_back(rec);
i++;
}
}
_sampleBox.resize(i);
}
//计算该图像的积分图,这个在常数时间内完成特征值的计算,值得看看
http://blog.csdn.net/zouxy09/article/details/7929570/
integral(_frame, imageIntegral, CV_32F);
R(nxm)为产生的随机矩阵,x向量表示样本图片,v为在R下生成的特征向量
void CompressiveTracker::getFeatureValue(Mat& _imageIntegral, vector
{
int sampleBoxSize = _sampleBox.size();
_sampleFeatureValue.create(featureNum, sampleBoxSize, CV_32F);
float tempValue;
int xMin;
int xMax;
int yMin;
int yMax;
//把之前生成的harr模板作用于样本图片获取特征值,达到一个降维的作用
//生成一个featureNum*sampleBoxSize的矩阵
for (int i=0; i
for (int j=0; j
tempValue = 0.0f;
for (size_t k=0; k
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;
tempValue += featuresWeight[i][k] *
(_imageIntegral.at
_imageIntegral.at
_imageIntegral.at
_imageIntegral.at
}
_sampleFeatureValue.at
}
}
}
分类器的构建:
文章运用朴素贝叶斯构建分类器,可以看下面的网页了解朴素贝叶斯分类
http://www.cnblogs.com/leoo2sk/archive/2010/09/17/naive-bayesian-classifier.html
//mu、sigma的初始值为0
void CompressiveTracker::classifierUpdate(Mat& _sampleFeatureValue, vector
{
Scalar muTemp;
Scalar sigmaTemp;
for (int i=0; i
//计算每个特征分量的平均值和标准差
meanStdDev(_sampleFeatureValue.row(i), muTemp, sigmaTemp);
_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
}
}
以上是init()中函数的一个介绍,下面来看看processFrame
void CompressiveTracker::processFrame(Mat& _frame, Rect& _objectBox)
{
// predict 从初始的目标框周围产生样本图片
sampleRect(_frame, _objectBox, rSearchWindow,detectBox);
integral(_frame, imageIntegral, CV_32F);
//计算该样本图片的特征向量
getFeatureValue(imageIntegral, detectBox, detectFeatureValue);
int radioMaxIndex;
float radioMax;
//从样本图片中找出一张跟之前正样本
radioClassifier(muPositive, sigmaPositive, muNegative, sigmaNegative, detectFeatureValue, radioMax, radioMaxIndex);
_objectBox = detectBox[radioMaxIndex];
// update
sampleRect(_frame, _objectBox, rOuterPositive, 0.0, 1000000, samplePositiveBox);
sampleRect(_frame, _objectBox, rSearchWindow*1.5, rOuterPositive+4.0, 100, sampleNegativeBox);
getFeatureValue(imageIntegral, samplePositiveBox, samplePositiveFeatureValue);
getFeatureValue(imageIntegral, sampleNegativeBox, sampleNegativeFeatureValue);
classifierUpdate(samplePositiveFeatureValue, muPositive, sigmaPositive, learnRate);
classifierUpdate(sampleNegativeFeatureValue, muNegative, sigmaNegative, learnRate);
}
processFrame()的作用用来从当前frame中的搜索前一个objectRect区域,因为帧之前目标框的变化不太大,以此形成一个样本空间,并从中找出一个使正样本分类器的概率值最大的样本图片,作为当前帧跟踪到的一个目标区域,然后像init所做的工作一样更新每个特征分量的平均值mu和标准方差sigma。它跟init里面的函数就多了一个radioClassifier()。
从样本图片的特征向量中,找出一个H(v)最大的样本图片,则该图片是目标区域
// Compute the ratio classifier
void CompressiveTracker::radioClassifier(vector
Mat& _sampleFeatureValue, float& _radioMax, int& _radioMaxIndex)
{
float sumRadio;
_radioMax = -FLT_MAX;
_radioMaxIndex = 0;
float pPos;
float pNeg;
int sampleBoxNum = _sampleFeatureValue.cols;
//找出样本图片中,使得正样本分类器的与负样本分类器的概率差值最大的图片
for (int j=0; j
sumRadio = 0.0f;
for (int i=0; i
pPos = exp( (_sampleFeatureValue.at
pNeg = exp( (_sampleFeatureValue.at
sumRadio += log(pPos+1e-30) - log(pNeg+1e-30);// equation 4
}
if (_radioMax < sumRadio)
{
_radioMax = sumRadio;
_radioMaxIndex = j;
}
}
}
代码分析就到这里结束了,难道只能选取harr特征作为稀疏随机矩阵?