DeepSort 代码学习

https://github.com/nwojke/deep_sort #这个是代码网址





DeepSort论文学习



																				<div class="tags-box space">
							<span class="label">个人分类:</span>
															<a class="tag-link" href="https://blog.csdn.net/cdknight_happy/article/category/7516389" target="_blank">目标追踪																</a>
						</div>
																							</div>
			<div class="operating">
													</div>
		</div>
	</div>
</div>
<article class="baidu_pl">
	<div id="article_content" class="article_content clearfix csdn-tracking-statistics" data-pid="blog" data-mod="popu_307" data-dsm="post">
							<div class="article-copyright">
				版权声明:本文为博主原创文章,未经博主允许不得转载。					https://blog.csdn.net/cdknight_happy/article/details/79731981				</div>
							            <div class="markdown_views">
						<!-- flowchart 箭头图标 勿删 -->
						<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path></svg>
						<p>论文题目:SIMPLE ONLINE AND REALTIME TRACKING WITH A DEEP ASSOCIATION METRIC</p>

摘要

DeepSort是在Sort目标追踪基础上的改进。引入了在行人重识别数据集上离线训练的深度学习模型,在实时目标追踪过程中,提取目标的表观特征进行最近邻匹配,可以改善有遮挡情况下的目标追踪效果。同时,也减少了目标ID跳变的问题。

核心思想

算法的核心思想还是用一个传统的单假设追踪方法,方法使用了递归的卡尔曼滤波和逐帧的数据关联。

轨迹处理和状态估计

该部分的思路和sort很接近。

  • 运动状态估计:
    使用8个参数这里写图片描述来进行运动状态的描述,其中(u,v)是bounding box的中心坐标,r是长宽比,h表示高度。其余四个变量表示对应的在图像坐标系中的速度信息。使用一个基于常量速度模型和线性观测模型的标准kalman滤波器进行目标运动状态的预测,预测的结果为(u,v,r,h)。

  • 目标的创建与移除
    对每一个追踪目标,记录自其上一次检测结果与追踪结果匹配之后的帧数akak,则认为对该目标的追踪过程已结束。对新目标出现的判断则是,如果某次检测结果中的某个目标始终无法与已经存在的追踪器进行关联,那么则认为可能出现了新目标。如果连续的3帧中潜在的新的追踪器对目标位置的预测结果都能够与检测结果正确关联,那么则确认是出现了新的运动目标;如果不能达到该要求,则认为是出现了“虚警”,需要删除该运动目标。

指派问题

传统的解决检测结果与追踪预测结果的关联的方法是使用匈牙利方法。本文作者同时考虑了运动信息的关联和目标外观信息的关联。

  • 运动信息的关联:使用了对已存在的运动目标的运动状态的kalman预测结果与检测结果之间的马氏距离进行运行信息的关联。
    这里写图片描述
    djdj.

级联匹配

当一个目标长时间被遮挡之后,kalman滤波预测的不确定性就会大大增加,状态空间内的可观察性就会大大降低。假如此时两个追踪器竞争同一个检测结果的匹配权,往往遮挡时间较长的那条轨迹的马氏距离更小,使得检测结果更可能和遮挡时间较长的那条轨迹相关联,这种不理想的效果往往会破坏追踪的持续性。这么理解吧,假设本来协方差矩阵是一个正态分布,那么连续的预测不更新就会导致这个正态分布的方差越来越大,那么离均值欧氏距离远的点可能和之前分布中离得较近的点获得同样的马氏距离值。
所以,作者使用了级联匹配来对更加频繁出现的目标赋予优先权,具体的算法如下图:
这里写图片描述
级联匹配的核心思想就是由小到大对消失时间相同的轨迹进行匹配,这样首先保证了对最近出现的目标赋予最大的优先权,也解决了上面所述的问题。
在匹配的最后阶段还对unconfirmed和age=1的未匹配轨迹进行基于IoU的匹配。这可以缓解因为表观突变或者部分遮挡导致的较大变化。当然有好处就有坏处,这样做也有可能导致一些新产生的轨迹被连接到了一些旧的轨迹上。但这种情况较少。

深度特征描述器

网络结构:
这里写图片描述
在行人重识别数据集上离线训练模型。输入128维的归一化的特征。在GTX1050m显卡上,输入30个bounding box提取特征的时间约为30ms。预训练的模型和代码位于https://github.com/nwojke/deep_sort

实验

作者使用《Poi:
Multiple object tracking with high performance detection and appearance feature》文章训练的高性能faster rcnn模型进行检测。检测的置信度阈值设置为0.3。
和sort对比,好处是:
- 减少了45%的ID switch;
- 结合了深度外观信息,对遮挡目标的追踪效果大大提升;
- FP的升高很多,文章中提出这主要是由于静态场景中detection的错误以及过长的允许丢失的track age所导致的(相对于SORT只用相邻帧进行匹配来说,Deep SORT允许高达30帧的丢失,而Kalman的等速运动模型没有改变,这主要造成了FP的升高)。
- 20Hz,依旧实用;
- 达到了state-of-art online tracking的效果。

参考:
https://www.cnblogs.com/YiXiaoZhou/p/7074037.html
http://www.cnblogs.com/yanwei-li/p/8643446.html

代码解析

deep_sort代码(此处)处理流程解析:
按视频帧顺序处理,每一帧的处理流程如下:

  • 读取当前帧目标检测框的位置及各检测框图像块的深度特征(此处在处理实际使用时需要自己来提取);
  • 根据置信度对检测框进行过滤,即对置信度不足够高的检测框及特征予以删除;
  • 对检测框进行非最大值抑制,消除一个目标身上多个框的情况;
  • 预测:使用kalman滤波预测目标在当前帧的位置

执行kalman滤波公式1和2:x(k)=Ax(k1)x(k)=Ax(k−1)为目标的估计误差(代码中的covariance);A为状态转移矩阵;Q为系统误差;


#mean

mean_pos = measurement
mean_vel = np.zeros_like(mean_pos)
mean = np.r_[mean_pos, mean_vel]


#covariance

self._std_weight_position = 1. / 20
self._std_weight_velocity = 1. / 160    (可调参数)
std = [
            2 * self._std_weight_position * measurement[3],
            2 * self._std_weight_position * measurement[3],
            1e-2,
            2 * self._std_weight_position * measurement[3],
            10 * self._std_weight_velocity * measurement[3],
            10 * self._std_weight_velocity * measurement[3],
            1e-5,
            10 * self._std_weight_velocity * measurement[3]]
        covariance = np.diag(np.square(std))
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

#矩阵A

ndim, dt = 4, 1.

self._motion_mat = np.eye(2 * ndim, 2 * ndim)
for i in range(ndim):
  self._motion_mat[i, ndim + i] = dt                   
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

A=1000000001000000001000000001000010001000010001000010001000010001A=1000100001000100001000100001000100001000000001000000001000000001

std_pos = [
            self._std_weight_position * mean[3],
            self._std_weight_position * mean[3],
            1e-2,
            self._std_weight_position * mean[3]]
std_vel = [
            self._std_weight_velocity * mean[3],
            self._std_weight_velocity * mean[3],
            1e-5,
            self._std_weight_velocity * mean[3]]

#矩阵Q

motion_cov = np.diag(np.square(np.r_[std_pos, std_vel]))


#kalman滤波公式1

mean = np.dot(self._motion_mat, mean)


#kalman滤波公式2

covariance = np.linalg.multi_dot((
            self._motion_mat, covariance, self._motion_mat.T)) + motion_cov
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

预测完之后,对每一个追踪器设置self.time_since_update += 1

  • 更新:更新kalman追踪器参数及特征集,另外进行目标消失、新目标出现的判断

    • 检测结果与追踪预测结果的匹配

    • 区分已确认状态的追踪器和未确认状态的追踪器;
    • 已确认状态的追踪器进行级联匹配

    • 对同一消失时间的多个追踪器,计算当前帧新检测的每个目标的深度特征与各追踪器已保存的特征集之间的余弦距离矩阵。假如当前帧有11个检测目标,已有10个追踪器,每个追踪器已保留前面6帧目标的深度特征,则计算得到的cost_matrix大小为10*11,计算过程为首先对每一个追踪器的6个特征,计算与当前帧11个新检测目标特征之间的(1 - 余弦距离),得到6*11矩阵,对每个检测块求最小余弦距离,得到1*11矩阵,存入cost_matrix对应行,表示对当前追踪器而言与当前各检测块之间最小的余弦距离;
    cost_matrix = self.metric.distance(features, targets)
    
        #distance函数
        cost_matrix = np.zeros((len(targets), len(features)))
            for i, target in enumerate(targets):
                   cost_matrix[i, :] = self._metric(self.samples[target], features)
    
                           #_metric函数
                           distances = _cosine_distance(x, y)
                            return distances.min(axis=0)
    
            return cost_matrix
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 在计算特征的cost_matrix的基础上,计算kalman滤波预测位置与检测框之间的马氏距离,具体过程为,先将各检测框由[x,y,w,h]转化为[center x,center y, aspect ration,height],对每一个追踪器,也就是cost_matrix中的每一行,计算预测结果与检测结果之间的马氏距离,假设该帧有11个检测结果,那么马氏距离为1*11的矩阵,对cost_matrix中当前行,将马氏距离大于指定阈值的位置处置为1e+5。这样做实现了作者论文中所说的两种度量方式的gating,但是没有体现λλ参数的作用,另外使用cholesky分解计算马氏距离部分不是很理解。
    • 将cost_matrix中大于max_distance的元素置为cost_matrix > max_distance
    • 使用匈牙利算法以cost_matrix为输入进行指派
    • 指派完成后,分类未匹配的检测、未匹配的追踪、匹配对(cost_matrix中阈值大于max_distance的匹配对也送入未匹配检测和未匹配追踪中去)
  • 未级联匹配上追踪器和未确认状态的追踪与未级联匹配上的检测之间基于IOU进行匹配,具体实现是计算cost_matrix矩阵,矩阵的每一行表示一个追踪器和每一个检测结果之间的(1 - IOU)。对cost_matrix中大于max_distance的元素置为max_distance,然后使用匈牙利算法以cost_matrix矩阵作为输入进行指派,指派完成后依然统计matchs,unmatched_detections,unmatched_tracks;

  • 匹配上的,去做参数更新

    • 参数更新的过程就是计算kalman滤波的公式3,4,5。其中公式3中的R矩阵为
    std = [
        self._std_weight_position * mean[3],
        self._std_weight_position * mean[3],
        1e-1,
        self._std_weight_position * mean[3]]
    
    innovation_cov = np.diag(np.square(std))
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 参数更新完成之后,特征插入追踪器特征集,对应参数进行重新初始化
    self.features.append(detection.feature)
    
    self.hits += 1
    self.time_since_update = 0 #重置为0
    
    
    #满足条件时确认追踪器
    
    if self.state == TrackState.Tentative and self.hits >= self._n_init:
       self.state = TrackState.Confirmed
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 未匹配的追踪器有可能要删除

  • 未匹配的追踪器表示虽然预测到了新的位置,但是检测框匹配不上

    
    #待定状态的追踪器直接删除
    
        if self.state == TrackState.Tentative:
            self.state = TrackState.Deleted 
    
        #已经时confirm状态的追踪器,虽然连续多帧对目标进行了预测, 
        #但中间过程中没有任何一帧能够实现与检测结果的关联,说明目标 
        #可能已经移除了画面,此时直接设置追踪器为待删除状态
        elif self.time_since_update > self._max_age:
            self.state = TrackState.Deleted
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 未匹配的检测,初始化为新的追踪器
    没有匹配上的检测,说明是出现了新的待追踪目标,此时初始化一个新的kalman滤波器,再初始化一个新的追踪器

  • 
    #根据初始检测位置初始化新的kalman滤波器的mean和covariance
    
    mean, covariance = self.kf.initiate(detection.to_xyah()) 
    
    #初始化一个新的tracker
    
    self.tracks.append(Track(
                mean, covariance, self._next_id, self.n_init, self.max_age,
                detection.feature))
    
        #Tracker的构造函数
                self.mean = mean #初始的mean
                self.covariance = covariance #初始的covariance
                self.track_id = track_id #id
                self.hits = 1
                self.age = 1
                self.time_since_update = 0 #初始值为0
    
                self.state = TrackState.Tentative #初始为待定状态
                self.features = []
                if feature is not None:
                    self.features.append(feature) #特征入库
    
                self._n_init = n_init
                self._max_age = max_age
    
    #总的目标id++
    
    self._next_id += 1
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
  • 删除待删除状态的追踪器

  • self.tracks = [t for t in self.tracks if not t.is_deleted()]
     
     
    • 1
    • 更新留下来的追踪器的特征集
        #每个activate的追踪器保留最近的self.budget条特征
            for feature, target in zip(features, targets):
                self.samples.setdefault(target, []).append(feature)
                if self.budget is not None:
                    self.samples[target] = self.samples[target][-self.budget:]
    
            #以dict的形式插入总库
            self.samples = {k: self.samples[k] for k in active_targets}
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    				<script>
    					(function(){
    						function setArticleH(btnReadmore,posi){
    							var winH = $(window).height();
    							var articleBox = $("div.article_content");
    							var artH = articleBox.height();
    							if(artH > winH*posi){
    								articleBox.css({
    									'height':winH*posi+'px',
    									'overflow':'hidden'
    								})
    								btnReadmore.click(function(){
    									articleBox.removeAttr("style");
    									$(this).parent().remove();
    								})
    							}else{
    								btnReadmore.parent().remove();
    							}
    						}
    						var btnReadmore = $("#btn-readmore");
    						if(btnReadmore.length>0){
    							if(currentUserName){
    								setArticleH(btnReadmore,3);
    							}else{
    								setArticleH(btnReadmore,1.2);
    							}
    						}
    					})()
    				</script>
    				</article>
    
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值