目标跟踪–Learning Dynamic Siamese Network for Visual Object Tracking 代码阅读
该论文是2017年ICCV的会议论文,作者使用动态孪生网络进行目标跟踪,论文名《Learning Dynamic Siamese Network for Visual Object Tracking》。有关论文理论的详细介绍可以参考:https://blog.csdn.net/aiqiu_gogogo/article/details/79429071 博客内容。本篇主要记录对其代码阅读。
作者认为SiameseFC跟踪效果不好是因为缺少了在线更新过程,因此将目标变化和背景抑制认为是一种线性变化,变化系数求解采用KCF中提到的变换到傅立叶域内进行处理求解。个人认为只要有了KCF和SiameseFC的相关基础,本文还是较好理解的。
代码阅读
代码是matlab编写而成的,采用的深度学习框架是MatconvNet。入口韩式是demo_tracking.m文件。核心文件是run_DSiam.m文件。从代码可以看出作者是根据SiameseFC基础上修改而来,主要在里面增加了求解Target appearance variation V和Background suppression W过程。
网络初始化
demo_tracking.m文件主要完成相关路径设置等,完成相关配置后到run_DSiam.m文件进行网络初始化和跟踪过程。
在run_DSiam.m文件中第一个重要点是52行
[state,opts] = fcnet_init(img, state, netname, nettype);
fcnet_init函数完成网络孪生网络的初始化和相关参数设置等工作。孪生网络总用于目标跟踪的网络结构如图1所示。
跟踪阶段
跟踪阶段是run_DSiam.m中的57-91行,核心是fcn_update函数。下面主要分析fcn_update函数作用。
fcn_update函数首先加载相关参数和网络模型。然后代码
x_crops = make_scale_pyramid(img, pos, scaledInstance, instanceSize, avgChans,opts);
代码提取了三种尺度下的以目标为中心的图像patch块。然后使用孪生网络中用于跟踪的网络(网络结构如图1所示)进行一次前向传播,得到新目标位置等。到此完成跟踪当前帧,只需要不断迭代这一步骤即可。
下面主要记录一下论文中的创新点是如何体现在代码中的。在执行跟踪的前向传播阶段时,利用的是图1网络结构,其中18、19行作者自己创建了名为CirConv的层,该层主要配合接下来要讲的update_v和update_w计算论文中公式(即本文核心内容)
因为V和W求的是t-1帧的变化系数,所以从跟踪开始,在第三帧才进行计算第二帧的变化系数。因此在完成第二帧跟踪后,首先将exemplar的特征保存在变量corrfeat中,第二帧(t-1)帧的特征保存在tcorrfeat中,然后执行代码
net_conv = update_v(net_conv, corrfeat, tcorrfeat, opts)
其中参数net_conv网络结构如图1所示,corrfeat是图1网络提取到的feature map,tcorrfeat是exemplar的feature map。update_v函数核心如下所示。
function net = update_v(net,feats_1,feats_t,p)
[alphaf,featf] = update_change(feats_1{1}(:,:,:,1),feats_t{1},p.v_lambda);
net.params(net.getParamIndex('cir11_alphaf')).value = alphaf;
net.params(net.getParamIndex('cir11_featf')).value = featf;
net.layers(net.getLayerIndex('circonv1_1')).block.enable = true;
上述代码可以看出,update_v重点是update_change函数。其代码为
function [change_alpahf,change_featf] = update_change(corrfeat,new_corrfeat,lambda,issum)
if nargin<4
issum =false;
end
% leanring filter from corrfeat to new_corrfeat
cos_window = hann(size(corrfeat,1)) * hann(size(corrfeat,2))';
tcorrfeat = bsxfun(@times, corrfeat, cos_window);
corrfeatf = fft2(tcorrfeat);
numcorr = numel(corrfeatf(:,:,1));
if ~issum
kcorrfeatf = (corrfeatf .* conj(corrfeatf))./numcorr;
else
kcorrfeatf = sum(corrfeatf .* conj(corrfeatf),3)./numel(corrfeatf);
end
tnew_corrfeat = bsxfun(@times, new_corrfeat, cos_window);
tnew_corrfeatf = fft2(tnew_corrfeat);
alphaf = tnew_corrfeatf./ (kcorrfeatf+ lambda);
change_alpahf = alphaf;
change_featf = corrfeatf;
end
分析这段代码可以看出先对exemplar做fft变换并保存在变量corrfeatf中,然后将
F
∗
(
F
1
l
)
⨀
F
(
F
1
l
)
\mathcal{F^*(F^l_1)}\bigodot\mathcal{F(F^l_1)}
F∗(F1l)⨀F(F1l)结果存放在变量kcorrfeatf中,变量alpha中存放
F
(
F
t
−
1
l
)
F
∗
(
F
1
l
)
⨀
F
(
F
1
l
)
+
λ
v
\frac{\mathcal{F(F^l_{t-1})}}{\mathcal{F^*(F^l_1)}\bigodot\mathcal{F(F^l_1)} + \lambda_v}
F∗(F1l)⨀F(F1l)+λvF(Ft−1l)。这样执行完update_change函数后返回update_v中将alpha保存在cir11_alphaf(图1中18行的一个参数),corrfeatf存放入cir11_featf(图1中18行的一个参数)。
在跟踪第三帧,使用图1的网络结构进行前向传播,其中circonv1_1核心代码如下所示
for fi = 1:numsamp
feat = ofeat(:,:,:,fi);
trans_alphaf = params{1};
trans_featf = params{2};
trans_lr = params{3};
% add transformation to feat
cos_window = hann(size(feat,1)) * hann(size(feat,2))';
feat = bsxfun(@times, feat, cos_window);
featf = fft2(feat);
numcorr = numel(featf(:,:,1));
if ~obj.calsum
kfeatf = (featf .* conj(trans_featf))./numcorr;
t_feat = real(ifft2(trans_alphaf.* kfeatf));
assert(ndims(feat) == ndims(t_feat), 'feat and t_feat have different number of dimensions');
toutputs(:,:,:,fi)= (1-trans_lr).*ofeat(:,:,:,fi)+trans_lr.*t_feat;
else
kfeatf = sum(featf .* conj(trans_featf),3)./numel(featf);
t_feat = real(ifft2(trans_alphaf.* kfeatf));
toutputs(:,:,:,fi) = (1-trans_lr).*inputs{2}(:,:,:,fi)+trans_lr.*t_feat;
end
end
代码
kfeatf = (featf .* conj(trans_featf))./numcorr;
t_feat = real(ifft2(trans_alphaf.* kfeatf));
完成论文中公式(2)与刚刚update_v中为未计算完的部分。此时关于V的更新已经结束,接下来准备更新W,W更新与V更新调用同一函数,过程完全一致。
到此,论文中主要代码就到此结束,其他内容多是相关配置项设置等,与理解论文核心内容关系不大。