最近在看视觉跟踪方面的论文,ZK博士的TLD算法作为跟踪算法的state-of-the-art,当然不得不去拜读下了,看完论文后虽然对作者整体的思想有了一个大致了解,但是对于很多细节却也还是无从得知,好在作者将自己的算法源代码全部开源,这也造福了我们这些无知者的胃口,虽然网上有几个c++版本的源码,但是matlab版本作为作者的原始版本,拿来作研究也是极好的。
通览源代码,个人感觉精髓之处无非两个函数,即:
tldInit();
tldProcessFrame();
这二者一个是用来跟踪前的初始化工作,后者当然是run-time的跟踪工作。我个人的阅读注释也将从这两个函数展开。好了,闲话少叙,下面来看看tldInit()这个函数:
进去第一个函数是
%输入: %tld.source.bb 用户输入的目标标定框 %size(tld.source.im0.input) 输入图像的尺寸 %tld.model.min_win 目标标定框的长或宽的最小尺寸 %输出: %tld.grid 是一个6Xn(n表示不同有效尺度下共有多少个的网格)的矩阵,前四行组成列向量表示gridbox的4个顶点,5表示索引,6表示横向的分布点数 % tld.scales 有效尺度下gridbox的高和宽,为2Xm矩阵(m表示有效的尺度的个数) [tld.grid tld.scales] = bb_scan(tld.source.bb,size(tld.source.im0.input),tld.model.min_win);
再让我们继续进入bb_scan()函数:
bbW = round(bb_width(bb) .* SCALE);%bb_width(bb)*(0.1615~6.1917)矩形框变尺度 bbH = round(bb_height(bb) .* SCALE); bbSHH = SHIFT * min(bbH,bbH);%取bbH和bbH中最小的元素组成新的矩阵,维数和原来相同 SHIFT =0.1表示变形后的矩形框的移动步长 bbSHW = SHIFT * min(bbH,bbW); bbF = [2 2 imsize(2) imsize(1)]‘;%[2 2 320 240] bbs = {}; sca = []; idx = 1; for i = 1:length(SCALE) if bbW(i) < MINBB || bbH(i) < MINBB, continue; end left = round(bbF(1):bbSHW(i):bbF(3)-bbW(i)-1);%2开头,0.1* min(bbH(i),bbW(i))为步长,320- bbW(i)-1为结尾 top = round(bbF(2):bbSHH(i):bbF(4)-bbH(i)-1);%2开头,0.1* min(bbH(i),bbH(i))为步长,240- bbH(i)-1为结尾 grid = ntuples(top,left);%重复功能,维度为2 X length(top)*length(left);grid的每个列向量成为网格顶点的坐标点 if isempty(grid), continue; end bbs{end+1} = [grid(2,:); ...%省略号为续行,bbs前四行的列向量构成一个矩形网格的四个顶点 grid(1,:); ... grid(2,:)+bbW(i)-1; ... grid(1,:)+bbH(i)-1; ... idx*ones(1,size(grid,2));%坐标点对应的编号,从1开始 length(left)*ones(1,size(grid,2));];%记录横向分布多少个点? sca = [sca [bbH(i); bbW(i)]];%在原来SCA的基础上增加[bbH(i); bbW(i)]列向量,该列向量表示某个尺度因子下的gridbox的高和宽 idx = idx + 1; end bb_out = []; for i = 1:length(bbs) bb_out = [bb_out bbs{i}]; end
总之,该函数功能就是扫描输入图像得到很多的gridbox。
下面来到了产生特征的函数
%输入: %tld.model.num_trees:有10棵分类树 %tld.model.num_features:每棵树里有13个特性,这里是会有13个点对的比较 %输出: %tld.features: 4*13X10的矩阵,为fern分类器选取的随机点对,4表示一个点对的四个坐标值 tld.features = tldGenerateFeatures(tld.model.num_trees,tld.model.num_features,1);
还是进入一看究竟:
SHI = 1/5; SCA = 1; OFF = SHI; x = repmat(ntuples(0:SHI:1,0:SHI:1),2,1);%ntuples生成网格点坐标2X36矩阵,repmat表示将生成的矩阵作为初始化元素(还是矩阵) %生成一个2X1的大矩阵,这里是4X36(6*6)矩阵 x = [x x + SHI/2];%前者4X36矩阵,后者矩阵偏移0.1 k = size(x,2);%k = 36*2 %rand(1,k)随机生成都1Xk维随机分布矩阵,范围0~1 r = x; r(3,:) = r(3,:) + (SCA*rand(1,k)+OFF);%x的第3行加上随机的1X72矩阵(0~1)再加上0.2 l = x; l(3,:) = l(3,:) - (SCA*rand(1,k)+OFF);%x的第3行减去随机的1X72矩阵(0~1)再减去0.2 t = x; t(4,:) = t(4,:) - (SCA*rand(1,k)+OFF);%x的第4行减去随机的1X72矩阵(0~1)再减去0.2 b = x; b(4,:) = b(4,:) + (SCA*rand(1,k)+OFF);%x的第4行加上随机的1X72矩阵(0~1)再加上0.2 x = [r l t b]; idx = all(x([1 2],:) < 1 & x([1 2],:) > 0,1);%idx和X维数相同,如果满足条件相应元素为1,否则为0 x = x(:,idx);%挑选满足条件的所有X的列 x(x > 1) = 1;%x元素中大于1的则用1代替此元素 x(x < 0) = 0;%x元素中小于0的则用0代替此元素 numF = size(x,2);%看看现在X还有多少列 x = x(:,randperm(numF));%randperm(numF)从1~numF的数字序列随机打乱,整个表达式是指把X的列打乱 x = x(:,1:nFEAT*nTREES);%取x的前130列,x目前为4x130矩阵 x = reshape(x,4*nFEAT,nTREES);%把X塑造成4*13 X 10 的矩阵
整个函数就是生成10*13个特征点对,坐标用小数表示,表示在box的相对位置,至于为什么随机我也感觉挺困惑的,求解释啊!!
下面再来到fern(0);没什么好说的,来看下面的吧:
%输入: %tld.source.im0.input:输入的图像 %tld.grid:6Xn(n表示不同有效尺度下共有多少个的网格)的矩阵,gridbox信息 %tld.features:4*13X10的矩阵,为fern分类器选取的随机点对,4表示一个点对的四个坐标值 %tld.scales: 有效尺度下gridbox的高和宽,为2Xm矩阵(m表示有效的尺度的个数) %输出: fern(1,tld.source.im0.input,tld.grid,tld.features,tld.scales); % allocate structures
进入CPP的fern
iHEIGHT = mxGetM(prhs[1]);//240 iWIDTH = mxGetN(prhs[1]);//320 nTREES = mxGetN(mxGetField(prhs[3],0,"x"));//10 nFEAT = mxGetM(mxGetField(prhs[3],0,"x")) / 4; // feature has 2 points: x1,y1,x2,y2 13 thrN = 0.5 * nTREES;//等于5 nSCALE = mxGetN(prhs[4]);//获得共有多少种尺度的BOX,即有效尺度的个数 IIMG = (double*) malloc(iHEIGHT*iWIDTH*sizeof(double));//积分图像变量准备 IIMG2 = (double*) malloc(iHEIGHT*iWIDTH*sizeof(double));//平方积分图像变量准备 // BBOX mBBOX = mxGetM(prhs[2]);//等于6 nBBOX = mxGetN(prhs[2]);//等于在各个有效尺度下有多少个网格 BBOX = create_offsets_bbox(mxGetPr(prhs[2]));//创建保存网格数据索引等数据 //prhs[3]是4*13X10的矩阵 //matlab 代码中有f.x = x;f.type = ‘forest‘;见到下面就不怪了 double *x = mxGetPr(mxGetField(prhs[3],0,"x"));//获得特征点的指针 double *s = mxGetPr(prhs[4]);//各种尺度的BOX的尺寸 OFF = create_offsets(s,x);//记录各个特征点对在各种尺度下box中的具体位置 for (int i = 0; i<nTREES; i++) { WEIGHT.push_back(vector<double>(pow(2.0,nBIT*nFEAT), 0));//nBIT=1,权重分配权2^13 nP.push_back(vector<int>(pow(2.0,nBIT*nFEAT), 0));//nBIT=1,nP分配2^13 nN.push_back(vector<int>(pow(2.0,nBIT*nFEAT), 0));//nBIT=1,nN分配2^13 } //static vector<vector <double> > WEIGHT; //static vector<vector <int> > nP; //static vector<vector <int> > nN;下面见怪不怪,10X 2^13的容器 for (int i = 0; i<nTREES; i++) { for (int j = 0; j < WEIGHT[i].size(); j++) { WEIGHT[i].at(j) = 0; nP[i].at(j) = 0; nN[i].at(j) = 0; } }
来具体看看create_offset_bbox和creater_offset两个函数
int *offsets = (int*) malloc(BBOX_STEP*nBBOX*sizeof(int));//7*nBBOX*sizeof(int) int *off = offsets; for (int i = 0; i < nBBOX; i++) {//nBBOX表示所有的网格数 double *bb = bb0+mBBOX*i;//偏移到下一个网格的属性的向量 //sub2idx(row,col,height) ((int) (floor((row)+0.5) + floor((col)+0.5)*(height))) //floor不大于 //bb[0]:left bb[1]top bb[2]right bb[3]bottom iHEIGHT:240 bb[4]索引从1开始 bb[5]表示number of left-right bboxes //以下记录索引是从左到右,从上到下方式 *off++ = sub2idx(bb[1]-1,bb[0]-1,iHEIGHT);//左上顶点索引 *off++ = sub2idx(bb[3]-1,bb[0]-1,iHEIGHT);//左下顶点索引 *off++ = sub2idx(bb[1]-1,bb[2]-1,iHEIGHT);//右上顶点索引 *off++ = sub2idx(bb[3]-1,bb[2]-1,iHEIGHT);//右下顶点索引 *off++ = (int) ((bb[2]-bb[0])*(bb[3]-bb[1]));//记录当前网格的大小 *off++ = (int) (bb[4]-1)*2*nFEAT*nTREES; // pointer to features for this scale *off++ = bb[5]; // number of left-right bboxes, will be used for searching neighbours } return offsets;
int *offsets = (int*) malloc(nSCALE*nTREES*nFEAT*2*sizeof(int));// int *off = offsets; for (int k = 0; k < nSCALE; k++){//共有多少种尺度的BOX double *scale = scale0+2*k;//scale0第一种尺度的尺寸信息,*2表示列向量有2维,即高和宽,此表达式表示偏移到下一个box的尺寸向量 for (int i = 0; i < nTREES; i++) {//10 for (int j = 0; j < nFEAT; j++) {//13 //x0 4*13 X 10 的矩阵 double *x = x0 +4*j + (4*nFEAT)*i;//4*j因为每个feature是一个4维列向量,(4*13)*i即下一棵树 //sub2idx(row,col,height) ((int) (floor((row)+0.5) + floor((col)+0.5)*(height))) //scale[1]宽,scale[0]高,x[0]x坐标,x[1]y坐标 *off++ = sub2idx((scale[0]-1)*x[1],(scale[1]-1)*x[0],iHEIGHT);//记录第一个点在该尺度BOX的具体位置,并转化为索引 *off++ = sub2idx((scale[0]-1)*x[3],(scale[1]-1)*x[2],iHEIGHT);//记录第二个点在该尺度BOX的具体位置,并转化为索引 } } }
好了,至此特征点的选取和gridbox的初始化已经完成了。
TLD matlab 源代码阅读(1)
时间: 2024-10-24 08:47:59