news 2026/9/23 3:25:21

YOLOv5+DeepSORT车辆行人追踪计数实战:从环境配置到防重复计数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5+DeepSORT车辆行人追踪计数实战:从环境配置到防重复计数

简介:这份资源面向计算机视觉初学者与进阶开发者,聚焦车辆与行人追踪计数这一典型落地场景,提供基于YOLOv5与DeepSORT的完整项目实践代码。YOLOv5负责实时目标检测,输出带置信度与分类标签的边界框;DeepSORT则借助卡尔曼滤波与匈牙利算法完成轨迹匹配,即使目标短暂遮挡或视角变化也能保持连续追踪,二者结合可构建智能监控与客流统计系统。压缩包共103个文件,约79.93MB,以42个py源码与51个pyc编译文件为主,另含yaml配置、md说明、pt预训练权重及t7模型文件,覆盖模型推理、跟踪逻辑、参数配置与辅助工具等模块。目前已有3289人学习下载。读者可据此理解检测与跟踪的衔接方式,参考Detector类的封装思路,并基于预训练权重快速跑通车辆行人计数流程,为二次开发与场景调优提供可复用的工程模板。

1. 从一段路口监控说起:YOLOv5+DeepSORT 到底在数什么

路口摄像头架好,画面里车流不断。你要的不是一张张标注框,而是「今天下午三点到四点,这个方向过了多少辆车、多少个人」。这件事拆开看是两件事:先得在每一帧里把车和人找出来,再得把跨帧的同一个目标串成一条轨迹,最后在轨迹穿过一条虚拟线的瞬间加一。YOLOv5 负责前者,DeepSORT 负责后者,计数逻辑是你自己写的那几十行状态机。

很多人第一次做这个项目,卡住的地方不是模型跑不起来,而是数出来的数字对不上:一辆车被数了三次,或者两辆车贴在一起只算了一辆。这不是模型精度问题,是追踪 ID 跳变和计数线判定逻辑的问题。这篇笔记按「先跑通、再调准、最后防重复」的顺序,把 YOLOv5+DeepSORT 做车辆行人追踪和计数的完整路径讲清楚,包括环境配置、参数怎么设、重复计数怎么修、以及树莓派这类边缘设备上部署时要注意什么。适合已经会跑 YOLOv5 推理、想把它变成可用计数系统的同学。

2. 环境配置与最小可跑通链路:conda 建环境到第一帧带 ID 的输出

2.1 为什么选 YOLOv5+DeepSORT 这套组合

做车辆行人追踪计数,检测器可以换,追踪器也可以换,但 YOLOv5+DeepSORT 是目前资料最全、复现成本最低的组合。YOLOv5 的优势不在精度天花板,而在于工程化程度高:权重文件小、推理速度快、训练自己数据集的流程成熟,yolov5s.pt在普通显卡上就能跑到实时。DeepSORT 的优势在于它把外观特征(ReID 向量)和运动信息(卡尔曼滤波)结合起来做匹配,比纯 IOU 匹配的 SORT 更抗遮挡——车辆互相遮挡两三帧后重新出现,DeepSORT 大概率还能把它认回原来的 ID。

选型上有一个容易忽略的点:DeepSORT 的 ReID 模型是独立于检测器的。也就是说你换 YOLOv5 的版本、换自己训练的权重,DeepSORT 这边不用动。这个解耦对落地很友好,检测器可以按场景迭代,追踪器保持稳定。

常见做法是直接用 ultralytics 的 YOLOv5 仓库加一个 DeepSORT 实现。我一般会先把检测和追踪分开验证:先确认 YOLOv5 单帧检测没问题,再接 DeepSORT 看 ID 是否稳定,最后才加计数逻辑。一次性全接上,出问题很难定位是检测漏了还是追踪跟丢了。

2.2 conda 环境与依赖安装的具体命令

环境配置这一步翻车最多,主要是 torch 版本和 CUDA 对不上。下面是我常用的流程,先建独立环境避免污染 base。

# 创建 Python 3.9 环境,YOLOv5 对 3.8-3.10 兼容较好 conda create -n yolo_track python=3.9 -y conda activate yolo_track # 安装 PyTorch,这里以 CUDA 11.8 为例,具体按你的驱动版本改 # 没有 GPU 就装 CPU 版,命令去 PyTorch 官网生成 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 克隆 YOLOv5 仓库并安装依赖 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt # DeepSORT 依赖,deep_sort_realtime 是封装较好的一个实现 pip install deep-sort-realtime opencv-python

这里有几个参数要盯住。python=3.9不是随便选的,3.11 以上部分 DeepSORT 依赖会编译失败。torch 的 CUDA 版本必须和nvidia-smi右上角显示的 CUDA Version 兼容,不是必须相等,但不能超过。deep-sort-realtime相比原版 deep_sort 省去了单独下载 ReID 权重的步骤,它会自动拉取,适合快速验证。

装完先跑一句验证:

python -c "import torch; print(torch.cuda.is_available())"

输出True说明 GPU 可用。如果是False而你有显卡,八成是 torch 装成了 CPU 版,重装即可。

2.3 检测加追踪的最小脚本

先不写计数,只把检测和追踪串起来,确认每一帧的输出框上带稳定 ID。

import cv2 import torch from deep_sort_realtime.deepsort_tracker import DeepSort # 加载 YOLOv5,用本地权重避免每次联网 model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True) model.conf = 0.4 # 置信度阈值,低于此值的框丢弃 model.iou = 0.45 # NMS 的 IOU 阈值,控制重叠框合并 # 初始化 DeepSORT tracker = DeepSort( max_age=30, # 目标丢失多少帧后删除轨迹 n_init=3, # 连续命中多少帧才确认新轨迹 max_iou_distance=0.7 # 级联匹配的 IOU 距离阈值 ) cap = cv2.VideoCapture('test.mp4') while cap.isOpened(): ret, frame = cap.read() if not ret: break # YOLOv5 推理,只保留 car 和 person 两类 results = model(frame, size=640) dets = results.xyxy[0].cpu().numpy() # 转成 DeepSORT 需要的格式:[x, y, w, h], conf, class det_list = [] for *xyxy, conf, cls in dets: if int(cls) in [0, 2]: # COCO 里 0 是 person,2 是 car x1, y1, x2, y2 = xyxy det_list.append(([x1, y1, x2 - x1, y2 - y1], conf, int(cls))) tracks = tracker.update_tracks(det_list, frame=frame) for t in tracks: if not t.is_confirmed(): continue l, tp, r, b = t.to_ltrb() cv2.rectangle(frame, (int(l), int(tp)), (int(r), int(b)), (0, 255, 0), 2) cv2.putText(frame, f'ID{t.track_id}', (int(l), int(tp) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow('track', frame) if cv2.waitKey(1) & 0xFF == 27: break cap.release() cv2.destroyAllWindows()

逻辑上分三段:YOLOv5 出检测框,过滤出人和车两类,转成 DeepSORT 的输入格式;DeepSORT 用上一帧的轨迹和这一帧的检测做匹配,输出带track_id的轨迹;最后画框。max_age=30表示目标消失 30 帧内还保留轨迹,等它重新出现;n_init=3表示新目标要连续 3 帧被检测到才给正式 ID,这个参数能过滤掉大量一闪而过的误检。max_iou_distance调大匹配更宽松,遮挡场景可以适当加大,但太大容易把两个目标混成一个。

跑通这一步,你会看到画面里每个车和人框上有一个相对稳定的 ID。ID 稳定了,计数才有意义。

3. 计数逻辑怎么写:虚拟线、方向判定与去重状态机

3.1 计数线的两种画法和方向判定

计数本质是判断一个轨迹有没有「穿过」一条线。线有两种画法:水平线和垂直线。水平线适合俯拍或侧拍的车道,判断目标中心点的 y 坐标从线的一侧变到另一侧;垂直线适合正对车流的画面,判断 x 坐标。

方向判定不能只看「穿过了」,还要看「从哪边穿到哪边」,否则来回走动的行人会被反复计数。做法是记录轨迹上一帧的中心点位置,和当前帧比较:

# 计数线:水平线 y=400,只统计从上往下穿过的目标 LINE_Y = 400 counted_ids = set() # 已计数的轨迹 ID,防止重复 prev_center = {} # 记录每个轨迹上一帧的中心点 def check_cross(track_id, cx, cy): if track_id in counted_ids: return False if track_id not in prev_center: prev_center[track_id] = (cx, cy) return False _, prev_y = prev_center[track_id] prev_center[track_id] = (cx, cy) # 上一帧在线以上,当前帧在线以下,判定为向下穿过 if prev_y < LINE_Y <= cy: counted_ids.add(track_id) return True return False

counted_ids这个集合是防重复计数的第一道防线。一个轨迹 ID 只要计过一次,就不再计。prev_center记录上一帧位置,用来判断穿越方向。这里有个细节:如果目标移动快,一帧跨过了整条线,prev_y < LINE_Y <= cy依然成立,不会漏。但如果目标在线的附近抖动,中心点反复跨越,就可能误判——所以counted_ids必须配合使用。

3.2 重复计数的三个来源和对应修法

重复计数是这个项目最典型的坑,热词里「重复计数」出现频率很高。来源主要有三个。

第一个是 ID 跳变。目标被遮挡后 DeepSORT 给了新 ID,同一个目标被当成两个,计两次。修法是调大max_age,让轨迹在遮挡期间保留更久;同时确认 ReID 特征在正常工作,如果deep-sort-realtime没加载到 ReID 权重,匹配会退化成纯 IOU,遮挡后基本必跳 ID。

第二个是计数线附近的抖动。目标停在线上或缓慢移动,中心点来回穿越。修法是加一个缓冲区:只有目标中心点从线的一侧移动到另一侧超过一定像素(比如 10 像素)才判定穿越,而不是一碰到线就算。

BUFFER = 10 # 缓冲区像素 def check_cross_stable(track_id, cx, cy): if track_id in counted_ids: return False if track_id not in prev_center: prev_center[track_id] = (cx, cy) return False _, prev_y = prev_center[track_id] prev_center[track_id] = (cx, cy) # 必须完整跨过线加缓冲区 if prev_y < LINE_Y - BUFFER and cy >= LINE_Y + BUFFER: counted_ids.add(track_id) return True return False

第三个是同一目标在画面里被检测成多个框。这属于检测层的问题,靠调 NMS 的iou阈值解决,把重叠框合并掉。如果两个框是同一辆车的前后部分,说明模型对该场景训练不足,需要用自己的数据集微调。

3.3 把计数结果落到视频和日志

计数不能只在画面上显示,落地要能导出。常见做法是每帧把当前计数写到画面左上角,同时在穿越发生时往 CSV 追加一条记录,包含时间戳、轨迹 ID、类别、方向。

import csv, time with open('count_log.csv', 'a', newline='') as f: writer = csv.writer(f) # 穿越发生时调用 writer.writerow([time.time(), track_id, class_name, 'down'])

这样即使视频处理中断,日志也在。后续要做分时段统计,直接读 CSV 按时间窗口聚合就行。注意 CSV 里的时间戳用浮点秒,别用格式化字符串,方便后续计算。如果导出到 Excel 发现数值变成科学计数法,那是 Excel 的显示问题,不是数据问题,导入时把该列设为文本即可。

4. 避坑与排查:ID 跳变、漏检和性能这三类问题怎么定位

4.1 现象:同一辆车在画面里换了 ID

原因:DeepSORT 的外观特征匹配失效,或者max_age太小导致轨迹被提前删除。遮挡、光照突变、目标尺寸剧烈变化都会触发。

解决:先确认 ReID 权重加载成功,打印一下 tracker 的 embedder 是否非空。然后把max_age从 30 调到 50 试试,代价是内存里保留的轨迹变多。如果还跳,检查检测框是否稳定——检测框抖动大,IOU 匹配也会失败,可以适当降低 YOLOv5 的conf让检测更连续。

4.2 现象:计数比实际少,有车没数到

原因:检测漏检,或者目标从画面边缘进入时还没被确认(n_init没满足)就已经穿过了计数线。

解决:把计数线往画面中间挪,给目标足够的帧数完成初始化。n_init从 3 降到 2 能加快确认,但误检也会增加。如果是夜间或逆光漏检,那是检测器的问题,需要用对应场景的数据微调 YOLOv5,调参救不了。

4.3 现象:帧率很低,视频处理比实时慢很多

原因:YOLOv5 输入尺寸太大,或者每帧都在做 ReID 特征提取。

解决:size从 640 降到 416 或 320,速度提升明显,代价是小目标检测变差。DeepSORT 的 ReID 是主要开销之一,如果场景里目标外观差异大(比如不同颜色的车),可以降低 ReID 的调用频率,每隔几帧提一次特征。树莓派上部署时这两条尤其重要,树莓派 5 跑yolov5n加轻量追踪才勉强实时,yolov5s基本跑不动。

4.4 现象:CSV 日志里计数对不上画面显示

原因:画面显示的是当前帧的累计值,CSV 是穿越事件流,两者统计口径不同。如果程序中途重启,画面计数归零但 CSV 还在追加。

解决:统一以 CSV 为准,画面计数从 CSV 读初始值。或者每次启动生成带时间戳的新日志文件,避免混淆。

4.5 现象:换了自己训练的 YOLOv5 权重后追踪全乱

原因:自己训练的模型类别索引和 COCO 不一样,过滤类别时用了错误的索引,把该保留的类过滤掉了,或者把背景当成了目标。

解决:打印模型的names属性确认类别顺序,过滤逻辑按新索引改。另外自己训练的模型置信度分布可能不同,conf阈值要重新调,不能沿用 0.4。

5. 进阶:用自己数据集微调 YOLOv5 并验证计数精度

5.1 训练自己数据集的流程要点

通用 COCO 权重在特定场景(比如某个路口、某种车型)上漏检是常态。微调流程不复杂,关键是数据标注质量。用 labelImg 或 Roboflow 标注,导出 YOLO 格式,每张图对应一个 txt,每行是class x_center y_center width height,坐标归一化到 0 到 1。

目录结构按 YOLOv5 要求组织:

dataset/ images/ train/ val/ labels/ train/ val/

然后写数据配置文件:

# data/mydata.yaml path: ./dataset train: images/train val: images/val nc: 2 names: ['person', 'car']

训练命令:

python train.py --img 640 --batch 16 --epochs 100 \ --data data/mydata.yaml --weights yolov5s.pt

--img 640是训练分辨率,要和推理时一致,否则小目标表现会差。--batch 16按显存调,显存不够就降。--weights yolov5s.pt表示从预训练权重开始,比从头训练收敛快得多。训练完在runs/train/exp/weights/best.pt

5.2 超参数里最该动的三个

YOLOv5 的超参数很多,但做追踪计数,真正影响结果的就三个。学习率lr0默认 0.01,小数据集上容易过拟合,可以降到 0.001。数据增强里的mosaic默认开启,对小目标有帮助,但如果你的场景目标都很大,可以关掉减少训练噪声。anchor如果自己数据集的框尺寸和 COCO 差异大,跑一次python train.py --noautoanchor对比,或者用 k-means 重新聚类生成 anchor。

5.3 计数精度的验证方法

模型训完不能只看 mAP,要看计数误差。做法是人工数一段视频的真实过车数,然后跑系统对比。建议至少取三段不同时段的视频,分别统计,看误差是否稳定。如果误差集中在某个方向(比如总是少数),说明计数线位置或方向判定有系统偏差;如果误差随机,多半是 ID 跳变导致。

一个实用的验证技巧:把counted_ids和对应的轨迹 ID 一起导出,人工抽查几个被计数的轨迹,看是不是同一个目标被计了多次。这比只看总数更能定位问题。

5.4 边缘部署的现实预期

树莓派 5 上部署自己训练的 YOLOv5 模型,现实是:yolov5n加 ONNX Runtime 或 NCNN 推理,配合轻量追踪,能做到 5 到 10 FPS,够用于低速场景。yolov5s基本在 2 FPS 以下,不适合实时。如果一定要在边缘做,考虑把检测放到服务端,边缘只做推流,或者用更轻的检测器替换 YOLOv5。这个取舍要在项目开始前想清楚,别做到一半发现算力不够。

我自己做这类项目养成的习惯是:先把计数逻辑用一段固定视频跑通,把counted_ids和轨迹日志打出来人工核对,确认没有重复和漏计之后,再换场景、换权重。跳过这一步直接上生产视频,后面排查重复计数会非常痛苦。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 3:25:06

泉州雨棚漏水维修电话|接缝开裂渗水上门检查|欧米到家服务电话

&#x1f4dd; 文章简介泉州住宅、商铺和办公场所常见的漏水问题&#xff0c;包括卫生间渗水、阳台积水、屋顶漏水、外墙返潮、厨房墙面发霉、窗边渗水、地下室潮湿等。欧米到家提供泉州多区域防水补漏、漏水点排查、局部修补、卫浴及水电相关维修服务。遇到雨后渗水、墙顶水印…

作者头像 李华
网站建设 2026/9/23 3:23:52

RuoYi-Vue-Plus WebSocket实战:从鉴权到微服务跨节点推送

被标题里的“(2)”点进来的朋友&#xff0c;应该都看过我之前那篇WebSocket入门文章了。简单回顾一下&#xff1a;上一篇主要讲了怎么在RuoYi-Vue-Plus里把原生WebSocket跑起来&#xff0c;前端能连通、服务端能收到消息&#xff0c;算是完成了“从0到1”。但说实话&#xff0c…

作者头像 李华
网站建设 2026/9/23 3:23:27

Compton X Render后端优化指南:X11桌面合成性能提升

1. Wayland时代还在用X11&#xff1a;Compton的适用场景与性能困境1.1 Compton到底是什么&#xff0c;为什么老兵不死很多接触Linux桌面不到两三年的朋友&#xff0c;可能压根没听说过Compton这个名字。它是X11环境下的一款合成管理器&#xff08;compositor&#xff09;&#…

作者头像 李华
网站建设 2026/9/23 3:20:34

YOLOv8车流检测毕设实战:CPU环境零GPU部署指南

简介&#xff1a;本资源是一套基于YOLOv8的多端车流检测系统完整实现&#xff0c;面向计算机、人工智能、自动化等专业的本科生及初阶开发者&#xff0c;适用于毕业设计、课程设计与项目实践。系统支持多路视频流实时检测&#xff0c;涵盖模型训练、推理部署、GUI界面及报警机制…

作者头像 李华
网站建设 2026/9/23 3:19:53

WebGPU+Transformers.js:在浏览器中端侧运行DeepSeek-R1蒸馏模型

聊一个我最近一直在折腾的事&#xff1a;在浏览器里直接跑 DeepSeek-R1 的推理。先说清楚&#xff0c;完整版 R1 是六百多亿参数的 MoE 模型&#xff0c;普通电脑那点内存连模型文件都放不下&#xff0c;更别说单靠浏览器去做端侧推理。但我真正跑通的是 DeepSeek 官方蒸馏出来…

作者头像 李华
网站建设 2026/9/23 3:19:50

动态换肤组件化实战:基于CSS变量的主题Token设计与ThemeProvider实现

这年头做应用&#xff0c;不做个“白天摸鱼晚上蹦迪”的暗色模式&#xff0c;都不好意思说自己搞过前端。但你说做个换肤吧&#xff0c;需求方拿着截图跟你说“就按这个色儿来”&#xff0c;然后第二天又换成另一个色儿&#xff0c;这时候你要是还给每个页面写死颜色样式&#…

作者头像 李华