从图中可以看出,有三个核心点:
使用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
然后是具体流程:
# 从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)
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)
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
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)
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)
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)
主目录下的requirements.txt需要安装:pip install -r requirements.txt
需要自己新建一个文件夹models:下载all_hrnet_v2_w18.pth、hrnetv2_w18_imagenet_pretrained.pth两个预训练模型
basetracker基本数据结构,注意其中location用于跨镜跟踪。features是一个数组,用于存储所有的特征向量:
接下来是STrack类,继承BaseTrack类,注意这里有一个buffer_size参数,表示最大允许存储的特征向量个数:
然后是最核心的JDETracker:
其中的update函数用到了第二节的代码。
使用DCNv2_new作为backbone
cd src/lib/models/networks/DCNv2_new
sh make.sh
我们来看下里面的内容,其实就是pip install DCNv2,参见https://github.com/charlesshang/DCNv2
具体来说,原来只需要学习9个参数,现在要学习18个参数(每个点还有一个偏移量要学习)
两种模式,我们测试的时候使用的方法是,把追踪结果写到result.txt中,然后再进行渲染。
核心是调用了track.py中的eval_seq函数。