Advertisement

目标追踪_【MOT】详解DeepSORT多目标追踪模型

阅读量:
14b4692abab32531ffea73ec3022f187.png

1. 前言

DeepSORT作为一种多目标追踪算法在实际应用中得到了广泛使用(尽管其性能表现一般化但在实际运行中仍具备较快的速度优势且实现相对简便)因此在研究界可以找到大量基于YOLO V3 YOLO V4以及CenterNet等不同检测器实现的DeepSORT实战案例

链接分别如下:

(1)YOLO V3+DeepSORT

mikel-brostrom/Yolov3_DeepSort_Pytorch​github.com

24a0fa4fb7ef15ce9737d8e30ec46b2f.png

(2)YOLO V4+DeepSORT

https://github.com/theAIGuysCode/yolov4-deepsort​github.com

(3)CenterNet +DeepSORT

https://github.com/kimyoon-young/centerNet-deep-sort​github.com

基于DeepSORT算法设计的高度通用追踪系统(DeepSORT),能够方便地安装到任何一种检测装置上(不仅仅局限于之前列举的情况)。

在之前的项目中,我们开发了一个利用YOLOv4算法与DeepSORT技术结合的交通流参数提取系统;其中一些具体效果展示如下

45d588b682a134c68e0f2394cf834e42.png

在先前的文章中,在此前的文章中...我们对其进行了深入阐述,并特别地分析了卡尔曼滤波算法的原理和应用。链接如下

周威:【MOT】详解SORT与卡尔曼滤波算法​zhuanlan.zhihu.com

9d4f9c7804ff77e8d263991912703690.png

2 DeepSORT中的Deep

仅仅通过名称就能看出,DeepSORT确实是SORT算法的一个增强型版本 。显而易见的是,在该算法中使用了‘深度学习网络’这一关键术语。

那么,在对比于SORT算法时,DeepSORT主要实现了哪些方面的改进呢?为了深入分析

SORT算法基于卡尔曼滤波算法 对各检测框在后续帧中的运动状态进行估计,并通过将预测出的状态与实际检测到的结果进行对比分析,在此基础上完成目标车辆的精确识别和持续跟踪。

那么,在这种情况下(即物体被遮挡或存在其他未被检测到的因素),卡尔曼滤波所预测的状态信息就无法实现与实际检测结果的有效匹配。这导致追踪过程在达到一定条件后必须提前终止。

在遮挡解除后,在车辆识别阶段可能会继续进行后续操作;因此,在这种情况下SORT只能为其赋予一个新的跟踪标识以开启一个新的追踪序列。

受遮挡等情况影响较大,会有大量的ID切换

那么如何缓解SORT算法中出现过多ID切换的问题呢?由于在线追踪的特点限制了我们依赖全局检测到的物体框信息,在面对因拥挤状态导致的身份标签切换问题时,可以通过之前捕获的物体外观特征来进行推断。具体来说,在一个物体被遮挡时的状态维持至遮挡解除期间(即从被遮挡开始至结束),我们能够通过之前捕获的信息推断该物体在未被遮挡时的身份标签号,并将其分配给当前观测结果。这种处理方式能够有效降低因身份标签切换带来的混乱现象。

当然DeepSORT就是这么做的,论文中提到

We address the challenge by replacing the association measure with an enhanced metric that incorporates both motion and appearance data. We apply a convolutional neural network (CNN) trained for pedestrian discrimination on a large-scale person re-identification dataset.

显然,在DeepSORT算法中采用了单一简洁(计算负担不高)的一个深度神经网络(CNN),用于从目标区域内部提取出用于描述目标特性的外部特征信息(以低维特征向量表示)。每一次检测与追踪循环结束后,会实时更新目标描述信息。

每当执行下一个步骤时

那么这个小型的CNN网络长什么样子呢?论文中给出了结构表,如下

6fd05066c8f8825cfac181a504f3fedf.png

该网络最终生成的空间维度设定为128维。关于残差网络及其相关模块的结构描述较为简洁明了。

该网络最终生成的空间维度设定为128维。关于残差网络及其相关模块的结构描述较为简洁明了。

值得我们关注的一个重要问题是,在DeepSORT算法的主要应用场景是person detection时(即行人追踪问题中),推荐使用的输入尺寸为128像素(高度)乘以64像素(宽度)的矩形框。如果要实现对不同目标进行跟踪,则可能需要相应地调整网络模型的输入参数。

实现该网络结构的代码如下:

复制代码
 class Net(nn.Module):

    
     def __init__(self, num_classes=751 ,reid=False):
    
     super(Net,self).__init__()
    
     # 3 128 64
    
     self.conv = nn.Sequential(
    
         nn.Conv2d(3,64,3,stride=1,padding=1),
    
         nn.BatchNorm2d(64),
    
         nn.ReLU(inplace=True),
    
         # nn.Conv2d(32,32,3,stride=1,padding=1),
    
         # nn.BatchNorm2d(32),
    
         # nn.ReLU(inplace=True),
    
         nn.MaxPool2d(3,2,padding=1),
    
     )
    
     # 32 64 32
    
     self.layer1 = make_layers(64,64,2,False)
    
     # 32 64 32
    
     self.layer2 = make_layers(64,128,2,True)
    
     # 64 32 16
    
     self.layer3 = make_layers(128,256,2,True)
    
     # 128 16 8
    
     self.layer4 = make_layers(256,512,2,True)
    
     # 256 8 4
    
     self.avgpool = nn.AvgPool2d((8,4),1)
    
     # 256 1 1 
    
     self.reid = reid
    
     self.classifier = nn.Sequential(
    
         nn.Linear(512, 256),
    
         nn.BatchNorm1d(256),
    
         nn.ReLU(inplace=True),
    
         nn.Dropout(),
    
         nn.Linear(256, num_classes),
    
     )
    
     
    
     def forward(self, x):
    
     x = self.conv(x)
    
     x = self.layer1(x)
    
     x = self.layer2(x)
    
     x = self.layer3(x)
    
     x = self.layer4(x)
    
     x = self.avgpool(x)
    
     x = x.view(x.size(0),-1)
    
     # B x 128
    
     if self.reid:
    
         x = x.div(x.norm(p=2,dim=1,keepdim=True))
    
         return x
    
     # classifier
    
     x = self.classifier(x)
    
     return x

从实验结果可以看出,在网络运行时其输出的结果并非固定在128维度空间中。为了提取图像特征,在整个网络运行过程中应当避免激活最后一层 classifier(其中 128 维向量表示的是被输入至 classifier 前的所有特征数据)。

3 DeepSORT中的卡尔曼滤波

我们在SORT深度解析时提及过,在SORT算法中所使用的卡尔曼滤波所使用的状态是一个7维的状态向量

e5a16faa9c6348518e15df26219df179.png

在DeepSORT中,使用的状态是一个8维的向量

45282d52d85025958cb793abc7a1c36e.png

相较于SORT中的状态,在这里新增了一个**长宽比(aspect ratio)**的变化速率。这合乎情理是因为,在SORT方法中假设物体检测框的长宽比是固定的;但在真实场景中,物体会随着镜头移动或相机运动而改变其长宽比。

同时,DeepSORT对追踪的初始化、新生与消失进行了设定。

  • 初始化 :自该检测起非新目标的初始化过程启动。
  • 新生 :自初始化以来持续获得高质量跟踪数据,并在前三帧内完成全部捕获步骤。
  • 消失 :若同一物体长时间未能与任何现有跟踪建立持续联系,则被视为退出画面。

具体来说,您是否对卡尔曼滤波算法的具体应用感兴趣?我可以为您提供之前的SORT分析链接以及我的详细分析内容。

周威:【MOT】详解SORT与卡尔曼滤波算法​zhuanlan.zhihu.com

9d4f9c7804ff77e8d263991912703690.png

4. DeepSORT中的分配问题

在惯例模式下(类似于SORT),解决分配问题所采用的是匈牙利算法这一特定方法:该算法特别适用于解决那些基于运动特征构建的代价矩阵所对应的问题。相比之下,在DeepSORT模型中,则采用了更为综合的特征融合策略:即不仅依赖运动特征来构建代价矩阵(由小型CNN提取的128维向量),还融合了外观特征(基于卡尔曼滤波预测的目标位置),从而能够更加全面地反映目标特性信息。随后,在构建好完整的代价矩阵后,则通过匈牙利算法实现目标匹配这一关键步骤

  1. 运动(motion)特征

作者采用了马氏距离这一指标, 用于评估滤波后的状态与新增观测结果(检测框)之间的距离。

3f60d347fad7041f9189ae21526f8517.png

公式1

上述公式中

表示

第i个追踪分布 (卡尔曼滤波分布)在测量空间上的投影,

为均值,

为协方差。因为要和

测量值(检测框) 进行距离测算,所以必须转到 同一空间 分布中才能进行。

马氏距离基于**卡尔曼滤波器的平均位置(mean track location)的标准偏差与检测框之间的比较来评估状态估计的不确定性。

为第i个追踪分布和第j个检测框之间的马氏距离(不确定度)。

值得注意的是,这里的两个符号含义分别为

  • i:追踪的序号
  • j:检测框的序号

i,j的含义将在后面的解析中仍然出现。

通过设定一个合理的阈值来计算马氏距离...从而能够剔除那些无关联的目标。文章中给出的阈值是

The Mahalanobis distance within a 95% confidence interval is estimated from the inverse chi-squared distribution.

就是倒卡方分布计算出来的95%置信区间作为阈值。

有关马氏距离的实现,定义在Tracker类 中可以获得,代码如下:

复制代码
     def gating_distance(self, mean, covariance, measurements,

    
                     only_position=False):
    
     """Compute gating distance between state distribution and measurements.
    
   5.         A suitable distance threshold can be obtained from `chi2inv95`. If
    
     `only_position` is False, the chi-square distribution has 4 degrees of
    
     freedom, otherwise 2.
    
   9.         Parameters
    
     ----------
    
     mean : ndarray
    
         Mean vector over the state distribution (8 dimensional).
    
     covariance : ndarray
    
         Covariance of the state distribution (8x8 dimensional).
    
     measurements : ndarray
    
         An Nx4 dimensional matrix of N measurements, each in
    
         format (x, y, a, h) where (x, y) is the bounding box center
    
         position, a the aspect ratio, and h the height.
    
     only_position : Optional[bool]
    
         If True, distance computation is done with respect to the bounding
    
         box center position only.
    
   23.         Returns
    
     -------
    
     ndarray
    
         Returns an array of length N, where the i-th element contains the
    
         squared Mahalanobis distance between (mean, covariance) and
    
         `measurements[i]`.
    
   30.         """
    
     mean, covariance = self.project(mean, covariance)
    
     if only_position:
    
         mean, covariance = mean[:2], covariance[:2, :2]
    
         measurements = measurements[:, :2]
    
  
    
     cholesky_factor = np.linalg.cholesky(covariance)
    
     d = measurements - mean
    
     z = scipy.linalg.solve_triangular(
    
         cholesky_factor, d.T, lower=True, check_finite=False,
    
         overwrite_b=True)
    
     squared_maha = np.sum(z * z, axis=0)
    
     return squared_maha

通常情况下,在目标运动过程中其**不确定性较低(马氏距离较小)**时(即基于以下假设:所有物体运动具有一定的规律性且无明显遮挡的情况下),基于motion特性的模型(Sort improved)自然展现出较好的效果。

但是实际情况并非常理想,并非仅靠motion特征就可实现目标;必须结合appearance特征以弥补其局限性

2. 外观(appearance)特征

上文我们介绍了外观特征提取网络——一种小型残差网络。该系统采用reshape操作对检测窗口内的物体进行处理。其中检测窗口大小为128×64像素,并且专门用于人体目标检测。

对于每个检测框(编号为j)内物体

,其128维度的向量设为

,该向量的模长为1,即

。这个应该是经过了一个softmax层的原因。

随后作者为每个目标k构建了相应的存储单元——一个专门用于存储各帧中该目标的外观特征描述(128维向量)的具体应用。

表示。

请注意,在此定义中符号\mathbf{k}代表的是"目标追踪对象"这一概念,请明确区分与之相关的其他变量。特别地,在图中进行了说明。

9f3103f1db967d23715cb2b6b8f5bd66.png

作者原论文是这么提到的

afcb6f4920c09bb43d2d0920e2369440.png

这里的

就是gallery,作者限定了

的大小,它最大不超过100,即最多只能存储

在目标k的当前时段内,包含在过去100帧中的目标外观特征。其中i代表的就是前面所述的目标追踪编号。

在目标k的当前时段内,包含在过去100帧中的目标外观特征。其中i代表的就是前面所述的目标追踪编号。

接着在某一时刻,作者获得出检测框(编号为j)的外观特征,记作

。然后求解所有

已收集的 gallery 中的外观特征与其对应的检测框(编号为 j)的最小余弦相似度。

4d7806d9a4de75aeb80106c1e9470f26.png

公式2

接着作者对最小余弦距离设定了阈值,来区分关联是否合理,如下

0b2a4a38b781b7c9574a40a9b90f7e5e.png

3. 运动(motion)特征与外观(appearance)特征的融合

motion特征与appearance特征相互补充,在DeepSORT算法中,基于马氏距离计算得到的motion特徵能够揭示物体运动轨迹的关键特征,在短期内预测效果尤为显著

appearance特征(通过余弦距离计算获得)可以在目标被长时间被遮挡 后,使目标重新获取其ID编号 ,降低目标ID状态转换的频率。

为了结合两个特征,作者做了一个简单的加权运算 。也就是

89c6855414222abc75b624ed3175a1b4.png

公式3

这里的

为马氏距离,

为余弦距离。

为权重系数。所以当

时,那么就是

改进版的SORT ,

时,仅仅依靠外观特征进行匹配也是可以进行追踪的。

最后,作者设定了如何判断关联是否匹配的判别总阈值,作者提到

where we define an acceptable association as one that falls within the gating regions of both metrics

8b7b2e8ac0fda853eff399103a9d099b.png

公式4

该学者将上述提到的两个指标(分别为马氏距离与余弦距离指标)结合在一起,并对某个关联(association)进行综合判断这一判断标准是否合理可行

4 更多匹配的细节

论文中作者提到了Matching Cascade,该算法流程的伪代码如下:

832bb56aa501ec591e1323aba08d8622.png

输入 :该算法接受三个输入,分别为

  • 追踪的索引集合

,

,i在前面已经讲过了。

  • 当前帧检测框索引的集合

,

,j在前面已经讲过了。

  • 最大保留时长(Maximum age)

步骤1 :根据上面图名为公式3的公式,计算联合代价矩阵

步骤2 :根据上面图名为公式4的公式,计算gate矩阵

步骤3 :初始化匹配列表

,为空

步骤4 :初始化非匹配列表

,将

赋予

步骤5 :循环

基于给定age值确定track。
利用匈牙利算法计算最小代价匹配时的变量i和j。
将满足一定条件下的i和j设置为匹配列表。

,保存

  • 重新更新非匹配列表

步骤6 :循环结束,匹配完成

返回匹配列表

非匹配列表

代码实现如下:

复制代码
 def min_cost_matching(

    
     distance_metric, max_distance, tracks, detections, track_indices=None,
    
     detection_indices=None):
    
     """Solve linear assignment problem.
    
   6.     Parameters
    
     ----------
    
     distance_metric : Callable[List[Track], List[Detection], List[int], List[int]) -> ndarray
    
     The distance metric is given a list of tracks and detections as well as
    
     a list of N track indices and M detection indices. The metric should
    
     return the NxM dimensional cost matrix, where element (i, j) is the
    
     association cost between the i-th track in the given track indices and
    
     the j-th detection in the given detection_indices.
    
     max_distance : float
    
     Gating threshold. Associations with cost larger than this value are
    
     disregarded.
    
     tracks : List[track.Track]
    
     A list of predicted tracks at the current time step.
    
     detections : List[detection.Detection]
    
     A list of detections at the current time step.
    
     track_indices : List[int]
    
     List of track indices that maps rows in `cost_matrix` to tracks in
    
     `tracks` (see description above).
    
     detection_indices : List[int]
    
     List of detection indices that maps columns in `cost_matrix` to
    
     detections in `detections` (see description above).
    
   28.     Returns
    
     -------
    
     (List[(int, int)], List[int], List[int])
    
     Returns a tuple with the following three entries:
    
     * A list of matched track and detection indices.
    
     * A list of unmatched track indices.
    
     * A list of unmatched detection indices.
    
   36.     """
    
     if track_indices is None:
    
     track_indices = np.arange(len(tracks))
    
     if detection_indices is None:
    
     detection_indices = np.arange(len(detections))
    
  
    
     if len(detection_indices) == 0 or len(track_indices) == 0:
    
     return [], track_indices, detection_indices  # Nothing to match.
    
  
    
     cost_matrix = distance_metric(
    
     tracks, detections, track_indices, detection_indices)
    
     cost_matrix[cost_matrix > max_distance] = max_distance + 1e-5
    
  
    
     row_indices, col_indices = linear_assignment(cost_matrix)
    
  
    
     matches, unmatched_tracks, unmatched_detections = [], [], []
    
     for col, detection_idx in enumerate(detection_indices):
    
     if col not in col_indices:
    
         unmatched_detections.append(detection_idx)
    
     for row, track_idx in enumerate(track_indices):
    
     if row not in row_indices:
    
         unmatched_tracks.append(track_idx)
    
     for row, col in zip(row_indices, col_indices):
    
     track_idx = track_indices[row]
    
     detection_idx = detection_indices[col]
    
     if cost_matrix[row, col] > max_distance:
    
         unmatched_tracks.append(track_idx)
    
         unmatched_detections.append(detection_idx)
    
     else:
    
         matches.append((track_idx, detection_idx))
    
     return matches, unmatched_tracks, unmatched_detections

和上面的伪代码一一对应,很清晰。

至此为止,已经完成了对DeepSORT的讲解。如对内容有疑问,请深入研究原始论文和源代码实现。

5. 总结

如若文中出现误解或误读的情况,请您及时告知我们以便共同进步!未来本专栏将继续对目标追踪相关文章进行深入解读,欢迎持续关注!

全部评论 (0)

还没有任何评论哟~