转自:http://blog.csdn.net/zouxy09/article/details/7893081
下面是自己在看论文和这些大牛的分析过程中,对代码进行了一些理解,但是由于自己接触图像处理和机器视觉没多久,另外由于自己编程能力比较弱,所以分析过程可能会有不少的错误,希望各位不吝指正。而且,因为编程很多地方不懂,所以注释得非常乱,还海涵。
TLD.h
- #include <opencv2/opencv.hpp>
- #include <tld_utils.h>
- #include <LKTracker.h>
- #include <FerNNClassifier.h>
- #include <fstream>
- //Bounding Boxes
- struct BoundingBox : public cv::Rect {
- BoundingBox(){}
- BoundingBox(cv::Rect r): cv::Rect(r){} //继承的话需要初始化基类
- public:
- float overlap; //Overlap with current Bounding Box
- int sidx; //scale index
- };
- //Detection structure
- struct DetStruct {
- std::vector<int> bb;
- std::vector<std::vector<int> > patt;
- std::vector<float> conf1;
- std::vector<float> conf2;
- std::vector<std::vector<int> > isin;
- std::vector<cv::Mat> patch;
- };
- //Temporal structure
- struct TempStruct {
- std::vector<std::vector<int> > patt;
- std::vector<float> conf;
- };
- struct OComparator{ //比较两者重合度
- OComparator(const std::vector<BoundingBox>& _grid):grid(_grid){}
- std::vector<BoundingBox> grid;
- bool operator()(int idx1,int idx2){
- return grid[idx1].overlap > grid[idx2].overlap;
- }
- };
- struct CComparator{ //比较两者确信度?
- CComparator(const std::vector<float>& _conf):conf(_conf){}
- std::vector<float> conf;
- bool operator()(int idx1,int idx2){
- return conf[idx1]> conf[idx2];
- }
- };
- class TLD{
- private:
- cv::PatchGenerator generator; //PatchGenerator类用来对图像区域进行仿射变换
- FerNNClassifier classifier;
- LKTracker tracker;
- //下面这些参数通过程序开始运行时读入parameters.yml文件进行初始化
- ///Parameters
- int bbox_step;
- int min_win;
- int patch_size;
- //initial parameters for positive examples
- //从第一帧得到的目标的bounding box中(文件读取或者用户框定),经过几何变换得
- //到 num_closest_init * num_warps_init 个正样本
- int num_closest_init; //最近邻窗口数 10
- int num_warps_init; //几何变换数目 20
- int noise_init;
- float angle_init;
- float shift_init;
- float scale_init;
- ////从跟踪得到的目标的bounding box中,经过几何变换更新正样本(添加到在线模型?)
- //update parameters for positive examples
- int num_closest_update;
- int num_warps_update;
- int noise_update;
- float angle_update;
- float shift_update;
- float scale_update;
- //parameters for negative examples
- float bad_overlap;
- float bad_patches;
- ///Variables
- //Integral Images 积分图像,用以计算2bitBP特征(类似于haar特征的计算)
- //Mat最大的优势跟STL很相似,都是对内存进行动态的管理,不需要之前用户手动的管理内存
- cv::Mat iisum;
- cv::Mat iisqsum;
- float var;
- //Training data
- //std::pair主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型。
- //pair实质上是一个结构体,其主要的两个成员变量是first和second,这两个变量可以直接使用。
- //在这里用来表示样本,first成员为 features 特征点数组,second成员为 labels 样本类别标签
- std::vector<std::pair<std::vector<int>,int> > pX; //positive ferns <features,labels=1> 正样本
- std::vector<std::pair<std::vector<int>,int> > nX; // negative ferns <features,labels=0> 负样本
- cv::Mat pEx; //positive NN example
- std::vector<cv::Mat> nEx; //negative NN examples
- //Test data
- std::vector<std::pair<std::vector<int>,int> > nXT; //negative data to Test
- std::vector<cv::Mat> nExT; //negative NN examples to Test
- //Last frame data
- BoundingBox lastbox;
- bool lastvalid;
- float lastconf;
- //Current frame data
- //Tracker data
- bool tracked;
- BoundingBox tbb;
- bool tvalid;
- float tconf;
- //Detector data
- TempStruct tmp;
- DetStruct dt;
- std::vector<BoundingBox> dbb;
- std::vector<bool> dvalid; //检测有效性??
- std::vector<float> dconf; //检测确信度??
- bool detected;
- //Bounding Boxes
- std::vector<BoundingBox> grid;
- std::vector<cv::Size> scales;
- std::vector<int> good_boxes; //indexes of bboxes with overlap > 0.6
- std::vector<int> bad_boxes; //indexes of bboxes with overlap < 0.2
- BoundingBox bbhull; // hull of good_boxes //good_boxes 的 壳,也就是窗口的边框
- BoundingBox best_box; // maximum overlapping bbox
- public:
- //Constructors
- TLD();
- TLD(const cv::FileNode& file);
- void read(const cv::FileNode& file);
- //Methods
- void init(const cv::Mat& frame1,const cv::Rect &box, FILE* bb_file);
- void generatePositiveData(const cv::Mat& frame, int num_warps);
- void generateNegativeData(const cv::Mat& frame);
- void processFrame(const cv::Mat& img1,const cv::Mat& img2,std::vector<cv::Point2f>& points1,std::vector<cv::Point2f>& points2,
- BoundingBox& bbnext,bool& lastboxfound, bool tl,FILE* bb_file);
- void track(const cv::Mat& img1, const cv::Mat& img2,std::vector<cv::Point2f>& points1,std::vector<cv::Point2f>& points2);
- void detect(const cv::Mat& frame);
- void clusterConf(const std::vector<BoundingBox>& dbb,const std::vector<float>& dconf,std::vector<BoundingBox>& cbb,std::vector<float>& cconf);
- void evaluate();
- void learn(const cv::Mat& img);
- //Tools
- void buildGrid(const cv::Mat& img, const cv::Rect& box);
- float bbOverlap(const BoundingBox& box1,const BoundingBox& box2);
- void getOverlappingBoxes(const cv::Rect& box1,int num_closest);
- void getBBHull();
- void getPattern(const cv::Mat& img, cv::Mat& pattern,cv::Scalar& mean,cv::Scalar& stdev);
- void bbPoints(std::vector<cv::Point2f>& points, const BoundingBox& bb);
- void bbPredict(const std::vector<cv::Point2f>& points1,const std::vector<cv::Point2f>& points2,
- const BoundingBox& bb1,BoundingBox& bb2);
- double getVar(const BoundingBox& box,const cv::Mat& sum,const cv::Mat& sqsum);
- bool bbComp(const BoundingBox& bb1,const BoundingBox& bb2);
- int clusterBB(const std::vector<BoundingBox>& dbb,std::vector<int>& indexes);
- };
#include <opencv2/opencv.hpp> #include <tld_utils.h> #include <LKTracker.h> #include <FerNNClassifier.h> #include <fstream> //Bounding Boxes struct BoundingBox : public cv::Rect { BoundingBox(){} BoundingBox(cv::Rect r): cv::Rect(r){} //继承的话需要初始化基类 public: float overlap; //Overlap with current Bounding Box int sidx; //scale index }; //Detection structure struct DetStruct { std::vector<int> bb; std::vector<std::vector<int> > patt; std::vector<float> conf1; std::vector<float> conf2; std::vector<std::vector<int> > isin; std::vector<cv::Mat> patch; }; //Temporal structure struct TempStruct { std::vector<std::vector<int> > patt; std::vector<float> conf; }; struct OComparator{ //比较两者重合度 OComparator(const std::vector<BoundingBox>& _grid):grid(_grid){} std::vector<BoundingBox> grid; bool operator()(int idx1,int idx2){ return grid[idx1].overlap > grid[idx2].overlap; } }; struct CComparator{ //比较两者确信度? CComparator(const std::vector<float>& _conf):conf(_conf){} std::vector<float> conf; bool operator()(int idx1,int idx2){ return conf[idx1]> conf[idx2]; } }; class TLD{ private: cv::PatchGenerator generator; //PatchGenerator类用来对图像区域进行仿射变换 FerNNClassifier classifier; LKTracker tracker; //下面这些参数通过程序开始运行时读入parameters.yml文件进行初始化 ///Parameters int bbox_step; int min_win; int patch_size; //initial parameters for positive examples //从第一帧得到的目标的bounding box中(文件读取或者用户框定),经过几何变换得 //到 num_closest_init * num_warps_init 个正样本 int num_closest_init; //最近邻窗口数 10 int num_warps_init; //几何变换数目 20 int noise_init; float angle_init; float shift_init; float scale_init; ////从跟踪得到的目标的bounding box中,经过几何变换更新正样本(添加到在线模型?) //update parameters for positive examples int num_closest_update; int num_warps_update; int noise_update; float angle_update; float shift_update; float scale_update; //parameters for negative examples float bad_overlap; float bad_patches; ///Variables //Integral Images 积分图像,用以计算2bitBP特征(类似于haar特征的计算) //Mat最大的优势跟STL很相似,都是对内存进行动态的管理,不需要之前用户手动的管理内存 cv::Mat iisum; cv::Mat iisqsum; float var; //Training data //std::pair主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型。 //pair实质上是一个结构体,其主要的两个成员变量是first和second,这两个变量可以直接使用。 //在这里用来表示样本,first成员为 features 特征点数组,second成员为 labels 样本类别标签 std::vector<std::pair<std::vector<int>,int> > pX; //positive ferns <features,labels=1> 正样本 std::vector<std::pair<std::vector<int>,int> > nX; // negative ferns <features,labels=0> 负样本 cv::Mat pEx; //positive NN example std::vector<cv::Mat> nEx; //negative NN examples //Test data std::vector<std::pair<std::vector<int>,int> > nXT; //negative data to Test std::vector<cv::Mat> nExT; //negative NN examples to Test //Last frame data BoundingBox lastbox; bool lastvalid; float lastconf; //Current frame data //Tracker data bool tracked; BoundingBox tbb; bool tvalid; float tconf; //Detector data TempStruct tmp; DetStruct dt; std::vector<BoundingBox> dbb; std::vector<bool> dvalid; //检测有效性?? std::vector<float> dconf; //检测确信度?? bool detected; //Bounding Boxes std::vector<BoundingBox> grid; std::vector<cv::Size> scales; std::vector<int> good_boxes; //indexes of bboxes with overlap > 0.6 std::vector<int> bad_boxes; //indexes of bboxes with overlap < 0.2 BoundingBox bbhull; // hull of good_boxes //good_boxes 的 壳,也就是窗口的边框 BoundingBox best_box; // maximum overlapping bbox public: //Constructors TLD(); TLD(const cv::FileNode& file); void read(const cv::FileNode& file); //Methods void init(const cv::Mat& frame1,const cv::Rect &box, FILE* bb_file); void generatePositiveData(const cv::Mat& frame, int num_warps); void generateNegativeData(const cv::Mat& frame); void processFrame(const cv::Mat& img1,const cv::Mat& img2,std::vector<cv::Point2f>& points1,std::vector<cv::Point2f>& points2, BoundingBox& bbnext,bool& lastboxfound, bool tl,FILE* bb_file); void track(const cv::Mat& img1, const cv::Mat& img2,std::vector<cv::Point2f>& points1,std::vector<cv::Point2f>& points2); void detect(const cv::Mat& frame); void clusterConf(const std::vector<BoundingBox>& dbb,const std::vector<float>& dconf,std::vector<BoundingBox>& cbb,std::vector<float>& cconf); void evaluate(); void learn(const cv::Mat& img); //Tools void buildGrid(const cv::Mat& img, const cv::Rect& box); float bbOverlap(const BoundingBox& box1,const BoundingBox& box2); void getOverlappingBoxes(const cv::Rect& box1,int num_closest); void getBBHull(); void getPattern(const cv::Mat& img, cv::Mat& pattern,cv::Scalar& mean,cv::Scalar& stdev); void bbPoints(std::vector<cv::Point2f>& points, const BoundingBox& bb); void bbPredict(const std::vector<cv::Point2f>& points1,const std::vector<cv::Point2f>& points2, const BoundingBox& bb1,BoundingBox& bb2); double getVar(const BoundingBox& box,const cv::Mat& sum,const cv::Mat& sqsum); bool bbComp(const BoundingBox& bb1,const BoundingBox& bb2); int clusterBB(const std::vector<BoundingBox>& dbb,std::vector<int>& indexes); };
TLD.cpp
- /*
- * TLD.cpp
- *
- * Created on: Jun 9, 2011
- * Author: alantrrs
- */
- #include <TLD.h>
- #include <stdio.h>
- using namespace cv;
- using namespace std;
- TLD::TLD()
- {
- }
- TLD::TLD(const FileNode& file){
- read(file);
- }
- void TLD::read(const FileNode& file){
- ///Bounding Box Parameters
- min_win = (int)file["min_win"];
- ///Genarator Parameters
- //initial parameters for positive examples
- patch_size = (int)file["patch_size"];
- num_closest_init = (int)file["num_closest_init"];
- num_warps_init = (int)file["num_warps_init"];
- noise_init = (int)file["noise_init"];
- angle_init = (float)file["angle_init"];
- shift_init = (float)file["shift_init"];
- scale_init = (float)file["scale_init"];
- //update parameters for positive examples
- num_closest_update = (int)file["num_closest_update"];
- num_warps_update = (int)file["num_warps_update"];
- noise_update = (int)file["noise_update"];
- angle_update = (float)file["angle_update"];
- shift_update = (float)file["shift_update"];
- scale_update = (float)file["scale_update"];
- //parameters for negative examples
- bad_overlap = (float)file["overlap"];
- bad_patches = (int)file["num_patches"];
- classifier.read(file);
- }
- //此函数完成准备工作
- void TLD::init(const Mat& frame1, const Rect& box, FILE* bb_file){
- //bb_file = fopen("bounding_boxes.txt","w");
- //Get Bounding Boxes
- //此函数根据传入的box(目标边界框)在传入的图像frame1中构建全部的扫描窗口,并计算重叠度
- buildGrid(frame1, box);
- printf("Created %d bounding boxes\n",(int)grid.size()); //vector的成员size()用于获取向量元素的个数
- ///Preparation
- //allocation
- //积分图像,用以计算2bitBP特征(类似于haar特征的计算)
- //Mat的创建,方式有两种:1.调用create(行,列,类型)2.Mat(行,列,类型(值))。
- iisum.create(frame1.rows+1, frame1.cols+1, CV_32F);
- iisqsum.create(frame1.rows+1, frame1.cols+1, CV_64F);
- //Detector data中定义:std::vector<float> dconf; 检测确信度??
- //vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector
- //的capacity同时也增加了它的size!reserve是容器预留空间,但在空间内不真正创建元素对象,
- //所以在没有添加新的对象之前,不能引用容器内的元素。
- //不管是调用resize还是reserve,二者对容器原有的元素都没有影响。
- //myVec.reserve( 100 ); // 新元素还没有构造, 此时不能用[]访问元素
- //myVec.resize( 100 ); // 用元素的默认构造函数构造了100个新的元素,可以直接操作新元素
- dconf.reserve(100);
- dbb.reserve(100);
- bbox_step =7;
- //以下在Detector data中定义的容器都给其分配grid.size()大小(这个是一幅图像中全部的扫描窗口个数)的容量
- //Detector data中定义TempStruct tmp;
- //tmp.conf.reserve(grid.size());
- tmp.conf = vector<float>(grid.size());
- tmp.patt = vector<vector<int> >(grid.size(), vector<int>(10,0));
- //tmp.patt.reserve(grid.size());
- dt.bb.reserve(grid.size());
- good_boxes.reserve(grid.size());
- bad_boxes.reserve(grid.size());
- //TLD中定义:cv::Mat pEx; //positive NN example 大小为15*15图像片
- pEx.create(patch_size, patch_size, CV_64F);
- //Init Generator
- //TLD中定义:cv::PatchGenerator generator; //PatchGenerator类用来对图像区域进行仿射变换
- /*
- cv::PatchGenerator::PatchGenerator (
- double _backgroundMin,
- double _backgroundMax,
- double _noiseRange,
- bool _randomBlur = true,
- double _lambdaMin = 0.6,
- double _lambdaMax = 1.5,
- double _thetaMin = -CV_PI,
- double _thetaMax = CV_PI,
- double _phiMin = -CV_PI,
- double _phiMax = CV_PI
- )
- 一般的用法是先初始化一个PatchGenerator的实例,然后RNG一个随机因子,再调用()运算符产生一个变换后的正样本。
- */
- generator = PatchGenerator (0,0,noise_init,true,1-scale_init,1+scale_init,-angle_init*CV_PI/180,
- angle_init*CV_PI/180,-angle_init*CV_PI/180,angle_init*CV_PI/180);
- //此函数根据传入的box(目标边界框),在整帧图像中的全部窗口中寻找与该box距离最小(即最相似,
- //重叠度最大)的num_closest_init个窗口,然后把这些窗口 归入good_boxes容器
- //同时,把重叠度小于0.2的,归入 bad_boxes 容器
- //首先根据overlap的比例信息选出重复区域比例大于60%并且前num_closet_init= 10个的最接近box的RectBox,
- //相当于对RectBox进行筛选。并通过BBhull函数得到这些RectBox的最大边界。
- getOverlappingBoxes(box, num_closest_init);
- printf("Found %d good boxes, %d bad boxes\n",(int)good_boxes.size(),(int)bad_boxes.size());
- printf("Best Box: %d %d %d %d\n",best_box.x, best_box.y, best_box.width, best_box.height);
- printf("Bounding box hull: %d %d %d %d\n", bbhull.x, bbhull.y, bbhull.width, bbhull.height);
- //Correct Bounding Box
- lastbox=best_box;
- lastconf=1;
- lastvalid=true;
- //Print
- fprintf(bb_file,"%d,%d,%d,%d,%f\n",lastbox.x,lastbox.y,lastbox.br().x,lastbox.br().y,lastconf);
- //Prepare Classifier 准备分类器
- //scales容器里是所有扫描窗口的尺度,由buildGrid()函数初始化
- classifier.prepare(scales);
- ///Generate Data
- // Generate positive data
- generatePositiveData(frame1, num_warps_init);
- // Set variance threshold
- Scalar stdev, mean;
- //统计best_box的均值和标准差
- ////例如需要提取图像A的某个ROI(感兴趣区域,由矩形框)的话,用Mat类的B=img(ROI)即可提取
- //frame1(best_box)就表示在frame1中提取best_box区域(目标区域)的图像片
- meanStdDev(frame1(best_box), mean, stdev);
- //利用积分图像去计算每个待检测窗口的方差
- //cvIntegral( const CvArr* image, CvArr* sum, CvArr* sqsum=NULL, CvArr* tilted_sum=NULL );
- //计算积分图像,输入图像,sum积分图像, W+1×H+1,sqsum对象素值平方的积分图像,tilted_sum旋转45度的积分图像
- //利用积分图像,可以计算在某象素的上-右方的或者旋转的矩形区域中进行求和、求均值以及标准方差的计算,
- //并且保证运算的复杂度为O(1)。
- integral(frame1, iisum, iisqsum);
- //级联分类器模块一:方差检测模块,利用积分图计算每个待检测窗口的方差,方差大于var阈值(目标patch方差的50%)的,
- //则认为其含有前景目标方差;var 为标准差的平方
- var = pow(stdev.val[0],2) * 0.5; //getVar(best_box,iisum,iisqsum);
- cout << "variance: " << var << endl;
- //check variance
- //getVar函数通过积分图像计算输入的best_box的方差
- double vr = getVar(best_box, iisum, iisqsum)*0.5;
- cout << "check variance: " << vr << endl;
- // Generate negative data
- generateNegativeData(frame1);
- //Split Negative Ferns into Training and Testing sets (they are already shuffled)
- //将负样本放进 训练和测试集
- int half = (int)nX.size()*0.5f;
- //vector::assign函数将区间[start, end)中的值赋值给当前的vector.
- //将一半的负样本集 作为 测试集
- nXT.assign(nX.begin()+half, nX.end()); //nXT; //negative data to Test
- //然后将剩下的一半作为训练集
- nX.resize(half);
- ///Split Negative NN Examples into Training and Testing sets
- half = (int)nEx.size()*0.5f;
- nExT.assign(nEx.begin()+half,nEx.end());
- nEx.resize(half);
- //Merge Negative Data with Positive Data and shuffle it
- //将负样本和正样本合并,然后打乱
- vector<pair<vector<int>,int> > ferns_data(nX.size()+pX.size());
- vector<int> idx = index_shuffle(0, ferns_data.size());
- int a=0;
- for (int i=0;i<pX.size();i++){
- ferns_data[idx[a]] = pX[i];
- a++;
- }
- for (int i=0;i<nX.size();i++){
- ferns_data[idx[a]] = nX[i];
- a++;
- }
- //Data already have been shuffled, just putting it in the same vector
- vector<cv::Mat> nn_data(nEx.size()+1);
- nn_data[0] = pEx;
- for (int i=0;i<nEx.size();i++){
- nn_data[i+1]= nEx[i];
- }
- ///Training
- //训练 集合分类器(森林) 和 最近邻分类器
- classifier.trainF(ferns_data, 2); //bootstrap = 2
- classifier.trainNN(nn_data);
- ///Threshold Evaluation on testing sets
- //用样本在上面得到的 集合分类器(森林) 和 最近邻分类器 中分类,评价得到最好的阈值
- classifier.evaluateTh(nXT, nExT);
- }
- /* Generate Positive data
- * Inputs:
- * - good_boxes (bbP)