先引入多目标跟踪DeepSort的论文地址及代码链接(Python版):
论文地址:https://arxiv.org/pdf/1703.07402.pdf
代码链接:https://github.com/nwojke/deep_sort
SORT是一种实用的多目标跟踪算法,然而由于现实中目标运动多变且遮挡频繁,该算法的身份转换(Identity switches)次数较高。DEEPSORT整合外观信息使得身份转换的数量减少了45%。DEEPSORT属于传统的单假设跟踪算法,采用递归卡尔曼滤波和逐帧数据关联。所提方案为:
(1)使用马氏距离和深度特征余弦距离两种度量
(2) 采用级联匹配,有限匹配距上次出现间隔短的目标;
(3)第一级关联以余弦距离作为成本函数,但设定马氏距离和余弦距离两个阈值约束;
(4)第二关联与SORT中相同,尝试关联未确认和年龄为n=1的不匹配轨迹;
(5)同样采用试用期甄别目标,但大幅提高轨迹寿命Amax=30.
论文解读
1.轨迹处理及状态估计(track handing and state estimation)
对于每条轨迹track都有一个阈值a用于记录轨迹从上一次成功匹配到当前时刻的时间,当该值大于设置的阈值Amax=30则认为该轨迹终止,直观上说就是长时间匹配不上的轨迹认为已经走出监控范围。
轨迹的三种状态:(在代码中类似枚举变量)tentative(初始默认状态),confirmed(确认的),deleted(删除的)
class TrackState:
"""
Enumeration type for the single target track state. Newly created tracks are
classified as `tentative` until enough evidence has been collected. Then,
the track state is changed to `confirmed`. Tracks that are no longer alive
are classified as `deleted` to mark them for removal from the set of active
tracks.
"""
Tentative = 1
Confirmed = 2
Deleted = 3
在匹配时,对于没有匹配成功的检测都认为可能产生新的轨迹。但由于这些检测结果可能是一些错误警告,所以,对这种新生成的轨迹标注状态tentative(初始默认状态);
然后判定在接下来的连续3帧中是否连续匹配成功,若成功,则由tentative修改为confirmed,认为是新轨迹产生;
否则,由tentative修改为deleted,删除。另外,超过预先设置的Amax=30的轨迹,也被认为离开场景,由confirmed修改为deleted,删除
def mark_missed(self):
"""Mark this track as missed (no association at the current time step).
"""
if self.state == TrackState.Tentative:
self.state = TrackState.Deleted
elif self.time_since_update > self._max_age:
self.state = TrackState.Deleted
2、分配(匹配)问题(aaignment problem)
这里的匹配,是将过去的轨迹tracks与当前的检测detections之间的匹配。
根据tracks与detections之间的相似程度(这个相似程度是根据外观特征或iou计算得出的),使用匈牙利算法对相似程度进行筛选,得到tracks与detections之间的匹配对;
匈牙利算法:
可以告诉我们当前帧的某个检测目标detection,是否与前一帧(或者更早帧)的某个目标track是同一个物体。
首先,先介绍一下什么是分配问题(Assignment problem):假设有N个人和N个任务,每个任务可以任意分配给不同的人,已知每个人完成每个任务要花费的代价不尽相同,那么如何分配可以使得总的代价最小?
举个例子,假设现在有3个任务,要分别分配给3个人,每个人完成各个任务所需代价矩阵(cost matrix)如下所示,这个代价可以是金钱,时间等等:
怎样才能找到一个最优分配,使得完成所有任务花费的代价最小呢?
匈牙利算法(又叫KM算法)就是用来解决分配问题的一种方法,它基于定理:
如果代价矩阵的某一行或某一列同时加上或减去某个数,则这个新的代价矩阵的最优分配仍然是原代价矩阵的最优分配。
算法步骤(假设矩阵为N×N方阵):
1、对于矩阵的每一行,减去其中最小的元素
2、对于矩阵的每一列,减去其中最小的元素
3、用最少的水平线或垂直线覆盖矩阵中所有的0
4、如果线的数量=N,则找到了最优分配,算法结束,否则进入5
5、找到没有被任何线覆盖的最小元素,每个没被线覆盖的行减去这个元素,每个被线覆盖的列加上这个元素,返回步骤3
继续拿上面的例子做演示:
step1 每一行最小的元素分别为15、20、20,减去得到:
step2 每一列最小的元素分别为0、20、5,减去得到:
step3 用最少的水平线或垂直线覆盖所有的0,得到:
step4 线的数量为2,小于3,进入下一步;
step5 现在没被覆盖的最小元素是5,没被覆盖的行(第一和第二行)减去5,得到:
被覆盖的列(第一列)加上5,得到:
跳转到step3,用最少的水平线或垂直线覆盖所有的0,得到:
step4:线的数量为3,满足条件,算法结束。显然,将任务2分配给第1个人、任务1分配给第2个人、任务3分配给第3个人时,总的代价最小(0+0+0=0):
所以原矩阵的最小总代价为(40+20+25=85):
sklearn里的linear_assignment()函数以及scipy里的linear_sum_assignment()函数都实现了匈牙利算法,两者的返回值的形式不同:
import numpy as np
from sklearn.utils.linear_assignment_ import linear_assignment
from scipy.optimize import linear_sum_assignment
cost_matrix = np.array([
[15,40,45],
[20,60,35],
[20,40,25]
])
matches = linear_assignment(cost_matrix)
print('sklearn API result:\n', matches)
matches = linear_sum_assignment(cost_matrix)
print('scipy API result:\n', matches)
"""Outputs
sklearn API result:
[[0 1]
[1 0]
[2 2]]
scipy API result:
(array([0, 1, 2], dtype=int64), array([1, 0, 2], dtype=int64))
"""
在DeepSORT中,先基于外观信息(appearance information)或者IOU计算出成本矩阵矩阵cost_matrix。再用匈牙利算法对成本矩阵cost_matrix计算匹配对:
linear_assignment.py
def min_cost_matching(
distance_metric, max_dis