Abstract
问题:
传统的双流跟踪框架对目标提取的特征不够具体。
特征提取和关系建模是分开进行的,导致算法在区分目标和背景方面的能力有限。
两流、两阶段框架容易受到性能-速度困境的影响。
解决:
提出一种新的单流跟踪框架,OSTrack通过桥接具有双向信息流的模板搜索图像来统一特征学习和关系建模。
提出了一种基于单流框架中计算的强相似先验的网络内候选早期消除模块。
双流/单流
双流框架:
①Track-by-detection:检测流+关联流
②光流:光流流+特征流
③Anchor-free&re-ID:Anchor-free+re-ID
单流框架:单个模型同时检测和目标关联
本文创新点
①结合特征提取和关系建模,提出一种单流、单阶段跟踪框架。
②利用早期获得的目标与搜索区域各部分之间的相似性得分的先验性,提出了一种网络内早期候选消除模块,以减少推理时间。
Introduction
根据特征融合模块的计算量,通常采用两种不同的双流策略
双流两阶段轻关系跟踪框架:
举例:
孪生网络(Siamese networks)跟踪器
判别式(discriminative)跟踪器
过程:
①通过 CNN 主干网分别提取具有相同的结构和参数的模板和搜索区域特征
②采用单个算子,如互相关或判别相关滤波器融合特征用于状态估计
缺点:
效率高,但简单的线性运算导致判别信息丢失,效果差
无信息交互
双流两阶段重关系跟踪框架:
举例:
TransT:堆叠自注意力和交叉注意力层以进行迭代特征融合
STARK:将预先提取的模板和搜索区域特征连接输入自注意力层
缺点:
解决非线性交互造成的信息丢失问题(Transformer),但大量参数和使用迭代细化,效率低。
单流一阶段跟踪框架:
核心思想:
将模板和搜索区域连接,并将它们送到堆叠的自关注层中(Vision Transformer, ViT),生成的搜索区域特征直接用于目标分类和回归,而无需进一步匹配。
stacked self-attention layers:
堆叠多个自注意力层,每个自注意力层对输入序列处理,将输出传给下一个自注意力层。
模板会指导搜索区域的特征提取(让它更加关注目标),而搜索区域的信息也会反过来调整模板特征的表示。
优点:
在性能和速度之间取得了很好的平衡
单流框架提供了目标与搜索区域(即候选区域)的相似性的强先验
如图所示,该模型即使在早期阶段也可以识别背景区域,促使网络内早期候选淘汰模块放入提出。
Method
模型流程:
①输入图像对,包括目标初始框和搜索区域图像
# x:骨干网络的输出特征
# aux_dict:辅助字典,包含中间层特征或注意力权重
x, aux_dict = self.backbone(z=template, x=search,
ce_template_mask=ce_template_mask,
ce_keep_rate=ce_keep_rate,
return_last_attn=return_last_attn, )
# 跳转到 vit_ce.py---191 x Tensor:(4,320,768)
②将输入的图像送入ViT backbone预处理,切分图像块,将图像展平为一维向量
x = self.patch_embed(x)
z = self.patch_embed(z)
③线性投影到高维
def forward(self, x):
# allow different input size
# B, C, H, W = x.shape
# _assert(H == self.img_size[0], f"Input image height ({H}) doesn't match model ({self.img_size[0]}).")
# _assert(W == self.img_size[1], f"Input image width ({W}) doesn't match model ({self.img_size[1]}).")
x = self.proj(x) # Tensor:(4,768,16,16)
if self.flatten:
x = x.flatten(2).transpose(1, 2) # BCHW -> BNC # Tensor:(4,256,768)
x = self.norm(x) # Tensor:(4,256,768)
return x
④模型为每个包括图块特征和搜索区域的图块特征添加位置嵌入,特征包含图块嵌入和位置编码
⑤将图像嵌入连接并馈送到Transformer编码器层中,以进行联合特征提取和关系建模;模型可以包含多个编码器层,每个层都可能包含一个早期淘汰模块
# 将输入特征x通过线性变换得到QKV的组合表示,将其重新塑形和排列便于在注意力机制中使用
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
已淘汰的模块:
未淘汰的模块:经过多头注意力机制融合当前块和其他块的信息
⑥ 输入序列的长度需要被填充到一个固定的尺寸,使用令牌填充来确保所有序列具有相同长度。
占位符:是自动填充的,与淘汰的模块不是同一个
⑦经过编码器层处理后的特征被重塑(序列token拼回块状布局),通过一个头部网络来预测目标的位置。
def cal_bbox(self, score_map_ctr, size_map, offset_map, return_score=False):
# 获取最大得分和索引
max_score, idx = torch.max(score_map_ctr.flatten(1), dim=1, keepdim=True) # shape都是 Tensor:(4,1) 按 batch 拿出最大的得分和所对应的索引
# 计算索引坐标
idx_y = idx // self.feat_sz # Tensor:(4,1)
idx_x = idx % self.feat_sz # Tensor:(4,1)
# 扩展索引以匹配尺寸和偏移图
idx = idx.unsqueeze(1).expand(idx.shape[0], 2, 1) # Tensor:(4,2,1)
# 获取目标尺寸和偏移
size = size_map.flatten(2).gather(dim=2, index=idx) # Tensor:(4,2,1)
offset = offset_map.flatten(2).gather(dim=2, index=idx).squeeze(-1) # Tensor:(4,2)
# bbox = torch.cat([idx_x - size[:, 0] / 2, idx_y - size[:, 1] / 2,
# idx_x + size[:, 0] / 2, idx_y + size[:, 1] / 2], dim=1) / self.feat_sz
# cx, cy, w, h
# 计算边界框
bbox = torch.cat([(idx_x.to(torch.float) + offset[:, :1]) / self.feat_sz,
(idx_y.to(torch.float) + offset[:, 1:]) / self.feat_sz,
size.squeeze(-1)], dim=1) # Tensor:(4,4)
# 返回结果
if return_score:
return bbox, max_score
return bbox
Joint Feature Extraction and Relation Modeling
输入:
模板图像:
搜索区域:
展平:
模板图像:,为每个小块的分辨率,为小块数量
搜索区域:,
投影:
拼接:
同时用于模板和搜索区域的自注意力操作输出:
:模板区域内不同特征的相似性
:模板区域内和搜索区域内的相似性,与之对称
:搜索区域内部不同特征的相似性
与Transformer对比:
Transformer层仅用于融合提取的特征
OSTrack 在第一阶段直接连接线性投影模板和搜索区域图像,模板和搜索区域相互引导
OSTrack 受益于预训练的 ViT 模型
Early Candidate Elimination
将搜索区域的每个标记视为目标候选,将每个模板标记视为目标对象的一部分。
搜索区域的关注权值突出显示ViT早期(例如,第4层)的前景对象,然后逐步关注目标。
Candidate Elimination
模板token计算:
注意力权重 决定了模板部分与所有搜索区域令牌(候选者)之间的相似度。
代表性相似度:
,
将模板中心部分的权重与搜索区域token的相似度结合起来,作为代表性相似度
对每个头的分数取平均:
如果一个候选区域与目标的相似度相对较小,那么它更有可能是背景区域。因此,只保留k个最大(top-k)元素对应的候选元素(k是一个超参数,我们定义token保留比为),而剩余的候选元素被淘汰。
在编码器层进行多头注意操作后插入所提出的候选消除模块,如图所示。
此外,所有剩余候选人的原始顺序都被记录下来,以便在最后阶段恢复。
def candidate_elimination(attn: torch.Tensor, tokens: torch.Tensor, lens_t: int, keep_ratio: float, global_index: torch.Tensor, box_mask_z: torch.Tensor):
lens_s = attn.shape[-1] - lens_t
bs, hn, _, _ = attn.shape
# 计算保留的候选区域数量:
lens_keep = math.ceil(keep_ratio * lens_s)
if lens_keep == lens_s:
return tokens, global_index, None
# 从注意力张量中提取模板和搜索区域之间的注意力分数:
attn_t = attn[:, :, :lens_t, lens_t:]
if box_mask_z is not None:
box_mask_z = box_mask_z.unsqueeze(1).unsqueeze(-1).expand(-1, attn_t.shape[1], -1, attn_t.shape[-1])
# attn_t = attn_t[:, :, box_mask_z, :]
attn_t = attn_t[box_mask_z]
attn_t = attn_t.view(bs, hn, -1, lens_s)
attn_t = attn_t.mean(dim=2).mean(dim=1) # B, H, L-T, L_s --> B, L_s
else:
attn_t = attn_t.mean(dim=2).mean(dim=1) # B, H, L-T, L_s --> B, L_s
# 对注意力分数进行排序并获取索引:
sorted_attn, indices = torch.sort(attn_t, dim=1, descending=True)
# 选择顶部的 lens_keep 个候选:
topk_attn, topk_idx = sorted_attn[:, :lens_keep], indices[:, :lens_keep]
non_topk_attn, non_topk_idx = sorted_attn[:, lens_keep:], indices[:, lens_keep:]
# 收集保留和移除的索引:
keep_index = global_index.gather(dim=1, index=topk_idx)
removed_index = global_index.gather(dim=1, index=non_topk_idx)
tokens_t = tokens[:, :lens_t]
tokens_s = tokens[:, lens_t:]
B, L, C = tokens_s.shape
# 收集保留的token:
attentive_tokens = tokens_s.gather(dim=1, index=topk_idx.unsqueeze(-1).expand(B, -1, C))
tokens_new = torch.cat([tokens_t, attentive_tokens], dim=1)
return tokens_new, keep_index, removed_index
Candidate Restoration
候选项消除模块破坏了候选项的原始顺序,使得无法将候选项序列重塑回特征映射,因此先恢复剩余候选项的原始顺序,然后填充缺失的位置。
由于丢弃的候选对象属于无关背景区域,因此不会影响分类和回归任务。它们只是作为重塑操作的占位符,只需要首先恢复剩余候选项的顺序,然后在它们之间进行零填充。
Visualization
通过迭代丢弃搜索区域中不相关的标记,OSTrack不仅大大减轻了计算负担,而且避免了噪声背景区域对特征学习的负面影响。
Head and Loss
将填充的搜索区域令牌序列重新解释为二维空间特征图,然后将其馈送到全卷积网络(FCN)中。
使用局部偏移量以补偿因分辨率降低而引起的离散化误差,以及归一化的边界盒大小(即宽度和高度)。将分类得分最高的位置视为目标位置,即最终得到目标边界框
Experiments
网络结构:
OSTrack(
# 主干网络VIT CE代表使用了注意力机制
(backbone): VisionTransformerCE(
# 将输入图像分割成patch并映射到一个高维特征空间
(patch_embed): PatchEmbed(
(proj): Conv2d(3, 768, kernel_size=(16, 16), stride=(16, 16))
(norm): Identity()
)
(pos_drop): Dropout(p=0.0, inplace=False)
# ViT的核心部分block由多个相同的模块组成
(blocks): Sequential(
(0): CEBlock(
# 层归一化,用于稳定训练过程
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
# 注意力机制,用于计算不同特征之间的相关性
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
# 正则化,用于在训练过程中随机丢弃某些路径
(drop_path): Identity()
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
# 多层感知机,用于进一步处理特征
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(1): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.009)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(2): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.018)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(3): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.027)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(4): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.036)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(5): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.045)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(6): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.055)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(7): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.064)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(8): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.073)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(9): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.082)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(10): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.091)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
(11): CEBlock(
(norm1): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(qkv): Linear(in_features=768, out_features=2304, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=768, out_features=768, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
)
(drop_path): DropPath(drop_prob=0.100)
(norm2): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=768, out_features=3072, bias=True)
(act): GELU()
(drop1): Dropout(p=0.0, inplace=False)
(fc2): Linear(in_features=3072, out_features=768, bias=True)
(drop2): Dropout(p=0.0, inplace=False)
)
)
)
# 层归一化
(norm): LayerNorm((768,), eps=1e-06, elementwise_affine=True)
)
# 模型的头部,用于预测目标的边界框
# 用于预测中心点(center)
(box_head): CenterPredictor(
(conv1_ctr): Sequential(
(0): Conv2d(768, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
(conv2_ctr): Sequential(
(0): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
(conv3_ctr): Sequential(
(0): Conv2d(128, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
(conv4_ctr): Sequential(
(0): Conv2d(64, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
# 最后一个卷积层,输出一个特征图,用于预测中心点
(conv5_ctr): Conv2d(32, 1, kernel_size=(1, 1), stride=(1, 1))
# 用于预测偏移量(offset)
(conv1_offset): Sequential(
(0): Conv2d(768, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
(conv2_offset): Sequential(
(0): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
(conv3_offset): Sequential(
(0): Conv2d(128, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
(conv4_offset): Sequential(
(0): Conv2d(64, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
# 最后一个卷积层,输出两个通道,用于预测目标的偏移量
(conv5_offset): Conv2d(32, 2, kernel_size=(1, 1), stride=(1, 1))
# 用于预测目标大小(size)
(conv1_size): Sequential(
(0): Conv2d(768, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
(conv2_size): Sequential(
(0): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
(conv3_size): Sequential(
(0): Conv2d(128, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
(conv4_size): Sequential(
(0): Conv2d(64, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
)
# 最后一个卷积层,输出两个通道,用于预测目标的大小
(conv5_size): Conv2d(32, 2, kernel_size=(1, 1), stride=(1, 1))
)
)
头部是一个轻量级的FCN,由4个堆叠的convn - bn - relu层组成,用于三个输出中的每个输出。
每个候选消除模块的保持比设为0.7,在ViT的第4层、第7层、第10层共插入3个候选消除模块。
script_name: ostrack.py config_name: vitb_256_mae_ce_32x4_ep300.yaml
跟踪过程:
def track(self, image, info: dict = None):
H, W, _ = image.shape
self.frame_id += 1
# 以上一帧预测的位置 self.state 为中心裁剪当前帧的搜索区域
x_patch_arr, resize_factor, x_amask_arr = sample_target(image, self.state, self.params.search_factor,
output_sz=self.params.search_size) # (x1, y1, w, h)
# 预处理搜索区域
search = self.preprocessor.process(x_patch_arr, x_amask_arr)
with torch.no_grad():
x_dict = search
# 将模板特征和当前搜索区域特征输入到 Transformer 模型进行特征交互
out_dict = self.network.forward(
template=self.z_dict1.tensors, search=x_dict.tensors, ce_template_mask=self.box_mask_z)
# 分类分数图和边界框预测
pred_score_map = out_dict['score_map']
# 分数图和汉宁窗 (self.output_window) 相乘以平滑预测,防止目标位置偏移太大
response = self.output_window * pred_score_map
pred_boxes = self.network.box_head.cal_bbox(response, out_dict['size_map'], out_dict['offset_map'])
# 选择最终目标框
pred_boxes = pred_boxes.view(-1, 4)
# Baseline: Take the mean of all pred boxes as the final result
pred_box = (pred_boxes.mean(
dim=0) * self.params.search_size / resize_factor).tolist() # (cx, cy, w, h) [0,1]
# get the final box result
self.state = clip_box(self.map_box_back(pred_box, resize_factor), H, W, margin=10)
采用汉宁窗惩罚,按照惯例利用位置先验进行跟踪。具体来说,只需将分类图P与大小相同的汉宁窗口相乘,相乘后得分最高的方框将被选中作为跟踪结果。
Comparison with State-of-the-Arts
GOT-10k.单流跟踪框架可以通过相互引导提取更多未见类的判别特征。
早期候选消除模块对LaSOT, GOT-10k和TrackingNet基准上的推理速度,mac和跟踪性能的影响,w/o和w/分别表示有或没有早期候选消除模块的模型。
损失函数:
Ablation Study and Analysis
早期候选人淘汰模块的影响:
早期候选淘汰模块可以显著减少计算量,提高推理速度,大多数情况下性能略有提升,该模块缓解了噪声背景区域对特征学习的负面影响。
不同的预训练方法:
由于测试集中的对象类别与训练集中的对象类别完全不同,双流框架提取的特征的判别能力是有限的。而通过模板特征与搜索区域的迭代交互,OSTrack可以通过相互引导提取更多的判别特征。
OSTrack在保持联合特征提取和关联建模模块的高并行性的同时,忽略了多余的繁重的关系建模模块。因此,当采用同一骨干网时,所提出的单流框架比STARK(快40.2 FPS)和SwinTrack(快25.6 FPS)要快得多。
判别区域可视化:
OSTrack可以提取有区别的面向目标的特征,因为所提出的早期融合机制使模板和搜索区域之间的关系在第一阶段建模。
标签:768,Tracking,OSTrack,features,Stream,inplace,False,True,out From: https://blog.csdn.net/m0_61595251/article/details/143838238