TLD matlab 源代码阅读(1)

最近在看视觉跟踪方面的论文,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

TLD matlab 源代码阅读(1)的相关文章

TLD matlab源代码阅读(2)

今天继续,下面是开始要生成正负例来训练分类器了,首先: // TRAIN DETECTOR ========================================================== // Initialize structures tld.imgsize = size(tld.source.im0.input); //为fern准备的训练集 tld.X = cell(1,length(tld.source.idx)); //training data for fern

OpenJDK 源代码阅读之 Collections

概要 类继承关系 java.lang.Object java.util.Collections 定义 public class Collections extends Object 实现 sort public static <T extends Comparable<? super T>> void sort(List<T> list) { Object[] a = list.toArray(); Arrays.sort(a); ListIterator<T&g

Notepad++源代码阅读——窗口元素组织与布局

1.1 前言 这两天在看notepad++ 1.0版本的源代码.看了许久终于把程序的窗口之间的关系搞清楚了现在把其组织的要点写于此,希望对大家有所帮助. 1.2 窗口元素之间的关系 Notepad++主要有以下窗口元素(见下图). 其中Notepad_plus 是程序的主要窗口,其他:工具栏.状态栏.主次编辑窗口.主次选项卡窗口以及对话框窗口均为主窗口的子窗口.     _mainDocTab 和 _subDocTab 为 类:DocTabView 其成员_pView 分别指向 _mainEdi

Linux-0.11源代码阅读一 加载操作系统

x86系列CPU可以在16位实模式和32位保护模式下运行,实模式的特点是地址总线只有20位,也就是只有1MB的寻址空间,为了兼容老的CPU,Intel x86系列CPU包括最新的CPU在上电时都运行在16位的实模式下,同时在硬件上强行将CS置成0xF000,IP置成0xFFF0,那么CS:IP就指向0xFFFF0这个地址,也就是上电瞬间代码从该处开始执行,而BIOS恰恰就存储在这个地方,可以想象一下,如果连BIOS都没有将会是一个什么结果. BIOS程序被存储在计算机主板上的一块ROM芯片里,首

linux0.11 源代码阅读记录

*/--> pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;}

淘宝数据库OceanBase SQL编译器部分 源代码阅读--生成物理查询计划

SQL编译解析三部曲分为:构建语法树,制定逻辑计划,生成物理运行计划. 前两个步骤请參见我的博客<<淘宝数据库OceanBase SQL编译器部分 源代码阅读--解析SQL语法树>>和<<淘宝数据库OceanBase SQL编译器部分 源代码阅读--生成逻辑计划>>.这篇博客主要研究第三步,生成物理查询计划. 一. 什么是物理查询计划 与之前的阅读方法一致,这篇博客的两个主要问题是what 和how.那么什么是物理查询计划?物理查询计划可以直接运行并返回数据

Linux-0.11源代码阅读二 实模式到保护模式

bootsect部分已经执行完成,程序也跳转到setup部分: start: ! ok, the read went well so we get current cursor position and save it for ! posterity. mov ax,#INITSEG ! this is done in bootsect already, but... mov ds,ax mov ah,#0x03 ! read cursor pos xor bh,bh int 0x10 ! sa

【转】Tomcat总体结构(Tomcat源代码阅读系列之二)

本文是Tomcat源代码阅读系列的第二篇文章,我们在本系列的第一篇文章:在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码一文中介绍了如何在intelliJ IDEA 和 Eclipse中运行Tomcat源代码,本文介绍一下Tomcat的总体结构. 本文没有特别指明的地方,源代码都是针对tomcat7.0.42来说. Tomcat的总体结构 Tomcat即是一个Http服务器也是一个Servlet容器,它的总体结构我们可以用下图来描述: 通过上图我们可以看出Tomcat中

Hadoop源代码阅读环境搭建

Hadoop源代码阅读环境搭建 一.说明 作为一个学习hadoop的同学.必须在本机上搭建hadoop源代码阅读环境,这样,在方便阅读源代码的同一时候也方便进行调试和源代码改动. 好了.以下開始搭建好开发环境. 1.环境说明:hadoop 版本号:1.2.1. IDE:eclipse.操作系统:centos 2.网上有人是通过eclipse的新建项目指定文件夹的方式将hadoop文件夹转换成Eclipseproject同一时候导入eclipse,详细做法例如以下: File-->new-->J