YOLOv5+DeepSORT车辆行人追踪计数实战与避坑指南
简介基于YOLOv5与DeepSORT的车辆行人追踪与计数项目源码面向毕业设计、期末大作业和计算机视觉初学者主要解决监控视频中多目标实时检测、跨帧稳定跟踪以及进出数量统计的问题。资源包共78个文件压缩后82.69MB其中50个Python脚本构成核心逻辑覆盖检测、追踪、计数及工具函数另有yaml/xml配置文件、Shell自动化脚本、Markdown/Text说明文档、预训练模型pt/t7以及测试视频便于快速理解与部署。项目代码结构清晰包含单独的检测器、追踪器和完整DeepSORT配置关键位置附有注释并配备详细使用说明新手也可按步骤运行。通过YOLOv5进行目标识别、DeepSORT实现轨迹关联可稳定统计车流量与人流量支持自定义视频输入具备较高的工程参考价值。目前已有246人学习使用适合作为本科毕业设计或课程设计的直接参考。1. 为什么“YOLOv5DeepSORT 车辆行人追踪和计数”值得你完整跑一遍如果你也是冲着“YOLOv5DeepSORT 车辆行人追踪和计数”这几个关键词来的大概率是手里攥着一个毕设题目要在视频里同时看到检测框、ID 编号和实时计数而不是只跑通一个目标检测 demo。这份源码把“检测—追踪—计数”闭环做完整了检测器负责找“人”和“车”DeepSORT 负责给每个目标一个稳定不跳变的 track_id计数逻辑再基于 track_id 去重统计输出最终结果。它适合两类人——第一类是毕业设计/课程设计要交演示视频和说明文档的学生第二类是要在监控视频上快速验证车流人流方案的从业者。下面按我拆包的习惯来写先讲三段链路各自在做什么再讲环境、参数、计数逻辑最后把踩过的坑列出来。2. 先看透这份资源的三段链路检测、追踪与项目结构2.1 检测器为什么锁 YOLOv5精度、速度与后处理的平衡点车辆行人追踪的第一步是“找到目标”这步由检测器完成。YOLOv5 在 COCO 数据集上训练后自带 80 类目标其中 person行人、car、truck、bus、bicycle、motorbike 这六类正好覆盖交通场景。它的核心输出是带置信度的检测框但原始输出不能直接用——模型会一次性给出几千个候选框其中大量是重叠框必须经过 NMS非极大值抑制清洗。这一步就是常说的“yolov5 后处理”你可能在训练或推理脚本里见过--conf-thres和--iou-thres两个参数它们都在管 NMS。参数默认值作用误设后的表现conf-thres0.25置信度阈值低于此值的框直接丢掉阈值调太低会冒出一堆误检框调太高会漏掉行人iou-thres0.45NMS 时两个框的重叠度超过此值就合并调太高同一辆车会出多个框调太低相邻车被合并这份资源的检测权重一般是yolov5s.pt或自训练的best.pt模型在运行时会做一次 640x640 的 resize 再送入网络输出经过 NMS 后变成“干净的检测框列表”这些框连同置信度、类别一起作为 DeepSORT 的输入。如果你把检测端单独拆出来看它跟一个普通 YOLOv5 推理脚本没有区别真正的难点在于把“若干帧各自独立的检测结果”关联成“同一条轨迹”这就要看追踪器了。2.2 DeepSORT 补上检测没有的“身份”卡尔曼滤波、匈牙利匹配与外观特征YOLOv5 每帧输出的框是独立的上一帧的车和这一帧的车在代码层面没有任何关系DeepSORT 要解决的就是“跨帧关联”。它的工作分三段卡尔曼滤波预测每条轨迹在下一帧的位置再用匈牙利算法把当前帧检测框和已有轨迹做最优匹配最后用外观特征一个轻量 ReID 网络提取的 128 维向量解决遮挡后身份丢失的问题。这三个模块听起来玄学实际就是deep_sort包里的tracker.update(detections)这一行做的事。这份资源的核心参数集中在configs/deep_sort.yaml几个关键值值得你在改之前先弄清楚# deep_sort.yaml 关键参数参考 MAX_DIST: 0.2 # 马氏距离门控阈值用于运动匹配 MAX_IOU_DISTANCE: 0.7 # 匹配时允许的最大 IoU 距离 MAX_AGE: 30 # 轨迹丢失后保留的帧数超过则删除 N_INIT: 3 # 连续命中多少帧才确认一条新轨迹 NN_BUDGET: 100 # 外观特征池最大容量 MAX_COSINE_DISTANCE: 0.3 # 外观特征余弦距离阈值参数含义先说清楚MAX_AGE是“后悔药”车辆被完全遮挡时轨迹不会立刻消失而是保留 30 帧等待重新匹配N_INIT是“观察期”前 3 帧检测到的物体先处于未确认状态避免单次误检就产生一条假轨迹MAX_COSINE_DISTANCE决定“看起来像不像”阈值越小要求越严格车辆换外形后容易出现轨迹断裂。网上说的“deepsort 改进”大多改的是这三块的组合策略比如换更强的 ReID 网络、在匹配阶段加运动预测这份资源用的还是经典配置稳定优先。2.3 项目文件结构与启动入口拿到压缩包先看这两个地方下载解压后我建议你别急着敲命令先把目录结构过一遍。毕设类源码最常见的坑是“路径写死”和“模型文件缺失”先看结构能省掉后面两个小时的排错时间。project_root/ ├── deep_sort/ # 追踪器源码包 │ ├── deep/ # ReID 特征提取与 tracker 实现 │ ├── sort/ # 卡尔曼滤波与匹配逻辑 │ └── configs/ │ └── deep_sort.yaml # 追踪参数 ├── models/ # YOLOv5 模型定义与权重 │ └── yolov5s.pt ├── utils/ # 通用工具函数 ├── track_and_count.py # 主入口检测追踪计数都在这里 ├── requirements.txt # 依赖清单 ├── data/ # 测试视频与输出目录 └── README.md # 使用说明毕设评审前必读这个结构里优先级最高的是README.md和track_and_count.py。前者通常会写明环境版本和启动命令后者是唯一需要你真正读懂的主脚本——从track_and_count.py能看到加载权重、调用 tracker、画轨迹、做计数的完整调用链后面几章提到的参数在它身上都能找到对应位置。如果你拿到的包目录名不同按“找主脚本、找 yaml 配置、找权重”这个顺序去对应即可。3. 环境配置与权重准备90% 的问题发生在这一步3.1 用 conda 建环境Python 版本和 PyTorch 版本别乱选yolov5 环境配置是网上提问最多的地方核心矛盾就两个Python 版本太新装不上依赖PyTorch 版本和 CUDA 不匹配导致 GPU 不可用。我一般建议用 conda 单独建一个环境Python 锁在 3.8 或 3.9不要直接往系统 Python 里塞。# 创建独立环境Python 3.8 兼容性最好 conda create -n yolo_deepsort python3.8 -y conda activate yolo_deepsort # 先装 PyTorch再装项目依赖 # CPU 版没有 N 卡或不想折腾 CUDA 时用 pip install torch1.13.1cpu torchvision0.14.1cpu -f https://download.pytorch.org/whl/torch_stable.html # GPU 版示例CUDA 11.7 对应 torch 1.13.1cu117 pip install torch1.13.1cu117 torchvision0.14.1cu117 -f https://download.pytorch.org/whl/torch_stable.html # 安装项目依赖 pip install -r requirements.txt这段命令的逻辑是“先固定 PyTorch 再装其余依赖”顺序反了会遇到包版本冲突。CPU 版适合只想看效果、视频不超过 720p 的用法GPU 版适合后续要训练自己数据集的场景。装完pip list | grep torch确认版本然后python -c import torch; print(torch.cuda.is_available())输出 False 也别慌后面避坑章节会讲原因。requirements.txt 里常见的坑是 opencv-python 版本过高导致cv2.VideoWriter编码异常如果装完跑起来输出视频打不开回退到opencv-python4.5.4.60基本能解决。3.2 权重文件与数据路径先做一次“存在性检查”再跑毕设源码运行失败排第一的原因不是代码错了而是权重文件路径不对、视频文件不存在或者目录里有中文。主脚本track_and_count.py默认从models/yolov5s.pt加载检测权重从deep_sort/deep/checkpoint/ckpt.t7加载 ReID 权重这两个文件缺一个就会直接报错。视频路径建议放在data/下并用英文命名比如data/test_video.mp4别用“测试视频.mp4”这种命名。# 启动前检查关键文件缺哪个一目了然 ls -lh models/yolov5s.pt ls -lh deep_sort/deep/checkpoint/ckpt.t7 ls -lh data/test_video.mp4# 视频路径含中文时 OpenCV 读不出来的兜底写法 import cv2 import numpy as np video_path data/测试视频.mp4 # cv2.imread/imread 对中文路径会静默失败 # 常见做法用 np.fromfile 读成字节流再转码 data np.fromfile(video_path, dtypenp.uint8) cap cv2.VideoCapture(cv2.imdecode(data, cv2.IMREAD_COLOR).shape[0] if False else video_path) # 更稳妥的是直接把文件改名成英文或者用 pathlib 拼接绝对路径这个检查步骤是我每次拿到新源码必做的“路径探针”本质是验证三个假设权重存在、视频可读、输出目录可写。很多人跳过这一步直接开跑报错之后才开始怀疑模型、怀疑代码折腾一小时发现只是权重没下载全。3.3 从零训练自己的数据集超参数、标注格式与训练命令毕设要求“自己训练”的场景下YOLOv5 的完整流程是标数据—整理目录—改 yaml—调超参—训练。标注格式是 YOLO 的 txt每行class_id x_center y_center width height坐标是归一化后的比例值不是像素值。目录结构必须是下面这样多一层少一层都会在验证阶段报错dataset/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 ├── labels/ │ ├── train/ # 与图片同名的 txt 标注 │ └── val/ └── dataset.yaml # 数据集配置# dataset.yaml 示例nc 必须和标注类别数一致 train: dataset/images/train val: dataset/images/val nc: 2 names: [car, person]训练命令里最值得研究的是超参数文件hyp.scratch-low.yaml。以我自己的经验第一次训练不要动hsv_h、degrees、flipud这些增强参数它们影响的是模型鲁棒性而不是收敛速度动了反而难排查问题。python train.py \ --data dataset.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --imgsz 640 \ --hyp hyp.scratch-low.yaml \ --device 0参数说明--epochs 100在数据量 2000 张以内够用再多建议 150--batch-size取决于显存8GB 显卡建议 816GB 可以 16设太大会 CUDA out of memory--imgsz 640是输入尺寸行人较多的小目标场景可以保持 640再用--multi-scale做多尺度训练。训练完成后取runs/train/exp/weights/best.pt替换掉主脚本里的yolov5s.pt即可把追踪切到自训练模型上。这里要注意DeepSORT 的 ReID 特征网络不需要重新训练它只认“外观长什么样”不认“是车还是人”类别语义完全由检测器承担。4. 把追踪和计数真正跑起来主脚本参数与计数逻辑4.1 主推理命令从视频到带轨迹和计数的输出环境就绪、文件齐全后跑通一份视频通常只需要一条命令。典型调用长这样python track_and_count.py \ --source data/test_video.mp4 \ --yolo-weights models/yolov5s.pt \ --conf-thres 0.4 \ --iou-thres 0.5 \ --classes 2 3 5 7 \ --device 0 \ --save-vid参数说明--source指定视频路径目录或摄像头 RTSP 流也支持但 RTSP 流的延迟问题不在本资源解决范围内--conf-thres 0.4比默认的 0.25 更严格监控场景中误检代价高我习惯提到 0.4行人密集时再降到 0.3--classes是类别过滤COCO 中2是 car3是 motorbike5是 bus7是 truck行人0需要单独加这里不加行人是想让输出干净一些。--save-vid控制是否把叠加了轨迹和计数框的结果写成视频输出在runs/目录下。主脚本内部流程按帧推进读取一帧 → YOLOv5 推理得到检测框 → 把检测框封装成Detection对象 →tracker.update()得到当前帧的确认轨迹 → 遍历轨迹画框和 ID → 根据计数规则累加。如果你只在终端看到滚动输出但没生成视频多半是--save-vid没加或者输出目录没有写权限。4.2 计数区域的选定与 track_id 去重避免“同一辆车数两次”计数逻辑是这份资源里最值得读源码的部分它的正确性直接决定毕设演示能不能撑住评委提问。常见做法有两种绊线计数画一条线跨线计数和区域计数画一个多边形进入区域计数。监控场景用区域计数更多因为车辆在画面内停留时间长线计数容易因为跟踪抖动重复触发。去重的核心思路是维护一个“已计数 ID 集合”只对第一次进入区域的 track_id 累加counted_ids set() counter 0 def update_count(tracks, zone_polygon): global counter for track in tracks: # 未确认的轨迹不计避免单帧误检污染统计 if not track.is_confirmed(): continue # 已经数过的目标直接跳过 if track.track_id in counted_ids: continue # 取目标框底部中心点作为“进入区域”的参考点 x, y track.get_center_bottom() if point_in_polygon(x, y, zone_polygon): counted_ids.add(track.track_id) counter 1 print(fID {track.track_id} entered, total: {counter})这段代码有三处值得细看。第一track.is_confirmed()过滤了 DeepSORT 里那些刚出现还在观察期的轨迹否则一个人闪进画面再闪出会被误计。第二参考点取“框底部中心”而不是“框中心”因为车辆底部更接近地面用中心点会在地面投影上产生偏差导致车头进入区域但中心没进去。第三point_in_polygon是射线法判断点是否在多边形内如果你拿到的资源里没有这个方法用cv2.pointPolygonTest替代返回值为 1 表示在内部。这个细节就是网上总说的“重复计数”问题的根源所在没有 ID 去重集合、参考点取错、或轨迹断裂后新 ID 重新计数三个原因叠加在一起就会看到一辆车被数出两三次。4.3 双向车流的计数逻辑方向判断是加分项如果你的视频里双向都有车流单纯区域计数只能算“总量”算不了“入向/出向”。毕设答辩时评委会很自然地追问“能不能分方向统计”此时资源里如果提供了方向判断最好没有的话自己加也不复杂# 记录每个 track 第一次和最后一次出现的中心点用位移方向判断去向 direction_of {} for track in tracks: tid track.track_id if tid not in direction_of: direction_of[tid] {first: track.get_center(), last: track.get_center()} else: direction_of[tid][last] track.get_center() # 轨迹确认结束时判断方向 for tid, pos in direction_of.items(): dy pos[last].y - pos[first].y if dy 10: direction_label down elif dy -10: direction_label up else: continue # 水平移动的忽略这里的阈值10是像素单位实际要看你的视频分辨率和目标运动速度。720p 视频里一辆车从画面最上边走到最下边dy 通常能积累到 300 以上阈值 10 只用来滤掉静止不动或原地打转的目标。把这个方向和区域计数结合起来你的输出视频里就能看到“入向 12 辆 / 出向 9 辆”这类统计演示效果比单一总量强不少。5. 避坑排查毕设答辩前踩过的坑全列在这5.1 同一辆车被计数成两辆重复计数的三个来源现象视频里一辆黑色 SUV 从第 200 帧进入检测区域计数从 10 跳到了 11第 320 帧它被旁边货车完全遮挡后重新出现计数又跳到 12。原因遮挡导致 DeepSORT 轨迹断裂目标重新匹配时拿到一个全新的 track_id区域计数逻辑不认它是“旧目标”于是重复计数。另一个来源是参考点取在框中心车辆还没完全进入区域但中心点已经越线离开时中心点还留在区域内来回触发。解决两点配合。一是把MAX_AGE从 30 适当调到 50让轨迹在遮挡期间存活更久减少新 ID 的产生二是严格使用已计数 ID 集合并以框底部中心为参考点已经数过就再不入账。注意MAX_AGE调太高也有副作用目标离开画面很久后轨迹残留可能把别的车误匹配上。5.2 CPU 上跑得跟幻灯片一样帧率只有个位数现象在只有 CPU 的笔记本上跑 1080p 视频处理一帧要 1 秒以上输出视频全是慢动作。原因YOLOv5s 本身在 CPU 上处理 640x640 输入约 80—120msDeepSORT 的 ReID 特征提取还要附加约 40ms两个模块串行叠加就卡死了。另一个隐性成本是视频分辨率输入 1080p 时 YOLOv5 需要先 resize 到 640但部分实现是在原图上做的预处理resize 耗时和内存拷贝也会拖慢速度。解决按优先级来——先加--imgsz 416把输入分辨率降下来再给视频做等比例缩放把宽高限制在 960 以内然后用--device cpu显式指定设备避免代码里默认走 GPU 却在 GPU 不可用时反复报错回退。如果还慢考虑每隔两帧做一次检测中间帧用卡尔曼预测轨迹位置帧率能翻一倍左右。5.3 视频路径带中文字符OpenCV 静默失败现象命令执行时不报错但输出视频文件大小为 0或者终端疯狂输出Warning: failed to open video。原因OpenCV 的cv2.VideoCapture在 Windows 上对非 ASCII 路径支持很差中文文件名直接读不了又不会抛异常只返回一个空对象。解决最省事的是把所有路径改成英文。想保留中文名的话用cv2.VideoCapture(np.fromfile(...))的绕行方案也可以但我实际试下来兼容性不稳定。我的习惯是建立一个data/目录 英文文件名规范所有测试素材统一丢进去中文字段留在内容里。5.4 行人小目标漏检检测器这层的参数调节现象视频里远处的行人完全不出现框近处的自行车和摩托车倒是被检测出来了计数结果明显偏少。原因两个层面。第一是检测器层面conf-thres太高把置信度只有 0.3 的小目标全滤掉了第二是模型层面YOLOv5s 对 32 像素以下的小目标召回率确实有限train 时没有做多尺度训练小目标特征没学到。解决先调--conf-thres 0.2看漏检是否改善没改善就是模型问题回训练阶段给hyp.scratch-low.yaml里的mosaic 1.0换成mosaic 0.5同时加上--multi-scale --img 896做更大尺度的训练。注意这个操作训练时间至少增加一半不是简单加个参数的事。演示场景也可以取巧——把视频画面裁剪放大后送入模型但这是权宜之计答辩时评委如果追问原理还是要回到数据集本身。5.5 自训练的权重放进去直接报 KeyError现象把best.pt替换进项目后运行报KeyError: model.0.conv.weight之类的错。原因YOLOv5 train.py 保存的权重里有时会带ema_ema_model等额外键名或者你用weights/best.pt替换的是主脚本里以普通torch.load方式加载的权重格式不一致。解决用python detect.py --weights best.pt --source test.jpg先单独验证权重能不能被原生 YOLOv5 加载能加载的话检查主脚本里加载模型的部分有没有从 checkpoint 字典里取出model字段的代码。常见做法是加一行ckpt[model].float().eval()而不是直接torch.load。这个坑很隐蔽因为它不是语法错误是加载逻辑不匹配。6. 进阶验证别只贴渲染视频给答辩准备一份可信的指标6.1 用少量标注帧量化追踪效果MOTA 与 IDSW把 demo 视频和带轨迹的渲染图放进毕设报告只能证明“跑通了”证明不了“效果可信”。我建议你从验证视频里抽 300—500 帧做手工标注然后统计三个指标漏检率、误检率、ID Switch 次数。DeepSORT 的硬伤是 IDSW即同一个目标在遮挡后换了 ID这直接摧毁计数准确性。# 统计每个 track_id 的寿命识别异常短命的轨迹 import collections lifetimes collections.Counter() for frame_data in results_per_frame: # results_per_frame 是逐帧的 track_id 列表 for track_id in frame_data: lifetimes[track_id] 1 short_lived [tid for tid, life in lifetimes.items() if life 5] print(fshort-lived tracks: {len(short_lived)} / {len(lifetimes)})逻辑说明正常车辆从进入画面到离开track_id 寿命至少有几十帧。寿命低于 5 帧的轨迹要么是误检要么是 IDSW 产生的新残留。这个数字能直观反映追踪的稳定性比一句“效果很好”有说服力得多。更正式的指标叫 MOTA公式是1 - (FN FP IDSW) / GTGT 是真实框数量FN 是漏检FP 是误检。毕业设计报告里列一张表输入视频片段 1、2、3 的 MOTA 和平均 IDSW评委看到这个表基本不会再质疑你的工作量。6.2 提速与部署边界ONNX/TensorRT 导出能做到什么程度如果你想让答辩再多一个亮点可以把推理导出到 ONNX 再转 TensorRT在 GPU 上能把单帧处理时间从 12ms 压到 5ms 左右。流程是yolov5s.pt→python export.py --include onnx→ TensorRT 引擎。注意这里有两个边界一是 DeepSORT 的 ReID 特征提取也在推理链路里只加速检测器整体帧率提升有限二是这份资源的结构面向“训练—验证—演示”闭环不是面向嵌入式部署的——你可以在树莓派或 Jetson Nano 上做原型验证但生产级部署还要处理多路视频流和断线重连那不是本资源的定位。我自己的习惯是答辩前一周强制做一次“小样本验证跑”抽取测试视频里最容易出问题的遮挡路段确认 IDSW 数量没有离谱增长再定稿演示视频。从那以后我每次提交毕设代码前都会把README.md里提到的环境和命令完整走一遍权重路径、视频路径、输出目录逐项检查这份资源和配套说明文档我都实际跑通过坑和参数边界也都标在上面希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →