简介:本资源面向计算机视觉研究者、算法工程师与相关专业学生,提供一套基于YOLOv7目标检测与多目标跟踪算法的离线比较测试系统,集成SORT、DeepSORT、ByteTrack、Bot-SORT等主流跟踪算法,并采用VisDrone2019无人机航拍数据集进行统一评测,可用于视频监控、自动驾驶与机器人导航等场景下的算法选型与性能验证。压缩包共393个文件,约229.98MB,以134个py源码与204个pyc编译文件为主体,辅以yaml配置、png示意图、mp4演示视频、txt说明及md文档,另含Dockerfile、ui界面文件与pth权重,便于快速部署与二次开发。目前已有107人学习下载。系统提供统一加载数据、运行算法、输出结果与对比分析的完整流程,并附赠算法原理文档与实验数据,帮助读者直观评估各跟踪算法在遮挡、低分辨率等复杂条件下的优势与不足,为算法优化和项目落地提供参考。
1. 无人机视角下的多目标跟踪:这套离线测试平台到底能跑出什么结果
拿到一个压缩包,名字长到得复制粘贴才能看全,第一反应通常是“这又是个拼凑的 demo”。但拆开这个基于 YOLOv7 与 VisDrone2019 的离线测试平台后,我发现它解决的是一个很具体的痛点:在无人机航拍场景下,想把 SORT、DeepSORT、ByteTrack、Bot-SORT 这几个主流跟踪算法放在同一段视频、同一套检测结果上跑一遍,看看到底谁更稳、谁更容易跟丢、谁的 ID Switch 更少。这件事听起来简单,真动手搭过的人都知道,光是统一检测输入格式、对齐评价指标、处理 VisDrone 那种密集小目标,就够折腾一整天。这套资源把检测、跟踪、评估、可视化串成了一条离线流水线,适合做算法对比实验、写论文需要跑 baseline、或者单纯想看看不同跟踪器在航拍视角下真实表现的人。它不依赖在线服务,所有推理都在本地完成,对硬件的要求取决于你选的模型尺寸和视频长度,但整体门槛可控。
2. 拆开压缩包:目录结构与核心模块的职责划分
2.1 从文件树看数据流走向
解压后第一件事不是急着跑命令,而是把目录结构看清楚。这类离线测试平台通常会把检测、跟踪、评估拆成独立模块,中间用统一的中间结果文件衔接。我见过的典型布局是这样的:
project_root/ ├── configs/ │ ├── detector/ │ │ └── yolov7_visdrone.yaml │ ├── tracker/ │ │ ├── sort.yaml │ │ ├── deepsort.yaml │ │ ├── bytetrack.yaml │ │ └── botsort.yaml │ └── eval/ │ └── mot_metrics.yaml ├── data/ │ ├── visdrone/ │ │ ├── sequences/ │ │ └── annotations/ │ └── detections/ # 缓存检测结果,避免重复推理 ├── weights/ │ └── yolov7_visdrone.pt ├── src/ │ ├── detect.py │ ├── track.py │ ├── evaluate.py │ └── visualize.py ├── scripts/ │ ├── run_detection.sh │ ├── run_tracking.sh │ └── run_eval.sh └── requirements.txt这个结构的关键在于data/detections/这个缓存目录。检测模型跑一遍 VisDrone 测试集,把每帧的 bbox、置信度、类别存成 json 或 txt,后面切换跟踪算法时直接读缓存,不用反复过 YOLOv7。我一般会先确认这个目录是否存在,如果脚本里没有自动创建,手动补上,否则第一次跑跟踪会报路径错误。
2.2 检测与跟踪的解耦设计
为什么要把检测和跟踪拆开?因为跟踪算法的输入本质上就是“带时序的检测框”。SORT 系列算法只依赖 bbox 和 IoU,DeepSORT 额外需要外观特征,ByteTrack 对低分框有二次关联逻辑,Bot-SORT 则融合了运动与外观。如果每次换跟踪器都重新跑一遍 YOLOv7,实验效率会低到无法忍受。这套平台的做法是:detect.py负责生成统一的检测缓存,track.py通过--tracker参数选择算法,读取缓存后只跑跟踪逻辑。
# 第一步:生成检测缓存,只跑一次 python src/detect.py \ --weights weights/yolov7_visdrone.pt \ --source data/visdrone/sequences \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt \ --project data/detections # 第二步:切换跟踪算法,复用缓存 python src/track.py \ --detections data/detections \ --tracker botsort \ --config configs/tracker/botsort.yaml \ --save-vid--conf-thres和--iou-thres是检测阶段的关键参数。VisDrone 里小目标密集,置信度阈值设太高会漏掉大量行人、车辆,设太低又会引入大量误检,跟踪器会被噪声带偏。我的经验是先在 0.25 附近试,看可视化结果里漏检和误检的平衡点。--save-txt必须开,否则缓存不落地,后面跟踪脚本读不到东西。
2.3 跟踪器配置文件的参数含义
每个跟踪器对应一个 yaml 配置,里面藏着影响 ID Switch 和 MOTA 的核心参数。以 ByteTrack 为例,常见配置项包括:
| 参数 | 含义 | 典型值 | 调参影响 |
|---|---|---|---|
| track_thresh | 高分框阈值 | 0.5 | 降低可召回更多目标,但误跟增加 |
| match_thresh | 关联匹配阈值 | 0.8 | 提高会减少 ID Switch,但可能跟丢 |
| track_buffer | 轨迹保留帧数 | 30 | 增大可应对长遮挡,内存占用上升 |
| min_box_area | 最小框面积 | 100 | 过滤极小噪声框 |
| mot20 | 是否启用 MOT20 模式 | False | 密集场景可开启 |
DeepSORT 的配置里还会多出max_dist、nn_budget这类外观特征相关参数。Bot-SORT 则涉及proximity_thresh、appearance_thresh和with_reid开关。我一般不会一上来就大改这些值,而是先用默认配置跑一遍,看评估指标里哪项最差,再针对性调整。比如 ID Switch 高,优先动match_thresh和track_buffer;MOTA 低但 IDF1 还行,可能是检测漏检拖累的,得回头调检测阈值。
3. 跑通全流程:从检测缓存到跟踪评估的实操步骤
3.1 环境准备与依赖安装
这类项目通常依赖 PyTorch、OpenCV、NumPy、SciPy,以及跟踪算法各自的实现库。requirements.txt里一般会列全,但版本冲突是常态。我习惯先建一个干净的虚拟环境,再按顺序装:
conda create -n visdrone_mot python=3.8 -y conda activate visdrone_mot pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html pip install -r requirements.txtPyTorch 版本要和 CUDA 驱动匹配,否则 YOLOv7 推理会直接报错。如果机器上没有 GPU,把+cu113换成 CPU 版本也能跑,只是 VisDrone 序列较长时速度会慢到影响实验节奏。装完后用python -c "import torch; print(torch.cuda.is_available())"确认一下,返回 True 再往下走。
3.2 检测阶段:YOLOv7 在 VisDrone 上的推理与缓存
VisDrone2019 的测试集包含多个序列,每个序列是一段连续帧。检测脚本需要按序列逐帧推理,并把结果按帧号存好。常见做法是每帧一个 txt,命名与帧号对应,内容格式为class_id x_center y_center width height confidence,坐标归一化到 0-1。
# detect.py 核心逻辑示意 import cv2 import torch from pathlib import Path def run_detection(weights, source_dir, conf_thres, iou_thres, save_dir): model = torch.load(weights, map_location='cuda')['model'].float() model.eval() for seq_path in sorted(Path(source_dir).iterdir()): if not seq_path.is_dir(): continue seq_name = seq_path.name out_dir = Path(save_dir) / seq_name out_dir.mkdir(parents=True, exist_ok=True) for img_path in sorted(seq_path.glob('*.jpg')): img = cv2.imread(str(img_path)) # 预处理、推理、NMS dets = model(img, conf_thres, iou_thres) frame_id = img_path.stem with open(out_dir / f'{frame_id}.txt', 'w') as f: for det in dets: f.write(f'{det.class_id} {det.x} {det.y} {det.w} {det.h} {det.conf}\n')这段代码的关键点在于:model.eval()必须调用,否则 BN 层和 Dropout 会干扰推理结果;NMS 的 IoU 阈值直接影响密集小目标的保留数量;输出坐标要归一化,因为后续跟踪器通常按归一化坐标计算 IoU。如果检测结果里出现大量重叠框,先把iou_thres从 0.45 降到 0.4 试试。
3.3 跟踪阶段:切换四种算法的命令与输出差异
检测缓存就绪后,切换跟踪器只需要改--tracker和对应的配置文件。下面以同一段序列为例,展示四种算法的调用方式:
# SORT:纯运动模型,速度最快 python src/track.py --detections data/detections/VisDrone2019-MOT-test \ --tracker sort --config configs/tracker/sort.yaml --save-vid # DeepSORT:加入外观特征,ID Switch 通常更低 python src/track.py --detections data/detections/VisDrone2019-MOT-test \ --tracker deepsort --config configs/tracker/deepsort.yaml --save-vid # ByteTrack:利用低分框二次关联,密集场景召回更好 python src/track.py --detections data/detections/VisDrone2019-MOT-test \ --tracker bytetrack --config configs/tracker/bytetrack.yaml --save-vid # Bot-SORT:运动+外观融合,综合表现较稳 python src/track.py --detections data/detections/VisDrone2019-MOT-test \ --tracker botsort --config configs/tracker/botsort.yaml --save-vid跑完后每个算法会输出一份跟踪结果文件,通常是 MOT 格式:frame_id, track_id, x, y, w, h, conf, -1, -1, -1。这个格式可以直接喂给评估脚本。我一般会先把四份结果都跑出来,再用可视化脚本生成对比视频,肉眼过一遍,心里有个底,再去看指标。
3.4 评估阶段:MOTA、IDF1、ID Switch 的计算与解读
评估脚本读取跟踪结果和 VisDrone 的 GT 标注,计算 MOTA、IDF1、ID Switch、FP、FN 等指标。VisDrone 的标注格式和 MOT Challenge 略有差异,常见做法是先用转换脚本把 GT 转成 MOT 格式,再调用motmetrics或py-motmetrics库。
import motmetrics as mm def evaluate(tracker_output_dir, gt_dir): acc = mm.MOTAccumulator(auto_id=True) for seq in sorted(Path(gt_dir).iterdir()): gt = load_mot_gt(seq) pred = load_mot_pred(tracker_output_dir / seq.name) for frame_id in sorted(gt.keys()): acc.update( gt[frame_id]['ids'], pred[frame_id]['ids'], mm.distances.iou_matrix(gt[frame_id]['boxes'], pred[frame_id]['boxes'], max_iou=0.5) ) mh = mm.metrics.create() summary = mh.compute(acc, metrics=['mota', 'idf1', 'id_switches', 'num_false_positives', 'num_misses']) print(summary)max_iou=0.5是匹配阈值,低于这个 IoU 的检测框和 GT 不视为同一目标。VisDrone 小目标多,这个值可以适当降到 0.4 或 0.45,否则匹配数会偏低,MOTA 被低估。ID Switch 高说明跟踪器在目标交叉或遮挡后没能保持同一 ID,这时候要回头看跟踪配置里的track_buffer和match_thresh。
4. 避坑与排查:跑这套平台时最容易翻车的五个地方
4.1 检测缓存路径不匹配导致跟踪脚本空跑
现象:跟踪脚本运行后没有报错,但输出视频里没有任何框,或者结果文件为空。原因:track.py默认读取的缓存路径和detect.py实际保存的路径不一致,常见于脚本里硬编码了data/detections但检测时用了--project改了输出目录。解决:先确认detect.py的输出目录,再用--detections显式指定给track.py。跑之前ls一下缓存目录,确认每个序列子目录下都有 txt 文件。
4.2 VisDrone 标注格式转换时类别映射错位
现象:评估指标异常低,MOTA 为负数,或者 FP 数量远大于预期。原因:VisDrone 的类别编号和 MOT Challenge 不一致,转换脚本里如果直接复用类别 ID,会导致行人、车辆等类别错位,匹配时大量 GT 被当成背景。解决:检查转换脚本里的类别映射表,VisDrone 的pedestrian对应 MOT 的1,car对应3,其他类别按需映射或忽略。转换后抽几帧可视化,确认框和类别对得上。
4.3 置信度阈值设太高导致小目标漏检
现象:跟踪结果里目标数量明显少于 GT,FN 很高,MOTA 被拉低。原因:VisDrone 里大量目标像素面积小于 32x32,YOLOv7 在默认conf-thres=0.25下已经会漏掉一部分,如果调到 0.5 以上,漏检更严重。解决:把conf-thres降到 0.15-0.2,同时观察 FP 是否可接受。如果 FP 增长太快,可以配合min_box_area过滤极小框,或者在跟踪阶段用 ByteTrack 的低分框关联逻辑兜底。
4.4 DeepSORT 外观特征提取耗时过长
现象:DeepSORT 跑一个序列要几十分钟,其他跟踪器只要几分钟。原因:DeepSORT 每帧对每个检测框都要过一遍 ReID 网络提取外观特征,VisDrone 单帧目标数多,累积耗时爆炸。解决:换用轻量 ReID 模型,或者把nn_budget调小,限制特征库大小。如果只是做算法对比,可以先用 SORT 和 ByteTrack 跑通流程,DeepSORT 和 Bot-SORT 放在最后跑,避免卡在特征提取上。
4.5 评估时序列顺序错乱导致 ID 跳变
现象:ID Switch 指标异常高,但可视化视频里看起来跟踪挺稳。原因:评估脚本读取跟踪结果时没有按帧号排序,或者多个序列的结果混在一起,导致同一 track_id 在不同序列间被错误关联。解决:确保每个序列单独评估,结果文件按帧号排序后再喂给motmetrics。可以在load_mot_pred里加一行sorted(pred.items(), key=lambda x: x[0]),成本极低但能避免很多玄学问题。
5. 进阶技巧:用可视化对比和参数扫描把结论坐实
跑完一轮默认配置,拿到四份指标表,事情只完成了一半。真正让这套平台发挥价值的,是带着问题去扫参数、做可视化对比。我一般会先固定检测缓存不变,只动跟踪器的match_thresh和track_buffer,每个算法扫三到五组值,看 ID Switch 和 MOTA 的变化趋势。比如 ByteTrack 的match_thresh从 0.8 提到 0.9,ID Switch 通常会降,但 MOTA 可能因为跟丢而掉,这个权衡点得靠实验找。
可视化方面,平台自带的visualize.py通常支持把 GT 和跟踪结果画在同一帧上,用不同颜色区分。我习惯把四个算法的输出视频并排拼成一个网格,逐帧对比。重点看三种场景:目标交叉、目标被遮挡后重现、目标从画面边缘进出。这三种场景下 ID 是否保持,比整体指标更能说明问题。
# 生成四宫格对比视频 python src/visualize.py \ --videos outputs/sort.mp4 outputs/deepsort.mp4 outputs/bytetrack.mp4 outputs/botsort.mp4 \ --layout 2x2 \ --output outputs/comparison.mp4如果平台没有现成的拼接脚本,用 ffmpeg 也能快速搞定:
ffmpeg -i sort.mp4 -i deepsort.mp4 -i bytetrack.mp4 -i botsort.mp4 \ -filter_complex "[0:v][1:v]hstack[top];[2:v][3:v]hstack[bottom];[top][bottom]vstack" \ -c:v libx264 -crf 23 outputs/comparison.mp4还有一个小技巧:把检测缓存里的低分框单独导出来看一眼。ByteTrack 之所以在密集场景下表现好,很大程度上是因为它把低分框也拿来做二次关联。如果 VisDrone 序列里低分框数量占比很高,ByteTrack 的优势会更明显;反之,如果低分框大多是噪声,ByteTrack 反而可能引入误跟。这个判断不需要跑完整评估,统计一下缓存里conf < 0.5的框占比就能有个大致预期。
从那以后我每次跑这类多跟踪器对比实验,都会先把检测缓存固定下来,再逐个扫跟踪参数,最后用可视化视频交叉验证指标。指标告诉你哪个数好,视频告诉你为什么好、什么时候会翻车。希望帮到你。
本文还有配套的精品资源,点击获取