MEGA(CVPR 2020)视频目标检测方法详解

前言

论文地址
代码地址

  本文仅根据 MEGA《Memory enhanced global-local aggregation for video object detection》的代码进行解读。MEGA 基于二阶段方法,主要想看如何把目标位置编码结合到特征融合中。
  通过对上一篇 YOLOV++ 和本文的 MEGA 进行调研,当前比较主流的 VID 方案都是通过视频中的其他帧对当前需要检测的帧以特征融合的方式优化检测。融合特征并非是对全图的特征图进行融合,而是每张图出一些候选目标(Anchor、Region Proposal),将他们的特征融合到当前帧的候选目标特征中,再通过 Head 进行预测。

推理流程概述

  本节我们暂时跳过各种计算的细节,先看看检测当前帧时需要融合哪些帧的信息。

  上方视频模拟了对一个总帧数为 60 帧的视频的检测流程,其中 Key 帧代表当前检测的帧。一共有三组图像会参与:
(1)Global:一共10帧,从视频所有帧中随机抽取的
(2)Local:一共25帧,Key 帧前后相邻的帧,且包含 Key 帧本身
(3)Memory:一共25帧,最初是空的,会在过去的检测过程中逐渐填充队列

Two-Stage 网络结构

  省略 Backbone 的结构,大致是用 ResNet 提取图像特征

RPN

在这里插入图片描述
  后续会先根据 obj 得分挑选出 Top6000,然后通过 NMS 得到 300 或 75 个 Proposals(对于 Key 帧为300个,其他用于融合的帧为75个)。

ROI

在这里插入图片描述

  这里附带了最后的两个全连接层检测头,单帧检测基本按此流程得到最终结果,而 MEGA 就是对 Proposals Features [300,1024] 进行特征融合后再用检测头得到结果。

特征融合流程

  整个融合的过程分为好几个阶段,但是融合方式都是利用 Attention。我们先跳过 Attention 中具体的计算方式,先看究竟是什么特征之间在融合,梳理整体融合的流程。

1. Global

  首先梳理手头的素材,仅看各个 Proposals 的特征

feat_key			 300,1024	
feat_local			1875,1024		1875 = 25 * 75
feat_local_dis		 675,1024		 675 = 25 * 15
									其实就是从原本的75个Proposals中选择前15,也就是obj得分更高的15个
feat_global			 750,1024		 750 = 10 * 75

  当前所有特征都仅来自单张图像,通过 Attention('global', idx=0)(idx 代表对权重的索引,不同阶段使用的权重不同,但相同阶段对不同特征之间融合使用的权重相同),将 feat_global 融合入前三组特征。

2. Local

  Local 阶段包含 3 个子阶段,并且涉及 Memory 较为复杂。

  为了方便说明,暂且忽略 Memory,每个子阶段的融合方式都是 Attention('local', idx=stage) ,将 feats_ref 融入 feats_cur,新得到的 feats_cur 决定了下一次需要融合的特征,具体如下:

stage1
feats_cur		 				 675,1024		key+local_dis
feats_ref						1875,1024		local

stage2
feats_cur = feats_cur		 	 675,1024		key+local_dis
feats_ref = feats_cur[300:]		 375,1024		local_dis

stage3
feats_cur = feats_cur[:300]		 300,1024		key
feats_ref = feats_cur[300:]		 375,1024		local_dis

  现在说明一下 Memory,Memory 构造是 3 个长度为 25 的队列,也就对应了 3 个 stage。每次融合特征前会先获取当前 Memory 中的特征参与融合,然后用当前阶段的 feats_ref 中的第一帧的特征来更新 Memory,即加入到队列(也就是对应 Local 队列中第一帧的特征,并且这个特征经过了各种融合)。获取的 Memory 特征会与 feats_ref 拼接到一起(torch.cat([feats_ref, memory["feats"]], dim=0)),融入 feats_cur

3. Global

  经过上述融合后得到了 feats_cur 作为当前帧的特征,于是通过 Attention('global', idx=1) 再次融入 Global 特征(idx 不同)。至此,接上前文中的检测头即可获得检测结果。

融合模块

QKV

在这里插入图片描述
  这里以 Global Attention 为例进行说明,把两组特征转化为 QKV。Position 代表的是位置编码,仅在 Local Attention 中使用,大致理解就是用一个 64 维向量来衡量这些 Proposal 在图中的位置关系。

Attention

在这里插入图片描述
  这里面红色的 Add 部分仅在 Local Attention 中使用,也就是 Global Attention 在 Permute 后直接连接 Softmax。
  与常规 Attention 不同之处主要在于 u,从代码来看并不能理解其用途,单纯是可训练的参数。

us = []
for i in range(self.stage):
	us.append(nn.Parameter(torch.Tensor(self.groups, 1, self.embed_dim)))
	for weight in [us[i]]:
	    torch.nn.init.normal_(weight, std=0.01)

Position

  下面是计算 Position 的总流程,输入为 Proposals 的坐标,首先计算各个坐标 xywh 之间的差异 position_matrix,然后将差异传化为 64 维的向量 position_embedding,最后对维度进行调整。

def cal_position_embedding(self, rois1, rois2):
    # [num_rois, num_nongt_rois, 4]
    position_matrix = self.extract_position_matrix(rois1, rois2)
    # [num_rois, num_nongt_rois, 64]
    position_embedding = self.extract_position_embedding(position_matrix, feat_dim=64)
    # [64, num_rois, num_nongt_rois]
    position_embedding = position_embedding.permute(2, 0, 1)
    # [1, 64, num_rois, num_nongt_rois]
    position_embedding = position_embedding.unsqueeze(0)
    return position_embedding

(1)matrix

d x = log ⁡ ∣ ( x 1 − x 2 ) / w 1 ∣ dx=\log{|(x_1-x_2) / w_1|} dx=log(x1x2)/w1
d y = log ⁡ ∣ ( y 1 − y 2 ) / h 1 ∣ dy=\log{|(y_1-y_2) / h_1|} dy=log(y1y2)/h1
d w = log ⁡ ( w 1 / w 2 ) dw=\log{(w_1/w_2)} dw=log(w1/w2)
d h = log ⁡ ( h 1 / h 2 ) dh=\log{(h_1/h_2)} dh=log(h1/h2)

delta_x = center_x - center_x_ref.transpose(0, 1)
delta_x = delta_x / bbox_width
delta_x = (delta_x.abs() + 1e-3).log()

delta_width = bbox_width / bbox_width_ref.transpose(0, 1)
delta_width = delta_width.log()

(2)embedding

def extract_position_embedding(position_mat, feat_dim, wave_length=1000.0):
	"""
	position_mat: [n1, n2, 4]
	feat_dim: 64
	"""
    device = position_mat.device
    feat_range = torch.arange(0, feat_dim / 8, device=device)
    dim_mat = torch.full((len(feat_range),), wave_length, device=device).pow(8.0 / feat_dim * feat_range)
    dim_mat = dim_mat.view(1, 1, 1, -1).expand(*position_mat.shape, -1)
    """
    dim_mat: [1.0000, 2.3714, 5.6234, 13.3352, 31.6228, 74.9894, 177.8279, 421.6965]
    		 1000 的 [0, 1/8, 2/8, ..., 7/8] 次方
    		 expand 至 [n1, n2, 4, 8] 大小
	后续将 xywh 都复制8份分别除以 dim_mat 中的值后计算 sin 和 cos
	于是 xywh 都有16个数值, 累计64个数值作为 Position 的特征向量
    """

    position_mat = position_mat.unsqueeze(3).expand(-1, -1, -1, dim_mat.shape[3])
    position_mat = position_mat * 100.0

    div_mat = position_mat / dim_mat
    sin_mat, cos_mat = div_mat.sin(), div_mat.cos()

    embedding = torch.cat([sin_mat, cos_mat], dim=3)
    embedding = embedding.reshape(embedding.shape[0], embedding.shape[1], embedding.shape[2] * embedding.shape[3])

    return embedding
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值