深度学习系列17:FairMOT代码分析
1. 代码逻辑从图中可以看出,有三个核心点:network进行了4次下采样和上采样分别计算特征图输出结果的detection部分使用类似centerNet,heatmap用于预测中心点,center offset用于调整位置,boxsize用于预测框直接将特征向量作为结果输出(接在reid embeddings之后)跟踪器定义:tracker:activated(激活状态), tracked(跟踪
1. 核心模型
从图中可以看出,有三个核心点:
- network进行了4次下采样和上采样分别计算特征图
- 输出结果的detection部分使用类似centerNet,heatmap用于预测中心点,center offset用于调整位置,boxsize用于预测框
- 直接将特征向量作为结果输出(接在reid embeddings之后)
2. 跟踪器
使用JDE跟踪器,包含如下参数:
activated(激活状态), tracked(跟踪状态),mean(卡尔曼均值向量), covariance(卡尔曼协方差矩阵), smooth_feat(tracker的外观特征)
4种跟踪状态:
unconfirmed_stracks(activated = F, track_state=tracked ) 只出现一次的目标(检测器误检的目标)
activated_stracks(activate=T, track_state=tracked) 跟踪状态良好的tracker
lost_stracks(activate=T, track_state=lost)激活,但是跟踪状态丢失
refind_stracks(activated=T, track_state=lost->tracked)跟丢之后重新找回的目标
几个数据结构:
detection/track:每一帧的模型预测结果
strack:detection/track的组合。第一帧的activated=T,并赋予一个kalman_filter,其他为False
然后是具体流程:
- 第一帧:
对每个目标初始化一个tracker,检测出n个目标,就用n个tracker,然后将n个tracker放入activated_stracks容器中。
# 从step 4开始,其中u_detection是未分配的track。
for inew in u_detection:
# 对cosine/iou/uncofirmed_tracker都未匹配的detection重新初始化一个unconfimed_tracker
track = detections[inew]
track.activate(self.kalman_filter, self.frame_id)
# 激活track,第一帧的activated=T,其他为False
activated_stacks.append(track)
- 第二帧,对新检测目标进行外观+kalman距离匹配
step1对第2帧进行目标检测得detections,
然后再step2将[activated_strack,lost_stracks]融合成pool_stracks,
将这个pool_stracks与detections根据feat计算外观矩阵,就是用feat计算cosine距离,
然后用Strack.multi_predict函数,也就是卡尔曼算法预测pool_stracks的新的mean,covariance。
然后用matching.embedding_distance计算pool_stracks和detection的外观距离,
接着用matching.fuse_motion,加入运动模型,得到新的距离矩阵,并将大于阈值的矩阵赋值为inf。
接下来调用matching.linear_assignment,利用匈牙利算法进行匹配,得到matches, u_track, u_detection三元组。
- matches是能匹配的track和detection:1)pool_stracks的track_state=tracked,更新smooth_feat,卡尔曼状态更新mean,covariance(卡尔曼用),计入activated_stracks;2)pool_stracks的track_state=tracked,更新smooth_feat,卡尔曼状态更新mean,covariance(卡尔曼用),计入refind_stracks
不能匹配的:提出不能匹配的,得到新的detections,r_tracked_stracks
strack_pool = joint_stracks(tracked_stracks, self.lost_stracks)
dists = matching.embedding_distance(strack_pool, detections) # 计算新检测出来的目标和tracked_tracker之间的cosine距离
STrack.multi_predict(strack_pool) # 卡尔曼预测
dists = matching.fuse_motion(self.kalman_filter, dists, strack_pool, detections) # 利用卡尔曼计算detection和pool_stacker直接的距离代价
matches, u_track, u_detection = matching.linear_assignment(dists, thresh=0.7) # 匈牙利匹配 // 将跟踪框和检测框进行匹配 // u_track是未匹配的tracker的索引,
for itracked, idet in matches: # matches:63*2 , 63:detections的维度,2:第一列为tracked_tracker索引,第二列为detection的索引
track = strack_pool[itracked]
det = detections[idet]
if track.state == TrackState.Tracked:
track.update(det, self.frame_id) # 匹配的pool_tracker和detection,更新特征和卡尔曼状态
activated_starcks.append(track)
else:
track.re_activate(det, self.frame_id, new_id=False) # 如果是在lost中的,就重新激活
refind_stracks.append(track)
- 剩下的,用IOU进行匹配
对剩下的detections、r_tracked_stracks,使用matching.iou_distanc进行IOU匹配,然后对距离使用matching.linear_assignment再次进行匈牙利匹配
能匹配的:1)pool_stracks的track_state=tracked,更新smooth_feat,卡尔曼状态更新mean,covariance(卡尔曼用),计入activated_stracks;pool_stracks的track_state==tracked,更新smooth_feat,卡尔曼状态更新mean,covariance(卡尔曼用),计入refind_stracks
不能匹配的:r_tracked_stracks状态track_state改为lost,detections再遗留到下一步进行继续匹配
detections = [detections[i] for i in u_detection] # u_detection是未匹配的detection的索引
r_tracked_stracks = [strack_pool[i] for i in u_track if strack_pool[i].state == TrackState.Tracked]
dists = matching.iou_distance(r_tracked_stracks, detections)
matches, u_track, u_detection = matching.linear_assignment(dists, thresh=0.5)
for itracked, idet in matches:
track = r_tracked_stracks[itracked]
det = detections[idet]
if track.state == TrackState.Tracked:
track.update(det, self.frame_id)
activated_starcks.append(track)
else:
track.re_activate(det, self.frame_id, new_id=False) # 前面已经限定了是TrackState.Tracked,这里是不用运行到的。
refind_stracks.append(track)
for it in u_track:
track = r_tracked_stracks[it]
if not track.state == TrackState.Lost:
track.mark_lost()
lost_stracks.append(track) # 将和tracked_tracker iou未匹配的tracker的状态改为lost
- 上一步遗留的detection与unconfirmed_stracks进行IOU匹配
计算IOU cost,然后进行匈牙利匹配
能匹配:更新 unconfirmed_stracks,更新smooth_feat,卡尔曼状态更新mean,covariance(卡尔曼用),计入activated_stracks
不能匹配:unconfirmed_stracks直接计入removed_stracks;不能匹配的detections,在遗留到下一步
detections = [detections[i] for i in u_detection] # 将cosine/iou未匹配的detection和unconfirmed_tracker进行匹配
dists = matching.iou_distance(unconfirmed, detections)
matches, u_unconfirmed, u_detection = matching.linear_assignment(dists, thresh=0.7)
for itracked, idet in matches:
unconfirmed[itracked].update(detections[idet], self.frame_id)
activated_starcks.append(unconfirmed[itracked])
for it in u_unconfirmed:
track = unconfirmed[it]
track.mark_removed()
removed_stracks.append(track)
- 上一步遗留的detections,在step4初始化成unconfirmed_stracks中的tracker
for inew in u_detection: # cosine/iou/uncofirmed_tracker都未匹配的detection重新初始化一个unconfimed_tracker
track = detections[inew]
if track.score < self.det_thresh:
continue
track.activate(self.kalman_filter, self.frame_id) # 激活track,第一帧的activated=T,其他为False
activated_starcks.append(track)
- 对15帧连续track_state=lost的tracker,进行删除(这里改成仅用外观向量匹配)
for track in self.lost_stracks:
if self.frame_id - track.end_frame > self.max_time_lost: # 消失15帧之后
track.mark_removed()
removed_stracks.append(track)
3. 代码结构
主目录下的requirements.txt需要安装:pip install -r requirements.txt
需要自己新建一个文件夹models:下载all_hrnet_v2_w18.pth、hrnetv2_w18_imagenet_pretrained.pth两个预训练模型
3.1 Tracker
basetracker基本数据结构,注意其中location用于跨镜跟踪。features是一个数组,用于存储所有的特征向量:
接下来是STrack类,继承BaseTrack类,注意这里有一个buffer_size参数,表示最大允许存储的特征向量个数:
然后是最核心的JDETracker:
其中的update函数用到了第二节的代码。
3.2 Networks
3.2.1 DCNv2
使用DCNv2_new作为backbone
cd src/lib/models/networks/DCNv2_new
sh make.sh
我们来看下里面的内容,其实就是pip install DCNv2,参见https://github.com/charlesshang/DCNv2
具体来说,原来只需要学习9个参数,现在要学习18个参数(每个点还有一个偏移量要学习)
4. Demo
两种模式,我们测试的时候使用的方法是,把追踪结果写到result.txt中,然后再进行渲染。
核心是调用了track.py中的eval_seq函数。
更多推荐
所有评论(0)