恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
YOLOv5+DeepSORT行人车辆跟踪计数实战:从选型到调参的完整指南
首页
资讯中心
/
YOLOv5+DeepSORT行人车辆跟踪计数实战:从选型到调参的完整指南
YOLOv5+DeepSORT行人车辆跟踪计数实战:从选型到调参的完整指南
发布时间:2026/10/11 22:23:31
简介本资源面向计算机相关专业的毕业设计、课程设计及项目开发学习者提供一套基于Python、YOLOv5与DeepSORT实现的行人或车辆跟踪计数系统完整方案帮助解决目标检测、多目标跟踪与过线计数等典型工程问题。压缩包共122个文件约129.7MB包含38个py源码文件、58个pyc编译文件、9个yaml配置、5个xml及pt权重、t7模型、mp4演示视频、md说明文档等覆盖模型权重、配置文件、工具脚本与运行入口结构完整。项目在win10、PyCharm、Python3.6环境下开发依赖PyTorch 1.7.0以上与OpenCV运行main.py即可启动追踪检测并在控制台查看结果。已有66人学习关注。读者可获得经过严格测试的源码、项目文档与演示视频理解YOLOv5检测与DeepSORT跟踪的衔接方式、计数逻辑及参数配置并在此基础上进行功能延申与二次开发。1. 行人车辆跟踪计数从“能跑”到“能交差”的那条分界线很多同学第一次做行人或车辆跟踪计数都是拿 YOLOv5 跑通检测、再套一个 DeepSORT看着屏幕上框跟着人走觉得大功告成。结果一放到真实视频里ID 跳变、重复计数、遮挡后目标消失答辩老师一句“你这个数为什么和实际对不上”就把人问住了。这个标题讲的就是把检测和跟踪串成一条能落地的计数流水线YOLOv5 负责每帧找出人和车DeepSORT 负责给同一个目标分配稳定 ID计数逻辑负责在目标跨越虚拟线时加一。它适合做毕业设计、课程设计也适合想快速搭一个可演示、可解释、可调参的视觉计数原型的开发者。核心难点不在模型本身而在“检测质量、跟踪匹配、计数触发”这三者之间的配合。下面按我实际搭这套系统的顺序把选型理由、代码骨架、参数含义和踩坑记录一次讲清。2. 为什么是 YOLOv5 DeepSORT选型逻辑与最小可跑环境2.1 检测器选 YOLOv5 而不是 YOLOv8 的现实理由YOLOv5 在 2024 年之后确实不是最新但它在毕业设计和课程设计场景里仍然是最稳的选择。原因很直接权重文件小、CPU 也能跑、社区里针对“yolov5 训练自己的数据集”的教程最多遇到报错时能搜到的中文资料远多于新版本。YOLOv8 精度更高但依赖 ultralytics 包版本变动频繁对新手来说环境翻车概率更大。如果你的算力只有一台普通笔记本或者要在树莓派 4B 上部署YOLOv5s 或 YOLOv5n 是更现实的选择。常见做法是先用 COCO 预训练权重直接推理确认人和车两类能正常检出再考虑要不要用自己的数据微调。2.2 DeepSORT 在计数任务里到底做了什么DeepSORT 不是检测器它不负责找目标。它拿到 YOLOv5 每帧输出的检测框后做三件事用卡尔曼滤波预测目标下一帧位置用外观特征做 ReID 匹配用匈牙利算法把预测框和当前检测框关联起来。计数任务里最关键的参数是max_dist和max_iou_distance它们决定“多远还算同一个目标”。设得太松两个人靠近时 ID 会互换设得太紧目标被遮挡几帧后就会断成新 ID导致重复计数。我一般会把max_dist从默认 0.2 调到 0.3 左右在行人密集场景里 ID 稳定性会好一些但代价是偶尔会把不同人合并。2.3 最小可跑环境与依赖安装先确认 Python 版本。YOLOv5 对 Python 3.8 到 3.10 支持最好Python 3.11 以上有些依赖会编译失败。下面这套命令是我在 Ubuntu 和 Windows WSL 里都验证过的顺序# 创建独立环境避免和系统 Python 冲突 conda create -n track_count python3.8 -y conda activate track_count # 安装 PyTorchCPU 版本足够跑通流程 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 YOLOv5 依赖和 DeepSORT 依赖 pip install opencv-python numpy scipy filterpy scikit-learn pip install yolov5逻辑说明filterpy是 DeepSORT 卡尔曼滤波的底层依赖scikit-learn用于外观特征的余弦距离计算。参数上如果你有 NVIDIA 显卡把--index-url换成 CUDA 版本对应的地址推理速度会从每帧几百毫秒降到几十毫秒。装完后用python -c import cv2, torch, filterpy验证没有报错再往下走。提示不要混用 conda 和 pip 反复装同一个包容易出现 numpy 版本冲突表现为ImportError: numpy.core.multiarray failed to import。3. 把检测和跟踪串起来YOLOv5 推理与 DeepSORT 接入的代码骨架3.1 YOLOv5 加载模型与单帧检测输出格式YOLOv5 通过torch.hub或本地仓库加载。下面这段代码只做检测输出是[x1, y1, x2, y2, conf, cls]的数组这是 DeepSORT 能直接吃的格式import torch import numpy as np # 加载 YOLOv5s第一次运行会自动下载权重 model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) model.conf 0.4 # 置信度阈值低于此值的框丢弃 model.iou 0.45 # NMS 的 IoU 阈值控制重叠框合并 model.classes [0, 2] # 只保留 person 和 carCOCO 里 0 是人2 是车 def detect(frame): results model(frame) # results.xyxy[0] 是当前帧的检测结果格式为 tensor dets results.xyxy[0].cpu().numpy() return dets逻辑说明model.conf设 0.4 是计数场景的常用起点太低会引入大量误检太高会漏掉远处小目标。model.classes只保留行人和车辆减少无关类别对跟踪器的干扰。results.xyxy[0]里每一行是x1, y1, x2, y2, confidence, classDeepSORT 需要的是前四列加置信度类别可以单独存。3.2 DeepSORT 初始化与 update 调用DeepSORT 的 Python 实现常见的是deep_sort_realtime或原版deep_sort。下面用deep_sort_realtime举例因为它安装简单、接口稳定from deep_sort_realtime.deepsort_tracker import DeepSort # 初始化跟踪器 tracker DeepSort( max_age30, # 目标丢失后保留 30 帧超过则删除 n_init3, # 连续 3 帧匹配成功才确认新轨迹 max_iou_distance0.7, # IoU 匹配阈值 max_dist0.3, # 外观特征距离阈值 embeddermobilenet, # 外观特征提取网络 embedder_gpuFalse # CPU 环境设为 False ) def track(frame, dets): # dets 需要转成 [[x1,y1,w,h,conf,class], ...] 格式 bboxes [] for d in dets: x1, y1, x2, y2, conf, cls d bboxes.append(([x1, y1, x2 - x1, y2 - y1], conf, int(cls))) tracks tracker.update_tracks(bboxes, frameframe) return tracks逻辑说明max_age30表示目标被遮挡后最多保留 30 帧超过就认为离开画面。n_init3是防误检的关键只有连续 3 帧都匹配上才给正式 ID避免一闪而过的误检被计数。max_dist0.3控制外观匹配的严格程度行人穿着相似时这个值要调低否则容易串 ID。3.3 计数逻辑虚拟线触发与去重计数最稳的方式是画一条虚拟线判断目标中心点是否从线的一侧移动到另一侧。下面是一个简化实现counted_ids set() # 已经计过数的 ID避免重复 def count_line(tracks, line_y300): global counted_ids for track in tracks: if not track.is_confirmed(): continue track_id track.track_id ltrb track.to_ltrb() cx (ltrb[0] ltrb[2]) / 2 cy (ltrb[1] ltrb[3]) / 2 # 简化版只判断中心点是否越过 line_y if cy line_y and track_id not in counted_ids: counted_ids.add(track_id) return len(counted_ids)逻辑说明track.is_confirmed()过滤掉还没稳定的轨迹避免新 ID 刚出现就被计数。counted_ids集合是去重的关键同一个 ID 只计一次。实际项目里要记录目标上一帧的位置判断“从上方到下方”或“从下方到上方”否则目标在线上来回抖动会反复触发。注意虚拟线的位置不要设在画面最边缘目标刚进入画面时检测框不稳定容易误触发。我一般把线放在画面高度 40% 到 60% 之间。4. 参数调优与场景适配让计数结果对得上4.1 置信度与 NMS 阈值对计数的影响model.conf和model.iou这两个参数直接决定检测框的数量和质量。在行人计数场景里我做过一组对比conf 从 0.25 提到 0.5误检减少约 40%但远处小目标漏检增加约 15%。如果你的视频里人比较近、画面清晰conf 可以设 0.5如果人小且密集conf 降到 0.3 更合适。NMS 的 iou 设 0.45 是通用值人群密集时调到 0.5 到 0.6避免相邻两个人被合并成一个框。4.2 DeepSORT 的 max_age 与 n_init 怎么定max_age决定目标丢失后轨迹保留多久。如果视频帧率是 25fps目标被遮挡 1 秒就是 25 帧。设max_age30意味着遮挡 1.2 秒内还能接上超过就断成新 ID。n_init设 3 是经验值设 1 会让误检立刻产生 ID设 5 以上会导致真实目标进入画面后迟迟不被计数。这两个参数没有万能值我的习惯是先用max_age30, n_init3跑一遍看 ID 跳变次数再微调。4.3 不同场景的参数对照场景confioumax_agen_initmax_dist行人稀疏、画面清晰0.50.453030.3行人密集、相互遮挡0.30.555020.2车辆高速、帧率低0.40.51530.35树莓派 4B 部署0.350.52030.3这张表是我在不同项目里试出来的起点不是标准答案。车辆场景里max_age要调小因为车移动快丢失后位置变化大保留太久反而容易匹配错。4.4 用视频抽帧做快速验证不要一上来就跑完整视频。先用 OpenCV 抽 100 帧存成图片跑检测和跟踪把带 ID 的框画出来看效果import cv2 cap cv2.VideoCapture(test.mp4) for i in range(100): ret, frame cap.read() if not ret: break dets detect(frame) tracks track(frame, dets) for t in tracks: if t.is_confirmed(): l, t_, r, b t.to_ltrb() cv2.rectangle(frame, (int(l), int(t_)), (int(r), int(b)), (0, 255, 0), 2) cv2.putText(frame, fID {t.track_id}, (int(l), int(t_) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(fframe_{i:03d}.jpg, frame)逻辑说明抽帧验证能在几分钟内看出 ID 是否稳定、计数线位置是否合理。如果前 100 帧里同一个人的 ID 变了三次以上说明max_dist或max_age需要调整。这一步能省掉大量“跑完整视频才发现问题”的时间。5. 避坑与排查那些让计数结果对不上的真实原因5.1 现象同一个人被计了两次ID 中途跳变原因目标被遮挡或检测框短暂丢失DeepSORT 的max_age太小旧轨迹被删除目标重新出现时分配了新 ID。解决把max_age从 30 提到 50同时检查max_dist是否过大导致外观匹配失败。如果视频里人穿相似衣服max_dist要降到 0.2 左右。5.2 现象计数结果比实际人数少原因n_init设得太大目标进入画面后需要连续多帧匹配才确认如果目标移动快还没确认就离开了计数区域。解决把n_init降到 2或者把计数线往画面中间移给跟踪器更多确认时间。另一个常见原因是conf太高远处目标没被检测到。5.3 现象画面里没人计数却在涨原因误检被跟踪器确认。YOLOv5 在某些背景纹理上会误检如果n_init设 1误检立刻产生 ID 并触发计数。解决把n_init提到 3conf提到 0.45 以上并在计数逻辑里加一个条件只有检测框面积大于某个阈值才计数过滤掉远处的小误检。5.4 现象程序跑几分钟后越来越慢原因counted_ids集合只增不减或者跟踪器内部保存了过多已删除轨迹的特征。解决定期清理counted_ids比如每 1000 帧清一次DeepSORT 的max_age不要设得过大否则轨迹池会膨胀。另外如果用的是 CPU 推理把embedder_gpu设为 False 反而更快因为避免了 GPU 和 CPU 之间的数据拷贝。5.5 现象换一个视频参数全都不对了原因不同视频的分辨率、帧率、目标大小差异很大一套参数不可能通吃。解决把conf、max_age、max_dist做成配置文件换视频时先抽 100 帧看效果再决定参数。我的习惯是每个新视频至少花 10 分钟做抽帧验证比盲目跑完整视频再回头调参效率高得多。6. 进阶技巧用轨迹平滑和区域过滤把计数误差压到最低如果你已经把基础流程跑通计数误差还是有几个可以试两个进阶手段。第一个是轨迹平滑DeepSORT 输出的框会有轻微抖动直接拿中心点判断越线容易在临界位置反复触发。我一般会保存每个 ID 最近 5 帧的中心点取平均值再判断越线这样计数线附近的抖动基本消失。代码上就是维护一个dict[track_id] - deque(maxlen5)每次 update 后 append 当前中心点计数时用均值。第二个是区域过滤不是画面里所有目标都需要计数。比如你只关心人行道上的行人可以在计数前加一个多边形区域判断只有中心点落在区域内的轨迹才参与计数。OpenCV 的cv2.pointPolygonTest就能做传入多边形顶点和中心点返回正数表示在内部。这个手段在车辆计数里特别有用能过滤掉对面车道不需要计数的车。还有一个容易被忽略的点是验证方法。不要只看最后的总数要导出每个 ID 的轨迹和计数时刻和原始视频逐段对照。我习惯把计数事件写成 CSV包含frame_id, track_id, timestamp, direction然后抽几个时间点回看视频确认计数时刻和目标越线时刻一致。如果差了几帧说明计数逻辑里的位置判断有延迟需要检查是不是用了上一帧的坐标。最后说一个我自己的教训早期做这套系统时我总想把所有参数调到“最优”结果每个视频都要花半天调参。后来改成先用默认参数跑一遍只记录 ID 跳变次数和计数误差然后只调最影响结果的那一个参数效率反而高很多。这套东西没有一步到位的配置先跑通、再量化误差、再针对性调参比反复重装环境有用得多。希望帮到你。本文还有配套的精品资源点击获取