作者:Yifu Zhang 等
发表时间:2021
Paper 原文:ByteTrack: Multi-Object Tracking by Associating Every Detection Box
开源代码:ByteTrack
ByteTrack 是基于 tracking-by-detection 范式的跟踪方法。
作者提出了一种简单高效的数据关联方法 BYTE。
它和之前跟踪算法的最大区别在于,并不是简单的去掉低分检测结果,正如论文标题所述,Assiciating Every Detection Box。
利用检测框和跟踪轨迹之间的相似性,在保留高分检测结果的同时,从低分检测结果中去除背景,挖掘出真正的物体(遮挡、模糊等困难样本),从而降低漏检并提高轨迹的连贯性。
速度到 30 FPS(单张 V100),各项指标均有突破。
就我个人 demo 测试来看,相比 deep sort,ByteTrack 在遮挡情况下的提升非常明显。
但是需要注意的是,由于ByteTrack 没有采用外表特征进行匹配,所以跟踪的效果非常依赖检测的效果,也就是说如果检测器的效果很好,跟踪也会取得不错的效果,但是如果检测的效果不好,那么会严重影响跟踪的效果。
ByteTrack 的核心在于 BYTE,也就是说可以套用任何你自己的检测算法,把你的检测结果输入跟踪器即可,和 deepsort 类似,这种方式相比 JDE 和 FairMOT,在工程应用上更为简洁。
前面提到作者保留了低分检测框,直接当做高分检测框处理显然是不合理的,那样会带来很多背景(false positive)。
BYTE 数据关联方法具体的流程如下:
- 根据检测框得分,把检测框分为高分框和低分框,分开处理
- 第一次使用高分框和之前的跟踪轨迹进行匹配
- 第二次使用低分框和第一次没有匹配上高分框的跟踪轨迹(例如在当前帧受到严重遮挡导致得分下降的物体)进行匹配
- 对于没有匹配上跟踪轨迹,得分又足够高的检测框,我们对其新建一个跟踪轨迹。
对于没有匹配上检测框的跟踪轨迹,我们会保留30帧,在其再次出现时再进行匹配
BYTE 的工作原理可以理解为,遮挡往往伴随着检测得分由高到低的缓慢降低:被遮挡物体在被遮挡之前是可视物体,检测分数较高,建立轨迹;当物体被遮挡时,通过检测框与轨迹的位置重合度就能把遮挡的物体从低分框中挖掘出来,保持轨迹的连贯性。
ByteTrack 的检测器部分采用 YOLOX。
在数据关键的部分,和 SORT 一样,只使用卡尔曼滤波来预测当前帧的跟踪轨迹在下一帧的位置,预测的框和实际的检测框之间的 IoU 作为两次匹配时的相似度,通过匈牙利算法完成匹配。
这里值得注意的是 ByteTrack 没有使用 ReID 特征来计算外观相似度,也就是说仅仅使用了运动模型。
为什么没有使用ReID 特征? 作者解释:第一点是为了尽可能做到简单高速,第二点是我们发现在检测结果足够好的情况下,卡尔曼滤波的预测准确性非常高,能够代替 ReID 进行物体间的长时刻关联。
实验中也发现加入 ReID 对跟踪结果没有提升。
根据 ByteTrack 部分的代码整理跟踪算法的流程,如下所示。
作者提供了 demo 接口,直接参考文档即可。
我对比了 ByteTrack(bytetrack_x_mot17.pth.tar) 和 yolov5 + deepsort 的 demo 效果,确实在遮挡情况下的提升比较明显。
但是需要注意的是,yolox_x_mix_det.py 的输入尺寸是 800x1440,然后是单类别的,只能检测人。
虽然官方的 demo 只有检测人,没有提供多类别检测模型,但是应用到多类别跟踪是很容易的,比如可以直接下载 yolox 的官方预训练模型作为检测模型。
但是需要注意一点的是, ByteTrack 检测模型使用的预处理方式和 yolox 官方模型的是不同的,替换模型的时候需要对 ./yolox/data/data_augment.py
中的 preproc 函数替换为原本 yolox 项目中的 preproc 函数即可。
注意这两个函数的接口有差别,替换后调用处修改一下就可以跑了。
替换后效果确实没有原本的好,我觉得可能有两方面的原因,一个是输入尺寸,我替换的是 yolox_x,输入尺寸是 640x640;另一个是 yolox_x 并非专门针对人检测做的训练。
这也再次验证了作者论文中所说的,检测模型很重要。
- Step1 替换
./yolox/data/data_augment.py
中的 preproc 函数为 yolox 中的 preproc 函数。yolox 中的 preproc 函数如下:
def preproc(img, input_size, swap=(2, 0, 1)):
if len(img.shape) == 3:
padded_img = np.ones((input_size[0], input_size[1], 3), dtype=np.uint8) * 114
else:
padded_img = np.ones(input_size, dtype=np.uint8) * 114
r = min(input_size[0] / img.shape[0], input_size[1] / img.shape[1])
resized_img = cv2.resize(
img,
(int(img.shape[1] * r), int(img.shape[0] * r)),
interpolation=cv2.INTER_LINEAR,
).astype(np.uint8)
padded_img[: int(img.shape[0] * r), : int(img.shape[1] * r)] = resized_img
padded_img = padded_img.transpose(swap)
padded_img = np.ascontiguousarray(padded_img, dtype=np.float32)
return padded_img, r
-
Step 2
替换
demo_track.py
中img, ratio = preproc(img, self.test_size, self.rgb_means, self.std)
为如下代码:img, ratio = preproc(img, self.test_size)
-
Step 3 运行 demo
python demo_track.py video -f ../exps/default/yolox_x.py -c ../../weight/yolox_x.pth --fp16 --fuse --save_result
- 知乎:ByteTrack: Multi-Object Tracking by Associating Every Detection Box
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)