TLD代码学习(转一)

最近在看视觉跟踪方面的论文,ZK博士的TLD算法作为跟踪算法的state-of-the-art,当然不得不去拜读下了,看完论文后虽然对作者整体的思想有了一个大致了解,但是对于很多细节却也还是无从得知,好在作者将自己的算法源代码全部开源,这也造福了我们这些无知者的胃口,虽然网上有几个c++版本的源码,但是matlab版本作为作者的原始版本,拿来作研究也是极好的。

通览源代码,个人感觉精髓之处无非两个函数,即:

tldInit();

tldProcessFrame();

这二者一个是用来跟踪前的初始化工作,后者当然是run-time的跟踪工作。我个人的阅读注释也将从这两个函数展开。好了,闲话少叙,下面来看看tldInit()这个函数:

进去第一个函数是

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. %输入:  
  2. %tld.source.bb 用户输入的目标标定框  
  3. %size(tld.source.im0.input) 输入图像的尺寸  
  4. %tld.model.min_win 目标标定框的长或宽的最小尺寸  
  5. %输出:  
  6. %tld.grid 是一个6Xn(n表示不同有效尺度下共有多少个的网格)的矩阵,前四行组成列向量表示gridbox的4个顶点,5表示索引,6表示横向的分布点数  
  7. % tld.scales 有效尺度下gridbox的高和宽,为2Xm矩阵(m表示有效的尺度的个数)  
  8. [tld.grid tld.scales] = bb_scan(tld.source.bb,size(tld.source.im0.input),tld.model.min_win);  

再让我们继续进入bb_scan()函数:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bbW   = round(bb_width(bb) .* SCALE);%bb_width(bb)*(0.1615~6.1917)矩形框变尺度  
  2. bbH   = round(bb_height(bb) .* SCALE);  
  3. bbSHH = SHIFT * min(bbH,bbH);%取bbH和bbH中最小的元素组成新的矩阵,维数和原来相同 <span style="font-family: Arial, Helvetica, sans-serif;">SHIFT =</span>0.1表示变形后的矩形框的移动步长  
  4. bbSHW = SHIFT * min(bbH,bbW);  
  5.   
  6. bbF   = [2 2 imsize(2) imsize(1)]';%[2 2 320 240]  
  7.   
  8. bbs = {};  
  9. sca = [];  
  10. idx = 1;  
  11.   
  12. for i = 1:length(SCALE)  
  13.     if bbW(i) < MINBB || bbH(i) < MINBB, continue; end  
  14.       
  15.     left  = round(bbF(1):bbSHW(i):bbF(3)-bbW(i)-1);%2开头,0.1* min(bbH(i),bbW(i))为步长,320- bbW(i)-1为结尾  
  16.     top   = round(bbF(2):bbSHH(i):bbF(4)-bbH(i)-1);%2开头,0.1* min(bbH(i),bbH(i))为步长,240- bbH(i)-1为结尾  
  17.       
  18.     grid  = ntuples(top,left);%重复功能,维度为2 X length(top)*length(left);grid的每个列向量成为网格顶点的坐标点  
  19.     if isempty(grid), continue; end  
  20.       
  21.     bbs{end+1} =  [grid(2,:); ...%省略号为续行,bbs前四行的列向量构成一个矩形网格的四个顶点  
  22.         grid(1,:); ...  
  23.         grid(2,:)+bbW(i)-1; ...  
  24.         grid(1,:)+bbH(i)-1; ...  
  25.         idx*ones(1,size(grid,2));%坐标点对应的编号,从1开始  
  26.         length(left)*ones(1,size(grid,2));];%记录横向分布多少个点?  
  27.     sca  = [sca [bbH(i); bbW(i)]];%在原来SCA的基础上增加[bbH(i); bbW(i)]列向量,该列向量表示某个尺度因子下的gridbox的高和宽  
  28.     idx = idx + 1;  
  29. end  
  30. bb_out = [];  
  31. for i = 1:length(bbs)  
  32.     bb_out = [bb_out bbs{i}];  
  33. end  
总之,该函数功能就是扫描输入图像得到很多的gridbox。

下面来到了产生特征的函数

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. %输入:  
  2. %tld.model.num_trees:有10棵分类树  
  3. %tld.model.num_features:每棵树里有13个特性,这里是会有13个点对的比较  
  4. %输出:  
  5. %tld.features: 4*13X10的矩阵,为fern分类器选取的随机点对,4表示一个点对的四个坐标值  
  6. tld.features  = tldGenerateFeatures(tld.model.num_trees,tld.model.num_features,1);  
还是进入一看究竟:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. SHI = 1/5;  
  2. SCA = 1;  
  3. OFF = SHI;  
  4.   
  5. x = repmat(ntuples(0:SHI:1,0:SHI:1),2,1);%ntuples生成网格点坐标2X36矩阵,repmat表示将生成的矩阵作为初始化元素(还是矩阵)  
  6.                                          %生成一个2X1的大矩阵,这里是4X36(6*6)矩阵  
  7. x = [x x + SHI/2];%前者4X36矩阵,后者矩阵偏移0.1  
  8. k = size(x,2);%k = 36*2  
  9. %rand(1,k)随机生成都1Xk维随机分布矩阵,范围0~1  
  10. r = x; r(3,:) = r(3,:) + (SCA*rand(1,k)+OFF);%x的第3行加上随机的1X72矩阵(0~1)再加上0.2  
  11. l = x; l(3,:) = l(3,:) - (SCA*rand(1,k)+OFF);%x的第3行减去随机的1X72矩阵(0~1)再减去0.2  
  12. t = x; t(4,:) = t(4,:) - (SCA*rand(1,k)+OFF);%x的第4行减去随机的1X72矩阵(0~1)再减去0.2  
  13. b = x; b(4,:) = b(4,:) + (SCA*rand(1,k)+OFF);%x的第4行加上随机的1X72矩阵(0~1)再加上0.2  
  14.   
  15. x = [r l t b];  
  16.   
  17. idx = all(x([1 2],:) < 1 & x([1 2],:) > 0,1);%idx和X维数相同,如果满足条件相应元素为1,否则为0  
  18. x = x(:,idx);%挑选满足条件的所有X的列  
  19. x(x > 1) = 1;%x元素中大于1的则用1代替此元素  
  20. x(x < 0) = 0;%x元素中小于0的则用0代替此元素  
  21.   
  22. numF = size(x,2);%看看现在X还有多少列  
  23.   
  24. x = x(:,randperm(numF));%randperm(numF)从1~numF的数字序列随机打乱,整个表达式是指把X的列打乱  
  25. x = x(:,1:nFEAT*nTREES);%取x的前130列,x目前为4x130矩阵  
  26. x = reshape(x,4*nFEAT,nTREES);%把X塑造成4*13 X 10 的矩阵  
整个函数就是生成10*13个特征点对,坐标用小数表示,表示在box的相对位置,至于为什么随机我也感觉挺困惑的,求解释啊!!

下面再来到fern(0);没什么好说的,来看下面的吧:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. %输入:  
  2. %tld.source.im0.input:输入的图像  
  3. %tld.grid:6Xn(n表示不同有效尺度下共有多少个的网格)的矩阵,gridbox信息  
  4. %tld.features:4*13X10的矩阵,为fern分类器选取的随机点对,4表示一个点对的四个坐标值  
  5. %tld.scales: 有效尺度下gridbox的高和宽,为2Xm矩阵(m表示有效的尺度的个数)  
  6. %输出:  
  7. fern(1,tld.source.im0.input,tld.grid,tld.features,tld.scales); % allocate structures  
进入CPP的fern

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. iHEIGHT    = mxGetM(prhs[1]);//240  
  2. iWIDTH     = mxGetN(prhs[1]);//320  
  3. nTREES     = mxGetN(mxGetField(prhs[3],0,"x"));//<span style="font-family: Arial, Helvetica, sans-serif;">10</span>  
  4. nFEAT      = mxGetM(mxGetField(prhs[3],0,"x")) / 4; // feature has 2 points: x1,y1,x2,y2 13  
  5. thrN       = 0.5 * nTREES;//等于5  
  6. nSCALE     = mxGetN(prhs[4]);//获得共有多少种尺度的BOX,即有效尺度的个数  
  7.   
  8. IIMG       = (double*) malloc(iHEIGHT*iWIDTH*sizeof(double));//积分图像变量准备  
  9. IIMG2      = (double*) malloc(iHEIGHT*iWIDTH*sizeof(double));//平方积分图像变量准备  
  10.   
  11. // BBOX  
  12. mBBOX      = mxGetM(prhs[2]);//等于6   
  13. nBBOX      = mxGetN(prhs[2]);//等于在各个有效尺度下有多少个网格  
  14. BBOX       = create_offsets_bbox(mxGetPr(prhs[2]));//创建保存网格数据索引等数据  
  15.       //prhs[3]是4*13X10的矩阵  
  16.       //matlab 代码中有f.x = x;f.type = 'forest';见到下面就不怪了  
  17. double *x  = mxGetPr(mxGetField(prhs[3],0,"x"));//获得特征点的指针  
  18. double *s  = mxGetPr(prhs[4]);//各种尺度的BOX的尺寸  
  19. OFF        = create_offsets(s,x);//记录各个特征点对在各种尺度下box中的具体位置  
  20.   
  21. for (int i = 0; i<nTREES; i++) {  
  22.     WEIGHT.push_back(vector<double>(pow(2.0,nBIT*nFEAT), 0));//nBIT=1,权重分配权2^13  
  23.     nP.push_back(vector<int>(pow(2.0,nBIT*nFEAT), 0));//nBIT=1,nP分配2^13  
  24.     nN.push_back(vector<int>(pow(2.0,nBIT*nFEAT), 0));//nBIT=1,nN分配2^13  
  25. }  
  26.       //static vector<vector <double> > WEIGHT;  
  27.       //static vector<vector <int> > nP;  
  28.       //static vector<vector <int> > nN;下面见怪不怪,10X 2^13的容器  
  29. for (int i = 0; i<nTREES; i++) {  
  30.     for (int j = 0; j < WEIGHT[i].size(); j++) {  
  31.         WEIGHT[i].at(j) = 0;  
  32.         nP[i].at(j) = 0;  
  33.         nN[i].at(j) = 0;  
  34.     }  
  35. }  

来具体看看create_offset_bbox和creater_offset两个函数

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int *offsets = (int*) malloc(BBOX_STEP*nBBOX*sizeof(int));//7*nBBOX*sizeof(int)  
  2. int *off = offsets;  
  3.   
  4. for (int i = 0; i < nBBOX; i++) {//nBBOX表示所有的网格数  
  5.     double *bb = bb0+mBBOX*i;//偏移到下一个网格的属性的向量  
  6.        //sub2idx(row,col,height)      ((int) (floor((row)+0.5) + floor((col)+0.5)*(height)))  
  7.        //floor不大于  
  8.        //bb[0]:left bb[1]top bb[2]right bb[3]bottom iHEIGHT:240 bb[4]索引从1开始 bb[5]表示number of left-right bboxes  
  9.        //以下记录索引是从左到右,从上到下方式  
  10.     *off++ = sub2idx(bb[1]-1,bb[0]-1,iHEIGHT);//左上顶点索引  
  11.     *off++ = sub2idx(bb[3]-1,bb[0]-1,iHEIGHT);//左下顶点索引  
  12.     *off++ = sub2idx(bb[1]-1,bb[2]-1,iHEIGHT);//右上顶点索引  
  13.     *off++ = sub2idx(bb[3]-1,bb[2]-1,iHEIGHT);//右下顶点索引  
  14.     *off++ = (int) ((bb[2]-bb[0])*(bb[3]-bb[1]));//记录当前网格的大小  
  15.     *off++ = (int) (bb[4]-1)*2*nFEAT*nTREES; // pointer to features for this scale  
  16.     *off++ = bb[5]; // number of left-right bboxes, will be used for searching neighbours  
  17. }  
  18. return offsets;  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int *offsets = (int*) malloc(nSCALE*nTREES*nFEAT*2*sizeof(int));//  
  2. int *off = offsets;  
  3.   
  4. for (int k = 0; k < nSCALE; k++){//共有多少种尺度的BOX  
  5.     double *scale = scale0+2*k;//scale0第一种尺度的尺寸信息,*2表示列向量有2维,即高和宽,此表达式表示偏移到下一个box的尺寸向量  
  6.     for (int i = 0; i < nTREES; i++) {//10  
  7.         for (int j = 0; j < nFEAT; j++) {//13  
  8.                //x0 4*13 X 10 的矩阵  
  9.             double *x  = x0 +4*j + (4*nFEAT)*i;//4*j因为每个feature是一个4维列向量,(4*13)*i即下一棵树  
  10.                //sub2idx(row,col,height) ((int) (floor((row)+0.5) + floor((col)+0.5)*(height)))  
  11.                //scale[1]宽,scale[0]高,x[0]x坐标,x[1]y坐标  
  12.             *off++ = sub2idx((scale[0]-1)*x[1],(scale[1]-1)*x[0],iHEIGHT);//记录第一个点在该尺度BOX的具体位置,并转化为索引  
  13.             *off++ = sub2idx((scale[0]-1)*x[3],(scale[1]-1)*x[2],iHEIGHT);//记录第二个点在该尺度BOX的具体位置,并转化为索引  
  14.         }  
  15.     }  

  1. }  

今天继续,下面是开始要生成正负例来训练分类器了,首先:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // TRAIN DETECTOR ==========================================================  
  2.   
  3. // Initialize structures  
  4. tld.imgsize = size(tld.source.im0.input);  
  5. //为fern准备的训练集  
  6. tld.X       = cell(1,length(tld.source.idx)); //training data for fern  
  7. tld.Y       = cell(1,length(tld.source.idx));   
  8. %为nearest neighbor准备的训练集  
  9. tld.pEx     = cell(1,length(tld.source.idx)); // training data for NN  
  10. tld.nEx     = cell(1,length(tld.source.idx));  
  11. //输入:  
  12. //tld.source.bb:用户目标标定框  
  13. //tld.grid: 生成的gridbox信息矩阵  
  14. //输出:  
  15. // overlap一维行向量,记录GRID中的各个gridbox与用户目标标定框的重叠率  
  16. overlap     = bb_overlap(tld.source.bb,tld.grid);   
进入bb_overlap来看一下:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Input  
  2. double *bb1 = mxGetPr(prhs[0]); int M1 = mxGetM(prhs[0]); int N1 = mxGetN(prhs[0]);//4X1  
  3. double *bb2 = mxGetPr(prhs[1]); int M2 = mxGetM(prhs[1]); int N2 = mxGetN(prhs[1]);//6Xn(n表示gridbox总数)  
  4.   
  5. // Output  
  6.            
  7.          if (N1 == 0 || N2 == 0) {  
  8.              N1 = 0; N2 = 0;  
  9.          }  
  10. plhs[0] = mxCreateDoubleMatrix(N1, N2, mxREAL);//创建输出矩阵,1Xgridbox的数量  
  11. double *out = mxGetPr(plhs[0]);  
  12.   
  13. for (int j = 0; j < N2; j++) {//gridbox的数量  
  14.     for (int i = 0; i < N1; i++) {//1  
  15.         *out++ = bb_overlap(bb1 + M1*i, bb2 + M2*j);//计算重叠度  
  16.     }  
  17. }  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. double bb_overlap(double *bb1, double *bb2) {  
  2.   
  3.     if (bb1[0] > bb2[2]) { return 0.0; }//判断如果两个矩形没有相交部分,重叠度就为0;  
  4.     if (bb1[1] > bb2[3]) { return 0.0; }  
  5.     if (bb1[2] < bb2[0]) { return 0.0; }  
  6.     if (bb1[3] < bb2[1]) { return 0.0; }  
  7.       
  8.     double colInt =  min(bb1[2], bb2[2]) - max(bb1[0], bb2[0]) + 1;//求相交矩形的宽和高  
  9.     double rowInt =  min(bb1[3], bb2[3]) - max(bb1[1], bb2[1]) + 1;  
  10.   
  11.     double intersection = colInt * rowInt;//相交面积  
  12.     double area1 = (bb1[2]-bb1[0]+1)*(bb1[3]-bb1[1]+1);//分别求两个输入矩形的面积  
  13.     double area2 = (bb2[2]-bb2[0]+1)*(bb2[3]-bb2[1]+1);  
  14.     return intersection / (area1 + area2 - intersection);//求重叠率  
  15. }  
再接着

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //输入:  
  2. //tld.img{1}.input:输入图像,第一帧  
  3. //tld.bb(:,1):用户目标标定框  
  4. //输出:  
  5. //tld.target:目标标定框中特定的图像  
  6. tld.target = img_patch(tld.img{1}.input,tld.bb(:,1));  
进入img_patch,这个函数比较庞大,先看其中用到的一部分:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //如果4个坐标值都是整数  
  2. if sum(abs(round(bb)-bb))==0  
  3.     L = max([1 bb(1)]);  
  4.     T = max([1 bb(2)]);  
  5.     R = min([size(img,2) bb(3)]);  
  6.     B = min([size(img,1) bb(4)]);  
  7.     patch = img(T:B,L:R);//在不超过画面尺寸和小于1x1的情况下,取出BB框出的画面  
  8.       
  9.     % Sub-pixel accuracy  
  10. else  
  11.       
  12.     cp = 0.5 * [bb(1)+bb(3); bb(2)+bb(4)]-1;//bbox的中心坐标 center point  
  13.     %[1 0 -cp(1)]  
  14.     %[0 1 -cp(2)]  
  15.     %[0 0 1     ]  
  16.     H = [1 0 -cp(1); 0 1 -cp(2); 0 0 1];  
  17.       
  18.     bbW = bb(3,:)-bb(1,:);//宽  
  19.     bbH = bb(4,:)-bb(2,:);//高  
  20.     if bbW <= 0 || bbH <= 0  
  21.         patch = [];  
  22.         return;  
  23.     end  
  24.     box = [-bbW/2 bbW/2 -bbH/2 bbH/2];  
  25.       
  26.     if size(img,3) == 3//如果图像有三个通道,即判断图片是否为真彩色  
  27.         for i = 1:3  
  28.             P = warp(img(:,:,i),inv(H),box);  
  29.             patch(:,:,i) = uint8(P);  
  30.         end  
  31.     else  
  32.         patch = warp(img,inv(H),box);//inv(H)=[1 0 cp(1); 0 1 cp(2); 0 0 1];平移变换  
  33.         patch = uint8(patch);  
  34.     end  
  35.   
  36. end  
上面的函数功能就是对BB区域的图像提取,但是有针对坐标为整数和小数的处理,这里应该只用到整数部分,但至于小数坐标的处理跟踪了一下代码,发现是对图像作了平移的仿射变换,但是至于为什么要这么做,我也不理解,感觉直接舍去小数部分问题应该也不大吧(个人理解,没有看懂)。

好了下面开始产生正训练样本了:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //输入:  
  2. //overlap:一维行向量,记录GRID中的各个gridbox与用户目标标定框的重叠率  
  3. //tld.p_par_init:opt.p_par_init= struct('num_closest',10,'num_warps',20,'noise',5,'angle',20,'shift',0.02,'scale',0.02);  
  4. //输出:  
  5. //pX:10 X length(idxP)*20 (length(idxP)<=10,20为'num_warps',20)的矩阵列向量表示一个gridbox的10棵树上的13位有效的code  
  6. //pEx:225X1的列向量,各元素值为原像素值减去像素均值  
  7. //bbP:最靠近BBOX的的gridbox,列向量表示该box的4个顶点  
  8. [pX,pEx,bbP] = tldGeneratePositiveData(tld,overlap,tld.img{1},tld.p_par_init);  
  9. pY = ones(1,size(pX,2));%1 X length(idxP)*20  
这个函数也是比较大的,但是还要耐心的往下看啊

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. pX   = [];  
  2. pEx  = [];  
  3.   
  4. // Get closest bbox  
  5. [~,idxP] = max(overlap);//表示行不管,只取列,整个表达式表示最大overlap<span style="font-family: Arial, Helvetica, sans-serif;">所对应的列,一维</span>  
  6. bbP0 =  tld.grid(1:4,idxP);//1~4表示矩阵的4个顶点分布在四行,此取最靠近BBOX的的gridbox  
  7.   
  8. // Get overlapping bboxes  
  9. idxP = find(overlap > 0.6);//返回overlap > 0.6所对应的列索引  
  10. if length(idxP) > p_par.num_closest//如果overlap > 0.6的gridbox数大于10  
  11.     [~,sIdx] = sort(overlap(idxP),'descend');    //降序排序   
  12.     idxP = idxP(sIdx(1:p_par.num_closest));//取前p_par.num_closest个最大重叠度的bboxes所在的列  
  13. end  
  14. bbP  = tld.grid(:,idxP);//取出10个最大重叠度的gridboxes  
  15. if isempty(bbP), return; end  
  16.   
  17. % Get hull  
  18. bbH  = bb_hull(bbP);%得到能包围所有bbp中boxes<span style="font-family: Arial, Helvetica, sans-serif;">的最小矩形</span>  
  19. cols = bbH(1):bbH(3);  
  20. rows = bbH(2):bbH(4);  
  21.   
  22. im1 = im0;  
  23. //返回一个225x1(pEx)的列向量,各元素值为原像素值减去像素均值  
  24. pEx = tldGetPattern(im1,bbP0,tld.model.patchsize);//  
  25. if tld.model.fliplr  
  26. pEx = [pEx tldGetPattern(im1,bbP0,tld.model.patchsize,1)];  
  27. end  
  28. //返回20个正例  
  29. for i = 1:p_par.num_warps//p_par.num_warps=20  
  30.     if i > 1  
  31.         randomize = rand; // Sets the internal randomizer to the same state  
  32.         //patch_input = img_patch(im0.input,bbH,randomize,p_par);  
  33.         //返回将画面进行仿射变换后的patch  
  34.         patch_blur = img_patch(im0.blur,bbH,randomize,p_par);//bbH包围所有bbp中bboxes的最小矩形  
  35.         //这个很重要,保证在C调用里的偏移的起始地址可以是一样的  
  36.         im1.blur(rows,cols) = patch_blur;//把仿射变换后的图像放到原图像对应的位置(<span style="font-family: Arial, Helvetica, sans-serif;">能包围所有bbp中boxes</span><span style="font-family: Arial, Helvetica, sans-serif;">的最小矩形</span>)  
  37.         //im1.input(rows,cols) = patch_input;  
  38.     end  
  39.       
  40.     // Measures on blured image  
  41.     //单次返回10Xlength(idxP)的矩阵,列向量表示一个gridbox的10棵树上的13位code,  
  42.     //最后返回10Xlength(idxP)*20的矩阵  
  43.     pX  = [pX fern(5,im1,idxP,0)];//idxP :overlap > 0.6所对应的列索引  
  44.       
  45.     // Measures on input image  
  46.     //pEx(:,i) = tldGetPattern(im1,bbP0,tld.model.patchsize);  
  47.     //pEx = [pEx tldGetPattern(im1,tld.grid(1:4,idxP),tld.model.patchsize)];  
  48.       
  49. end  
当然这个函数是不能这么草草了事的,还有三大函数需要进一步细看:

1.tldGetPattern()

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. nBB = size(bb,2);//得到bbp0(最靠近BBOX的gridbox)的列,值为1  
  2. pattern = zeros(prod(patchsize),nBB);//15*15 X 1 矩阵,返回矩阵  
  3. if ~exist('flip','var')  
  4.     flip= 0;  
  5. end  
  6.   
  7. // for every bounding box  
  8. for i = 1:nBB//1  
  9.       
  10.     // sample patch  
  11.     patch = img_patch(img.input,bb(:,i));//取出对应框中的图像  
  12.       
  13.     // flip if needed  
  14.     if flip  
  15.         patch = fliplr(patch);  
  16.     end  
  17.       
  18.     // normalize size to 'patchsize' and nomalize intensities to ZMUV  
  19.     //返回一个225x1的列向量,各元素值为原像素值减去像素均值  
  20.     pattern(:,i) = tldPatch2Pattern(patch,patchsize);//patch压缩变换到patchsize大小,然后将各个元素减去元素均值  
  21. end  
切入到tldPatch2Pattern看一眼:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. patch   = imresize(patch,patchsize); // 'bilinear' is faster  
  2. pattern = double(patch(:));//此时变成225X1的矩阵  
  3. pattern = pattern - mean(pattern);//mean(pattern)求各列向量的均值  
2.img_patch()(4个传参)

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. rand('state',randomize);  
  2. randn('state',randomize);  
  3. //'noise',5,'angle',20,'shift',0.02,'scale',0.02;  
  4. NOISE = p_par.noise;  
  5. ANGLE = p_par.angle;  
  6. SCALE = p_par.scale;  
  7. SHIFT = p_par.shift;  
  8.   
  9. cp  = bb_center(bb)-1;//HULL矩形的中心  
  10. Sh1 = [1 0 -cp(1); 0 1 -cp(2); 0 0 1];  
  11.   
  12. sca = 1-SCALE*(rand-0.5);%0.99~1.01  
  13. //[0.99~1.01                ]  
  14. //[          0.99~1.01      ]  
  15. //[                      1  ]  
  16. Sca = diag([sca sca 1]);  
  17.   
  18. ang = 2*pi/360*ANGLE*(rand-0.5);//-10 ~ 10度 实际为弧度  
  19. ca = cos(ang);  
  20. sa = sin(ang);  
  21. Ang = [ca, -sa; sa, ca];  
  22. Ang(end+1,end+1) = 1;  
  23.   
  24. shR  = SHIFT*bb_height(bb)*(rand-0.5);//-0.01~1.01*bb_height(bb)  
  25. shC  = SHIFT*bb_width(bb)*(rand-0.5);//-0.01~1.01*bb_width(bb)  
  26. Sh2 = [1 0 shC; 0 1 shR; 0 0 1];  
  27.   
  28. bbW = bb_width(bb)-1;  
  29. bbH = bb_height(bb)-1;  
  30. box = [-bbW/2 bbW/2 -bbH/2 bbH/2];  
  31.   
  32. H     = Sh2*Ang*Sca*Sh1;  
  33. bbsize = bb_size(bb);  
  34. patch = uint8(warp(img,inv(H),box) + NOISE*randn(bbsize(1),bbsize(2)));//给图像造成5的高斯噪声  
以上的代码注释就少了,因为全都是关于仿射变换的,具体可以参看仿射变换,大体就是作者在论文中提到的(shift+-1%,scale +-1%, in-plane rotation +-10度)用来提高训练样本的多样性。

3.fern()(第一个传参为5,获得模式)

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. unsigned char *input = (unsigned char*) mxGetPr(mxGetField(prhs[1],0,"input"));  
  2. unsigned char *blur  = (unsigned char*) mxGetPr(mxGetField(prhs[1],0,"blur"));//获得仿射变换后的patch  
  3.   
  4. //if (mxGetM(prhs[1])!=iHEIGHT) { mexPrintf("fern: wrong input image.\n"); return; }  
  5.   
  6. // bbox indexes  
  7. double *idx = mxGetPr(prhs[2]);//bbp所对应的列索引  
  8. int numIdx = mxGetM(prhs[2]) * mxGetN(prhs[2]);//1 X (<=10)  
  9.   
  10. // minimal variance  
  11. double minVar = *mxGetPr(prhs[3]);//minVar=0  
  12. if (minVar > 0) {  
  13.     iimg(input,IIMG,iHEIGHT,iWIDTH);//返回IIMG,是图像进行矩形积分后的结果(运行不到这)  
  14.     iimg2(input,IIMG2,iHEIGHT,iWIDTH);//返回IIMG,是图像进行矩形平方积分后的结果(运行不到这)  
  15. }  
  16.   
  17. // output patterns  
  18.       //创建输出矩阵:10X(<=10)  
  19. plhs[0] = mxCreateDoubleMatrix(nTREES,numIdx,mxREAL);  
  20. double *patt = mxGetPr(plhs[0]);  
  21.       //创建输出矩阵:1 X(<=10)  
  22. plhs[1] = mxCreateDoubleMatrix(1,numIdx,mxREAL);  
  23. double *status = mxGetPr(plhs[1]);  
  24.   
  25. for (int j = 0; j < numIdx; j++) {//(<=10)  
  26.   
  27.     if (minVar > 0) {  
  28.         double bboxvar = bbox_var_offset(IIMG,IIMG2,BBOX+j*BBOX_STEP);//BBOX保存网格数据索引等数据(运行不到这)  
  29.               //E(p^2)-E^2(p)  
  30.         if (bboxvar < minVar) {  continue; }(运行不到这)  
  31.     }  
  32.     status[j] = 1;  
  33.     double *tPatt = patt + j*nTREES;  
  34.     for (int i = 0; i < nTREES; i++) {//10  
  35.               //返回对应gridbox及对应树的13位有效的像素比较码  
  36.         tPatt[i] = (double) measure_tree_offset(blur, idx[j]-1, i);//idx:bbp  
  37.     }  
  38. }  
  39. return;  
进入measure_tree_offset

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int index = 0;  
  2. int *bbox = BBOX + idx_bbox*BBOX_STEP;//BBOX存储gridbox的索引等信息BBOX_STEP=7(因为grid的行为6)  
  3.    //OFF + bbox[5],该表达式表示该gridbox的特征点信息在OFF的偏移,bbox[5]表示图像横向上多少个网格点  
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.    //OFF = create_offsets(s,x);//记录各个特征点在各种尺度下box中的具体位置  
  2. int *off = OFF + bbox[5] + idx_tree*2*nFEAT;//OFF存储特征点在各个尺度框下的分布位置等  
  3. for (int i=0; i<nFEAT; i++) {//13  
  4.     index<<=1;   
  5.        //off[0]为特征点的x坐标,off[1]为特征点的y坐标,bbox[0]为该gridbox在图画中的位置  
  6.     int fp0 = img[off[0]+bbox[0]];  
  7.     int fp1 = img[off[1]+bbox[0]];  
  8.     if (fp0>fp1) { index |= 1;}//两个像素点比较并置位相应CODE  
  9.     off += 2;//移到下一个点对  
  10. }  
  11. return index;     
看完上面,真的有点累啊,算了,把负例也看下好了,简单看了下,代码不算太多:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Correct initial bbox  
  2. tld.bb(:,1) = bbP(1:4,:);//最靠近BBOX的的gridbox  
  3.   
  4. // Variance threshold  
  5. tld.var = var(pEx(:,1)) / 2;//var计算方差,这里即求各个数平方和的平均数  
  6. // disp(['Variance : ' num2str(tld.var)]);  
  7.   
  8. // Generate Negative Examples  
  9. //nx:patch variance 挑出合适的patches,并提取fern特征赋给nx,  
  10. //nEx返回一个225x100(nEx)的矩阵,列向量各元素值为原像素值减去像素均值,100为num_patches  
  11. //输入:  
  12. //overlap:一维行向量,记录GRID中的各个gridbox与用户目标标定框的重叠率  
  13. //输出:  
  14. //nx:patch variance 挑出合适的patches,并提取fern特征赋给nx  
  15. //nEx:一个225x100(nEx)的矩阵,列向量各元素值为原像素值减去像素均值,100为num_patches  
  16. [nX,nEx] = tldGenerateNegativeData(tld,overlap,tld.img{1});  
再进

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Measure patterns on all bboxes that are far from initial bbox  
  2. //opt.n_par = struct('overlap',0.2,'num_patches',100);  
  3. idxN        = find(overlap<tld.n_par.overlap);//overlap < 0.2  
  4. [nX,status] = fern(5,img,idxN,tld.var/2);//此函数通过patch variance剔除一批,剩下的进入fern特征码提取  
  5. idxN        = idxN(status==1); // bboxes far and with big variance,注意C++代码中的status[j] = 1;一句  
  6. nX          = nX(:,status==1);//选出进入第二级分类器的负样本  
  7.   
  8. // Randomly select 'num_patches' bboxes and measure patches  
  9. idx = randvalues(1:length(idxN),tld.n_par.num_patches);//'num_patches',100应该是随机取出100个gridbox  
  10. bb  = tld.grid(:,idxN(idx));  
  11. nEx = tldGetPattern(img,bb,tld.model.patchsize);//不复注解  
再进入fern(5,...)因为有tld.var/2,执行稍有不同,请参见上面就行。

好了,至此已经为分类器的训练产生了可用的正例和负例了。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值