TLD算法的运行过程中,关于图像的操作,很大部分都是在fern.cpp中完成的,包括分配内存,计算一些指标,还有随机森林。这部分我看了好几遍,因为里面的结构实在有点多,而且比较繁琐。下面记录下我的结果。
按照tld调用fern的顺序讲。
(一)在tldinit.m中首先调用,fern(0),清楚所有静态数据,
(二)然后是fern(1,tld.source.im0.input,tld.grid,tld.features,tld.scales),分配所需的内存空间和结构。
tld.source.im0.input,这是是第一幅图像。
tld.grid,这是之前建立的关于窗格的矩阵。
是6*N的矩阵,每一列分别代表一个窗格,6个数分别表示坐上角x,y坐标,右下角x,y坐标,第几个尺度(从1开始),该尺度下一行的窗格数,这样看比较清楚,每列一个窗格,至于列数不确定,视具体情况而定。
tld.features,这是之前建立的特征点位置,包括两个域,第一个是52*10的矩阵,里面都是由随机函数生成,经过处理的0到1之间的点,代表在某个窗格中的偏移。第二个是个字符forest,没啥大意义。
tld.scales,这是2*M的矩阵,每列表示一个scale下面,窗格的高和宽,即行数和列数。
下面说下这个函数具体分配了哪些空间。:
iHEIGHT = mxGetM(prhs[1]); //图片的大小
iWIDTH = mxGetN(prhs[1]);
nTREES = mxGetN(mxGetField(prhs[3],0,"x"));
nFEAT = mxGetM(mxGetField(prhs[3],0,"x")) / 4;//因为feature中是52*10,所以除以4刚好的13和论文里一致。因为feature有两个域,所以要加mxGetField。
thrN = 0.5 * nTREES; //阀值
nSCALE = mxGetN(prhs[4]); //尺度的数目
IIMG = (double*) malloc(iHEIGHT*iWIDTH*sizeof(double));
IIMG2 = (double*) malloc(iHEIGHT*iWIDTH*sizeof(double));//分配两个图像块内存
mBBOX = mxGetM(prhs[2]); //其实就是6,
nBBOX = mxGetN(prhs[2]);//这是总共的格子数目 包括各个尺度下的
BBOX = create_offsets_bbox(mxGetPr(prhs[2]));//这个是关键之一,将输入的窗格的坐标转换成内存地址,因为建立窗格时使用的是平面坐标,经过简单的换算就可以变成内存地址,BBOX是7*N的矩阵,前四个是左上,右下角的x,y坐标,第五个是该窗格的面积,第六个是指向该尺度下特征点矩阵首地址的,因为每个尺度下,大小不一样,所有特征点的偏移量不一样,所有尺度下的特征点矩阵依次往后排,所有每个尺度都要往后移,每次累加260个地址,第七个是该尺度下每行的窗格数目。这个是静态的,以后还会用到这个结构体。
double *x = mxGetPr(mxGetField(prhs[3],0,"x")); //这个指向特征点矩阵,就是那个52*10的
double *s = mxGetPr(prhs[4]); //指向尺度矩阵 2*M 每列就是该scale的高和宽
OFF = create_offsets(s,x); //将特征点转换成每个尺度下的内存地址。这个也是很关键的东西,因为那个feature就一个,而每个尺度下宽和高是不一样的,所有按照各自的比例将特征点矩阵换算成各自的地址,而且得到的是21*10*13*2的矩阵,21个尺度,10棵树,13个特征点,每个点2个坐标值,就是x,y值,对应前面的BBOX中第6个每次累加260,其实就是移动一个’特征块‘,到达该尺度对应的特征点矩阵。在说下这个21,这个OFF只是每个尺度下‘一个’窗格的特征点矩阵,而且是最左上角的那个,所有计算下一个窗格特征点位置时要记得往后移。
后面两个for循环初始化下面三个结构体,全部为0
static vector<vector <double> > WEIGHT;
static vector<vector <int> > nP;
static vector<vector <int> > nN;
(三)下面是fern(2,tld.X{1},tld.Y{1},tld.model.thr_fern,bootstrap);
这个是得到了正负样本值后,对fern中的随机森林进行具体的初始化
X{1}里面是正负样本,Y{1}指示对应的列是正样本还是负样本,thr_fern和bootstrap是设定的参数。
下面说下WEIGHT,nP,nN 这三个结构是啥,分别是10*2的13次方,10表示10棵树,2的13次方是因为,每棵树13位,所以每个值都要有,nP[i][idx],表示第i棵树特征是idx的点出现的次数,nN类似, WEIGHT[i][idx],表示第i棵树中,特征idx出现在正样本中的次数与出现总次数的比例。
fern(2)就是根据正负样本的特征值初始化以上三个结构体,将来detector工作的时候需要用到这三个东西。
(四)conf_fern = fern(3,nX2),nX2是负样本矩阵,conf_fern是1*M的矩阵,表示对应的负样本被当成正样本的比例,用于纠正设定的系统参数tld.model.thr_fern。
(五)fern(4,img,tld.control.maxbbox,tld.var,tld.tmp.conf,tld.tmp.patt),这是在tldDetection中出现的
img就是输入的图片,tld.control.maxbbox和tld.var是系统参数,tld.var出现在tldinit中,tld.var = var(pEx(:,1))/2;是初始目标区域像素方差的一半,和论文中一致,后两个是用来接收计算结果的,输入是空的。
这个其实就是tld的检测器,只不过有些东西前面分配好了而已。大致过程就是,滑动每个窗格,用
conf[I] = measure_bbox_offset(blur,I,minVar,tPatt)这个处理,I就是窗格的序号,minVar,是初始目标方差的一半用于级联分类器的第一级,比它小的,直接返回,否则继续,计算这窗格对应的局部2值特征,并且到WEIGHT中查找,该特征值,属于正样本的比例,conf就是10棵树总的比例之和。
tld.tmp.conf,是窗格属于正样本的比例,越高越可能是正样本,tld.tmp.patt,是每个窗格的patten值,即局部二值特征。
(六)fern(5,im1,idxP,0) 就是计算输入窗格的局部二值特征值。