YOLOv5+DeepSORT实现车辆行人追踪计数的完整实战
简介面向人工智能与深度学习方向的毕业设计源码基于YOLOv5与DeepSORT实现车辆与行人的实时检测、追踪及计数。整套项目定位为课程设计与毕业设计解决方案适用于具备一定Python与深度学习基础的学习者用来快速掌握目标检测、多目标跟踪与流量统计的工程落地流程。压缩包共120个文件大小约129.68MB核心代码以Python为主包含37个py脚本与58个pyc编译文件另有模型权重pt、网络配置yaml及dockerfile、sh等部署脚本同时附带测试视频mp4、说明文档md和演示图片jpg结构清晰便于直接运行与二次开发已有232人学习。源码经过本地编译可运行评审分达95分以上难度适中内容经过助教审定特别适合毕业设计选题参考。借助该资源读者可以获得一套完整的车辆行人追踪计数示例包括YOLOv5检测模型、DeepSORT跟踪算法、计数逻辑与可视化输出能够在此基础上进行算法改进与功能扩展。1. 车辆行人追踪为什么一定是 YOLOv5 DeepSORT如果在路口监控里只做目标检测不做跟踪YOLOv5 的输出始终是若干离散的框上一秒还是“第 7 辆车”下一秒就变成另一个“第 8 辆车”车辆行人追踪和计数完全没有意义。把 YOLOv5 与 DeepSORT 串成一条检测-跟踪链路后系统才能稳定输出“某辆车从东往西穿过停止线”这样的结构化数据。这个组合不是最前沿的却是 GTX 1660 到 3090 上最容易跑实时、资料最全、且能支撑毕业设计答辩的路线。无论只是要快速交付可运行的毕业设计还是想把检测、跟踪、计数的完整链路吃透这个项目都值得打开看。2. 拆解 YOLOv5 网络结构与 DeepSORT 跟踪链路2.1 YOLOv5 的 Backbone、Neck 和 HeadYOLOv5 把检测任务拆成“哪里可能有目标”和“目标是什么”两步。Backbone 使用 CSPDarknet 架构通过跨阶段残差结构让梯度在深层网络中更容易流动Neck 采用 FPNPAN 的组合把高层语义信息传回浅层再把浅层定位信息往下融合最终形成三个不同尺度的特征图。以 yolov5l.pt 对应的模型为例三个检测层分别作用在输入图像的 8 倍、16 倍、32 倍下采样位置小行人在 P3 层更容易被召回大货车在 P5 层得到更充分的识别。这个结构也是后来很多方案愿意继续用 YOLOv5 的原因结构简单每一个模块都能在代码里线性找到。从模型配置里可以看到三个检测层的 anchors 分布anchors: - [10,13, 16,30, 33,23] # P3/8负责小目标 - [30,61, 62,45, 59,119] # P4/16负责中目标 - [116,90, 156,198, 373,326] # P5/32负责大目标这里 anchors 是预设候选框的宽高比。训练时模型会学习在每个特征图上偏移这几个 anchor而不是从任意位置回归。毕业设计场景里不需要改动 anchors但要知道如果视频画面里行人的像素跨度特别小可以优先把img参数从 640 提到 1280而不是去改 anchors因为anchors改不好会直接让 mAP 下跌。yolov5l.pt这个文件已经包含训练好的 COCO 权重默认能识别 80 类其中车辆和行人对应类别 2、5、7 和 0解压后不要随意删。实际做课程设计时YOLOv8 的检测精度略高但 DeepSORT 生态里大量代码、教程和已调好的权重都围绕 YOLOv5 展开YOLOv5 的 detect.py 与 track.py 分离适合把检测结果单独导出再灌给 DeepSORT。资源里的 datasets.py 和 general.py 也是顺着这条调用链组织的所以用 YOLOv5 做车辆行人追踪和计数资料闭环度比新框架高得多。2.2 DeepSORT 的卡尔曼滤波与级联匹配DeepSORT 不负责找目标只负责把同一目标在不同帧的检测框串起来。每个轨迹用一个卡尔曼滤波器维护 8 维状态向量包括框的中心坐标、宽高比、高度以及它们的一阶导数。预测阶段用匀速模型估计下一帧位置更新阶段把 YOLOv5 当前帧检测框与轨迹做关联。关联不是简单的 IoU 匹配。DeepSORT 采用级联匹配策略优先匹配最近连续丢帧的轨迹再用马氏距离衡量运动相似度用余弦距离衡量外观特征相似度最后按加权距离做匈牙利分配。这样当车辆被路灯杆短暂遮挡又重新出现时外观特征能把它拉回原来的 ID如果只按 IoU 匹配ID 很容易在遮挡瞬间跳变。初始化时一般这样定参数from deep_sort.deep_sort import nn_matching from deep_sort.deep_sort.tracker import Tracker metric nn_matching.NearestNeighborDistanceMetric( cosine, max_cosine_distance0.2, nn_budget100 ) tracker Tracker(metric, max_iou_distance0.7, max_age30, n_init3)max_cosine_distance控制外观特征相似度的容忍上限超过 0.2 就认为不是同一个目标nn_budget是保存历史外观向量的数量预算太小长期遮挡后重新出现的目标会丢失特征max_age是轨迹丢帧 30 次仍未恢复就会被删除n_init是轨迹至少连续匹配 3 帧才从预备状态转为确认状态。这几个参数直接决定了后面车辆行人追踪和计数的稳定性第 4 章会结合现象说怎么调。2.3 轨迹状态管理与计数前置条件计数要稳定不能把检测框直接当计数对象。DeepSORT 用三种状态管理一条轨迹预备态、确认态和删除态。只有确认态轨迹的框中心点才应该进入计数逻辑。资源压缩包里保留了 general.py、utils.py、datasets.py 等文件正是因为 YOLOv5 和 DeepSORT 的调用链都依赖这些公共工具函数比如把 xyxy 转 xywh、读取类别名、画框等。下面这张表是实际调试时判断轨迹状态会不断对照的依据轨迹状态出现条件是否参与计数Tentative连续匹配帧数不足 n_init否Confirmed连续匹配成功达到 n_init是Deleted超过 max_age 未匹配否拿到 tracker.update 返回后需要显式过滤确认态轨迹再做计数confirmed_tracks [track for track in tracker.tracks if track.is_confirmed()] for track in confirmed_tracks: ltrb track.to_ltrb() class_id track.get_det_class() center ((ltrb[0] ltrb[2]) // 2, (ltrb[1] ltrb[3]) // 2) # 这里 center 才会交给虚拟线判断to_ltrb()返回左上右下坐标get_det_class()在常见实现里会保留这条轨迹最近一次匹配的检测类别。这样计数模块不用关心底层状态机只认 Confirmed 轨迹误检造成的闪现框就不会进入计数。3. 搭好 yolov5 环境配置再训练自己的数据集完成追踪3.1 依赖版本与 CUDA 选择解压 zip 后第一件事不是直接运行而是先把 yolov5 环境配置理清楚。这个项目里同时存在 YOLOv5 和 DeepSORT 的依赖容易出现 torch 版本不匹配。单人复现时我一般用 Python 3.8 配 PyTorch 1.12 的组合CUDA 11.3 以上都可以。把下面这段写成 requirements 后集中安装torch1.12.1 torchvision0.13.1 opencv-python4.1.2 numpy1.19.5 scipy1.4.1安装命令cd yolov5_deepsort-master pip install -r requirements.txt需要说明的是yolov5l.pt和 DeepSORT 的ckpt.t7都是独立权重文件requirements 只负责运行时库。如果电脑没有 NVIDIA 显卡把 torch 换成 CPU 版本也能跑但test.mp4在 640 分辨率下可能只有 2 到 3 FPS做演示可以做实时计数会吃力。另一个常见坑是 Python 3.12 下torch旧版本装不上直接换 3.10 环境最省事。此外项目根目录有一份 Dockerfile适合老师要求统一环境时使用但本地调试不建议先扑在 Docker 里镜像构建时间通常比装本地环境长很多。3.2 数据集准备与界面自动标注如果要训练自己的数据集而不是直接用 COCO 权重第一步是拿到带标签的图片。手头没有标签时可以先让已经训练好的 YOLOv5 做一轮预标注再用标注软件在界面里修正。常见做法是这样跑python detect.py --source raw_frames/ --weights yolov5l.pt --save-txt --conf 0.2--save-txt会让每个检测结果生成同名 txt 文件格式是class x_center y_center width height正是 YOLOv5 训练需要的格式。一个典型的标注文件里可能长这样2 0.684375 0.415625 0.125000 0.312500 0 0.428125 0.437500 0.093750 0.406250第一行类别是 2表示 car后四个数分别是归一化后的中心坐标和宽高。随后用 LabelImg 打开raw_frames目录界面里能看到自动画好的框人工只需要补漏检和删误检。这一段流程就是“如何通过界面操作 yolov5 完成数据集的自动标注”的典型实现能省下两到三天的纯手工标注时间。注意--conf 0.2要开得低一点预标注时漏框比错框更麻烦后续人工逐个画新框的成本高于改已有框。3.3 训练自己的数据集与模型参数把标注结果整理成 YOLOv5 标准的目录结构datasets/ vehicle/ images/train/... images/val/... labels/train/... labels/val/...然后写一个vehicle.yamltrain: ./datasets/vehicle/images/train val: ./datasets/vehicle/images/val nc: 4 names: [car, truck, bus, person]训练命令按“yolov5 模型训练教程”里最常用的方式执行python train.py --data vehicle.yaml --weights yolov5l.pt --batch-size 8 --epochs 100 --img 640--weights yolov5l.pt表示从 COCO 预训练权重继续微调比--weights 从头训练收敛快得多尤其车辆行人这类目标COCO 里已经有大量相似样本100 个 epoch 足够看到效果。--batch-size 8是 8G 显存能稳定跑完的数值显存只有 6G 就把--img降到 480或者加--half用半精度训练。训练完成后权重会保存在runs/train/exp/weights/best.pt这个文件后面会替换掉yolov5l.pt作为检测模型。3.4 运行 demo 与 DeepSORT 权重资源里自带demo.jpg和test.mp4说明作者已经预留了快速验证入口。常见实现里运行追踪计数的命令是python track.py --source test.mp4 --yolo-weights yolov5l.pt --show --save-vid --classes 0 2 5 7--classes 0 2 5 7对应 COCO 数据集里的 person、car、bus、truck只让检测器输出这四类能减少 DeepSORT 的无效关联计算计数结果也更干净。DeepSORT 侧通常还需要一个 ReID 特征提取器权重默认路径是deep_sort/deep_sort/deep/checkpoint/ckpt.t7。这个文件不一定要自己训练常见实现会提供一个在行人重识别数据集上预训练好的权重如果项目压缩包里缺少该文件去对应开源仓库找同名权重即可。启动后按下空格暂停逐帧看框上方的 ID如果同一辆车的 ID 能连续保持 10 帧以上说明跟踪链路已经通了。4. 追踪计数实现的关键代码与参数调优4.1 检测框转跟踪框的关键流程YOLOv5 的原始输出是(x1, y1, x2, y2, conf, cls)而 DeepSORT 的update接口要求的是(x, y, w, h, conf, cls)构成的列表。常见实现会在主循环里写这样一段from utils.general import xyxy2xywh all_dets [] for *xyxy, conf, cls in outputs: x1, y1, x2, y2 map(int, xyxy) if int(cls) not in allowed_classes: continue if float(conf) conf_thres: continue x_center, y_center, w, h xyxy2xywh(xyxy).tolist() all_dets.append(([x_center, y_center, w, h], float(conf), int(cls))) tracks tracker.update(all_dets)这里allowed_classes是希望追踪的类别集合conf_thres是检测置信度阈值。逻辑的顺序很重要先过滤类别和低置信度框再送入 tracker不然 DeepSORT 会把大量背景误检也纳入关联轨迹会被频繁打断。有人直接把 YOLOv5 的xyxy2xywh结果传进去但漏掉了conf和cls会导致 DeepSORT 无法区分类型车辆和行人的轨迹串在一起计数结果完全失真。4.2 虚拟线与区域计数的实现追踪计数最常用的两种方案是虚拟线和进出区域。虚拟线适合单向或双向过车计数区域计数适合统计某个路口或园区内当前有多少人。毕业设计里用虚拟线更直观。判断轨迹是否跨线可以用两次检测中心点在线段两侧的叉积符号变化def cross_direction(pt_a, pt_b, line_start, line_end): s_a (pt_a[0] - line_start[0]) * (line_end[1] - line_start[1]) - ( pt_a[1] - line_start[1]) * (line_end[0] - line_start[0] ) s_b (pt_b[0] - line_start[0]) * (line_end[1] - line_start[1]) - ( pt_b[1] - line_start[1]) * (line_end[0] - line_start[0] ) if s_a * s_b 0: return 1 if s_b 0 else -1 return 0代码里pt_a是上一帧确认轨迹中心点pt_b是当前帧中心点line_start和line_end是虚拟线端点。叉积符号从正到负或从负到正说明目标越过了直线符号变化方向还能给出运动方向正好用来区分进和出。需要注意这里不能用检测框的左上角因为行人靠近镜头时框会上下抖动中心点相对稳定车辆被遮挡后跟踪框可能短暂漂移所以还要再叠加一个“连续两次有效跨线间隔至少 5 帧”的判断防止同一个 ID 在线附近抖动造成重复计数。如果是区域计数逻辑更简单每条确认轨迹在每一帧都有中心点坐标只要判断中心点是否落在多边形内进入时加一、离开时减一。OpenCV 提供cv2.pointPolygonTest可以直接做点与多边形的位置判断不需要自己写射线法。4.3 yolov5 超参数与 DeepSORT 参数的配合调优面对计数异常时先区分问题出在检测端还是跟踪端。如果同一辆车几帧内框不见了优先检查 yolov5 超参数如果框一直在但 ID 从 7 变成 19优先调 DeepSORT 参数。下面的表是实际调试时最常用的对照关系现象调整方向远处小行人漏检调低--conf到 0.05--img提到 1280ID 频繁切换max_cosine_distance从 0.2 降到 0.15nn_budget提到 150遮挡后目标丢失max_age从 30 提到 60同时保证n_init不超过 3计数速度慢于实时传递--classes过滤无关目标检测分辨率降到 640车辆与行人轨迹串 ID在构造检测列表时保留 cls 并参与关联调参基线一般放在track.py的可视化参数区conf_thres0.25、iou_thres0.45、max_cosine_distance0.2。先保持默认跑一段test.mp4记录现象再每次只改一个参数。把conf_thres调太低会引入大量误检DeepSORT 虽然能滤掉部分孤立点但密集场景下误检也会形成短轨迹所以最终阈值往往在 0.05 到 0.15 之间折中max_age调太大会让已经离开画面的车辆 ID 长时间保留在轨迹列表里计数模块如果不清除已删除轨迹会把这些离开车辆继续算成在场数量。建议把test.mp4剪成 30 秒的小片段固定片段调参每次改动只记录片段末尾的计数数字而不是整段视频跑完再看。5. 排错、验证与迭代技巧5.1 三个高频报错点第一是 CUDA out of memory。直接降低--batch-size到 4 或 2或者--img 480再加--half。第二是找不到ckpt.t7。检查是否把权重文件放在deep_sort/deep_sort/deep/checkpoint/下路径里多一层目录是最常见原因。第三是AttributeError: module utils has no attribute ...。这通常是因为当前工作目录混入了另一个 YOLOv5 工程导致导入了同名utils.py。处理办法是把本项目单独放在一个目录里执行不要和别的仓库共用一个工作根目录。5.2 计数结果的人工验证在交毕设前要用人工计数给程序输出做个对标。我的做法是在test.mp4里截取一段连续 60 秒暂停播放人工数出车辆和行人数量再对比程序输出。验证指标只需要两个检出召回率和计数误差率。可以快速用一个脚本统计manual {vehicle: 37, person: 22} counted {vehicle: 35, person: 21} err {k: abs(counted[k] - manual[k]) / manual[k] for k in manual}err在 5% 以内可以接受超过 10% 就要回到第 4 章查是漏检还是 ID 切换造成的重复计数。注意人工数的是“逻辑上通过的人数”程序数的是“轨迹数”如果一个人走进画面又走出去再回来程序会按两条轨迹计所以误差不一定全是 bug。5.3 演示效果优于精度的三个小技巧答辩演示时画面观感比最终 mAP 更重要。可以用颜色区分车辆和行人轨迹线上方显示累计计数把虚拟线画成约 3 像素宽的亮色线段这样截图和录屏都能看出计数的触发位置。夜间场景不要急着换模型先把--img提到 1280再关闭--agnostic-nms避免类别间的 NMS 互相压制多数情况能提升行人召回。最后记住一个经验数据集标注质量对跟踪计数的影响通常大于模型结构差异与其纠结要不要换 YOLOv8不如把自动标注里重叠框清理干净。比如在 640 分辨率下虚拟线坐标写歪 2 个像素夜间计数结果波动就会很大先把坐标标在 1280 再缩放回 640往往比换模型提分更快。本文还有配套的精品资源点击获取