ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLOv8车流量检测系统实战:从环境配置到跟踪计数

YOLOv8车流量检测系统实战:从环境配置到跟踪计数 简介这套道路车流量检测系统基于YOLOv8深度学习算法与Python语言实现面向高校毕业设计、交通数据分析和计算机视觉入门实践等场景解决车辆实时检测、计数与交通流量统计问题包含完整源码、训练好的模型和数据集下载后即可直接运行。压缩包共307个文件、155.1MB以126个py源码文件、125个pyc编译文件为主体另有37个yaml配置、可部署的pt/onnx权重以及mp4演示视频覆盖从代码到模型的完整链路。系统实现了端到端的检测流程支持多种交通场景下的车辆识别代码结构清晰、注释详尽适合学生对照学习YOLOv8的目标检测原理与Python工程化实现。已有88人学习下载对于需要快速完成课题或入门车辆检测的开发者是能显著节省环境搭建与训练时间的实用资源。1. 道路车流量检测系统这套 YOLOv8 项目能直接跑但难点在别处拿到这套道路车流量检测系统Python YOLOv8时多数人第一件事就是急着跑 demo结果往往卡在环境、路径和模型加载上。这套资源把最重的活做完了自带 yolov8m.pt 权重、yolov8n 和 yolov8m 的 ONNX 模型、bus.jpg 与 zidane.jpg 测试图还有 car.mp4、car2.mp4、traffic.MP4 三段真实交通视频。但我想先把一个反直觉的结论放在前面YOLOv8 在这个项目里只负责检测也就是把每一帧里的车框出来而车流量统计是另一件事需要你自己补上计数逻辑。搞清楚这个边界你才能把这套资源的价值榨干。它最适合两类人一是正在做毕业设计、需要一套能演示的检测加统计系统的学生二是想快速验证 YOLOv8 实际推理效果、不想从标注和训练开始折腾的从业者。2. 先把环境搭起来并跑通视频Python 环境配置与首次推理2.1 yolov8 环境配置依赖版本与 python 安装的注意点这套系统基于 ultralytics 封装库依赖远没有自己写训练框架那么复杂但版本匹配仍然是第一步的坑。先说 Python 本体建议装 Python 3.8 或 3.93.10 也能用但部分旧版 torch 对 3.10 支持不完整。安装时务必勾选 Add Python to PATH这一步对应很多人在命令行里敲 python 没反应的尴尬——本质上是 windows 的环境变量配置没做pip 和 python 全在别处。我一般会顺手验证一下python --version pip --version两条命令都有输出环境变量才算靠谱。接下来创建虚拟环境和装依赖这是 yolov8 环境配置里最值得固化下来的习惯python -m venv venv venv\Scripts\activate.bat # Windows # source venv/bin/activate # Linux / macOS pip install -r requirements.txtrequirements.txt 建议按下面这份走。注意 torch 要先装 CPU 版跑通再考虑 CUDA否则 CUDA 和 torch 版本不匹配的报错会消耗你大量时间# requirements.txt ultralytics8.0.0 # YOLOv8 训练与推理主库自带模型结构 opencv-python4.8.1.78 # 视频读取与图像处理必须单独装 numpy1.21 # 数组运算与后处理依赖 torch1.8 # 深度学习后端CPU 版即可装完之后别急着跑训练先做一次 import 验证很多人在这一步就翻车报错大概率是 torch 被装成了 CPU 版而代码里指定了 cuda或者 ultralytics 版本和 torch 版本冲突。这里建议直接跑一行python -c from ultralytics import YOLO; print(YOLO.__name__)能输出就说明 yolov8 环境配置成功可以进入推理环节了。2.2 第一次推理加载 yolov8m.pt 检测 bus.jpg资源里自带的 PyTorch 权重是 yolov8m.pt这也是我推荐你优先加载的版本。很多新手习惯照抄教程写 YOLO(yolov8n.pt)而本地明明没有这个文件ultralytics 就会自动联网下载如果网络不畅就会卡在 Downloading 上。正确做法是明确指定资源里已有的模型文件from ultralytics import YOLO import cv2 # 使用资源内自带的 yolov8m.pt断网也能跑 model YOLO(yolov8m.pt) # 读取测试图 img cv2.imread(bus.jpg) results model(img, conf0.4, iou0.5, imgsz640) for r in results: boxes r.boxes.xyxy.cpu().numpy() # 四角坐标 [x1, y1, x2, y2] classes r.boxes.cls.cpu().numpy().astype(int) # 类别索引 scores r.boxes.conf.cpu().numpy() # 置信度 for box, cls, score in zip(boxes, classes, scores): print(fclass{model.names[cls]}, score{score:.2f}, box{box})这段代码的推理参数值得展开说明。conf 是置信度阈值默认 0.25但在车流量场景里我强烈建议提到 0.4 以上因为路牌、树影、行人都会产生低置信度框提上去能省掉大量误检。iou 是 NMS 的非极大值抑制阈值0.5 是通用值目标重叠多可以降到 0.4但正常交通场景不用动。imgsz 是输入分辨率640 是速度与精度的平衡点想要更准可以改 1280代价是推理耗时明显上升。这里顺便解释一下类别输出。YOLOv8 默认在 COCO 80 类上训练和道路车流量相关的类别索引是2 表示 car3 表示 motorcycle5 表示 bus7 表示 truck。后面做统计时只筛选这四个类别就可以把非车辆目标过滤掉。bus.jpg 这张测试图恰好包含了 bus 类别第一次跑通看到 classbus 的输出说明整个链路已经通了。2.3 视频推理car.mp4 逐帧检测并输出 mp4图片检测只是热身这套资源真正的演示效果在视频上。car.mp4 是典型的城市道路场景直接逐帧推理并保存结果就能做出一个直观的检测演示。视频推理逻辑和图片差不多主要多了 VideoCapture 的读写和编码器设置import cv2 from ultralytics import YOLO model YOLO(yolov8m.pt) cap cv2.VideoCapture(car.mp4) fps cap.get(cv2.CAP_PROP_FPS) # 取原视频帧率 w int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 取原视频宽度 h int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 取原视频高度 out cv2.VideoWriter(output.mp4, cv2.VideoWriter_fourcc(*mp4v), fps, (w, h)) while cap.isOpened(): ret, frame cap.read() if not ret: break results model(frame, conf0.4, imgsz640) annotated results[0].plot() # 自带画框函数 out.write(annotated) cap.release() out.release()代码里值得注意的有两点。第一cap.get(cv2.CAP_PROP_FPS) 拿到的帧率要直接传给 VideoWriter否则输出的视频会快进或慢放看起来像倍速播放。第二VideoWriter 的编码器用 mp4v 兼容性最好如果保存出来的文件打不开换成 avc1 或 XVID 再试。我在 Windows 上遇到过 mp4v 写出来只有几 KB 的情况多半是编码器没装全这是 OpenCV 的老问题。性能方面要先有预期yolov8m 在纯 CPU 上跑 640 分辨率1080p 视频大约只有 2~5 帧每秒。如果机器没有独立显卡建议先换用 ONNX 版本的 yolov8n 跑推理速度能快 3 倍左右代价是 mAP 掉几个点。后面第 5 章我会讲怎么在 ONNX 和 PyTorch 权重之间切换。3. 把检测变成流量虚拟检测线与跟踪计数的实现3.1 车流量统计的难点检测和计数之间的鸿沟很多人第一次做完视频检测发现每一帧都框出车了但不知道这条路到底过了多少辆车。原因很简单检测解决的是这一帧里哪里有车而流量统计解决的是一段时间里有多少辆车通过。这两件事之间隔着目标跟踪。举个例子一段 600 帧的视频每秒 30 帧一辆车从画面上方开到下方要经过约 20 帧如果直接统计所有帧里的检测框数量这一辆车会被算成 20 次。最常见的解决方案是虚拟检测线法在画面里画一条横线每辆车底边或中心点穿过这条线时计一次数。这个方案实现简单、实时性好但只适合单向车道而且同一辆车来回穿越会被重复计数。更严谨的做法是给每个检测目标分配 ID 并持续跟踪比如 ByteTrack 或 DeepSORT。目标一旦跨线就把这个 ID 记入已计数集合每个 ID 只计一次。这种做法能同时统计双向流量还能顺带算出车速。ultralytics 库已经把 ByteTrack 内置在 model.track 里不需要额外安装依赖这对做毕设的同学来说非常友好。3.2 基于 ByteTrack 的计数脚本从检测框到过车数字理解了思路我给你一份可以直接改用的计数脚本。它把检测、跟踪、跨线计数和画面可视化串在一起也是这套资源从检测系统升级成车流量检测系统的关键一步from ultralytics import YOLO import cv2 model YOLO(yolov8m.pt) count_line_y 480 # 虚拟检测线的 y 坐标按视频高度调整 crossed_ids set() # 已跨线的车辆 ID 集合去重计数 car_classes {2, 3, 5, 7} # car, motorcycle, bus, truck cap cv2.VideoCapture(car2.mp4) total_count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break # persistTrue 让跟踪 ID 跨帧保持 results model.track(frame, conf0.4, persistTrue, imgsz640) if results[0].boxes is None: continue boxes results[0].boxes.xyxy.cpu().numpy() # 四角坐标 ids results[0].boxes.id.cpu().numpy() if results[0].boxes.id is not None else [] clses results[0].boxes.cls.cpu().numpy().astype(int) for box, tid, cls in zip(boxes, ids, clses): if cls not in car_classes: continue cy (box[1] box[3]) / 2 # 目标中心点 y 坐标 if cy count_line_y and tid not in crossed_ids: crossed_ids.add(tid) total_count 1 print(fvehicle {tid} crossed, total{total_count}) # 绘制检测结果和计数线 annotated results[0].plot() cv2.line(annotated, (0, count_line_y), (annotated.shape[1], count_line_y), (0, 0, 255), 2) cv2.putText(annotated, fcount: {total_count}, (20, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow(traffic count, annotated) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码的关键参数有三个。persistTrue 是必须的它告诉 tracking 模块把当前帧的检测结果和历史帧的目标关联起来ID 才能稳定存在如果漏掉这个参数每个目标每帧都会被分配一个新 ID计数会变成帧数而不是车数。count_line_y 检测线的位置需要根据你用的视频分辨率手动调一般放在画面下方 1/3 到 1/2 处避开画面边缘的目标进入和离开区域。car_classes 集合对应 COCO 类别只统计车辆子集可以避免行人、自行车等检测框干扰计数。用中心点而不是检测框底边做跨线判断是为了减少抖动。YOLOv8 在目标被部分遮挡时检测框的高度会跳动底边位置起伏很大而中心点相对稳定。实际使用中我还建议加一个方向判断记录目标上一次的 cy 值如果 cy 从上往下增加说明是正向通过减少说明是反向这样可以分别统计进出两个方向的车流。3.3 输出统计结果生成时段流量表计数跑通之后光在控制台打印还不够毕设答辩或项目汇报时需要把结果落成表格。最简单的做法是按固定时间窗口聚合每分钟刷一次流量import time from collections import deque window_counts deque() # 按秒记录 last_flush time.time() start_ts time.strftime(%Y-%m-%d %H:%M:%S, time.localtime()) # 这段逻辑加在上一节计数循环内部 now time.time() elapsed int(now - last_flush) if elapsed 10: # 每 10 秒统计一次 window_counts.append(total_count) print(f{time.strftime(%H:%M:%S)} 累计车辆: {total_count}) last_flush now跑完一段视频后可以把每个时间窗口的累计值导出成 CSV用 pandas 或直接写文件都行。这么做的好处是车流量数据变成了可分析的结构化数据后续可以用 matplotlib 画时段曲线也可以给交通管理场景做进一步的通行能力分析。统计时要注意这个思路是基于目标跨线的事件计数而不是画面内实时车辆数两者含义不同在说明文档里要区分清楚。4. 避坑指南YOLOv8 视频项目最常见的五个翻车点4.1 中文路径和空格模型加载失败的第一嫌疑人现象代码明明写着 YOLO(yolov8m.pt)报错却是 FileNotFoundError文件就在眼皮子底下但程序就是找不到。原因ultralytics 内部拼接路径时对中文目录和空格处理很脆弱。最常见的场景是毕设文件放在桌面/新建文件夹这类路径下Windows 用户名的中文目录也会让模型加载直接失败。解决把整个工程放到纯英文路径下比如 D:\traffic_count\。模型文件的读取路径用绝对路径或者把 .pt 文件放在当前工作目录然后用相对路径引用。我自己处理这类项目时第一件事就是检查当前目录用 os.getcwd() 确认工作目录到底在哪这能省掉一半的路径疑难杂症。4.2 断网时卡在 Downloading模型文件没被找到现象明明资源里有 yolov8m.pt一运行却出现 Downloading https://github.com/... 的提示然后卡住不动或者直接下载失败。原因ultralytics 在加载模型时如果代码里写的是 YOLO(yolov8n.pt) 而本地没有对应文件会自动尝试从官方仓库下载权重。很多教程默认写 yolov8n.pt你照着抄但资源里只有 yolov8m.pt 和 ONNX 版本于是触发联网下载。解决第一先 os.listdir() 看看当前目录有哪些模型文件加载时明确写完整文件名。第二把模型放到工程目录下用绝对路径加载杜绝 ultralytics 去官方源同步版本。如果确实需要 yolov8n.pt可以先用资源里自带的 yolov8n.onnx 做推理或者提前在有网络的环境下把它下好再拷贝过来。4.3 视频打不开OpenCV 的解码器跟 .MP4 不对付现象cap cv2.VideoCapture(traffic.MP4) 后cap.isOpened() 返回 False或者循环里 ret 一直是 False画面黑屏。原因OpenCV 是通过 FFmpeg 做视频解码的但 Windows 上官方 pip 包里的 FFmpeg 支持并不完整。traffic.MP4 这种大写扩展名的文件如果内部编码是 H.264 的高 profile 版本OpenCV 有时就是解不出来。解决最省事的方案是用 FFmpeg 先转码成 OpenCV 稳定支持的格式ffmpeg -i traffic.MP4 -c:v libx264 -pix_fmt yuv420p traffic_ok.mp4转码参数里 -c:v libx264 指定 H.264 编码-pix_fmt yuv420p 是为了兼容性转完后的文件 OpenCV 一般都能直接读。如果不想装 FFmpeg也可以用 imageio 库读帧它对编码器的支持和 OpenCV 不同经常能互补。4.4 显存不足和推理卡顿分辨率与批次要一起调现象跑视频推理显卡显存直接爆掉报 CUDA out of memory或者虽然没爆但视频从流畅变成一帧一帧卡成 PPT。原因视频原始分辨率往往比训练时的 640 大得多YOLOv8 默认会直接把整帧原始分辨率放进模型推理1080p 的分辨率是 640 的两倍多显存和算力需求呈指数上涨。如果是用 batch 方式一次丢多帧问题更明显。解决在推理时明确设置 imgsz640让 ultralytics 内部做 letterbox 缩放而不是直接吃原始分辨率。再加上 device0 指定显卡避免 CPU 和 GPU 之间反复切换。具体调整顺序我一般是这样先降 imgsz 到 480再把显卡空闲的机器 batch 降到 1最后才考虑换更小的模型。GTX 1660 Ti 这种 6GB 显存的卡跑 yolov8m 在 640 分辨率下勉强能实时想要更流畅就换 yolov8n。4.5 误检太多conf 阈值和 ROI 裁剪现象检测结果里出现大量把路牌、路灯、广告牌误判成车辆的框圈出来的目标明显不对。原因COCO 预训练模型覆盖场景太广对路边竖立的矩形物体很敏感尤其是在 conf 阈值很低时置信度 0.25 到 0.3 之间的误检会成群出现。另外画面里人行道、绿化带等区域本身就不应该有车但模型不管这些照样输出框。解决第一把 conf 从默认的 0.25 提到 0.4 甚至 0.5这一招能过滤掉至少一半误检。第二给检测区域做 ROI 掩码只保留路面区域的检测结果比如画面下半部分才是车道就只统计 y 坐标在某个范围内的框。第三如果车道场景很固定可以去掉类别集合里概率较低的类别比如摩托车在长距离下容易漏检而漏检比误检更影响流量统计的准确性。5. 模型调参yolov8n 与 yolov8m 对比、ONNX 导出与部署5.1 看网络结构图之前先搞清楚 n 和 m 差在哪很多人一上来就搜 yolov8 网络结构图对着 C2f、SPPF 模块分析半天其实选模型更该先看参数规模和推理速度。这套资源同时给了 yolov8m.pt、yolov8n.onnx 和 yolov8m.onnx本身就适合做对比实验。粗略参数如下模型参数量COCO mAP50-95推理速度参考适用场景yolov8n约 3.2M约 37.3最快实时监控、低算力设备yolov8m约 25.9M约 50.2中等精度优先、有显卡从表格能直接看出n 和 m 之间差了大约 8 倍参数量mAP 提升约 13 个点。在车流量场景里如果只需要统计大致数量、对检测精度不敏感n 是更理性的选择如果要画框给领导或答辩老师看细节m 的框会更贴合车身。我的建议是先用 m 跑一遍视频把结果存下来再拿 n 跑同样的视频对比误检数、漏检数和每秒帧数用数据决定最终用哪个。5.2 把 pt 导出成 onnx摆脱 PyTorch 依赖资源里的 ONNX 模型是已经导好的但如果以后自己训练了新权重还是要会导出。导出 ONNX 的主要目的是脱离 PyTorch 运行时尤其部署到边缘设备时ONNX 是通用格式。方法非常简单from ultralytics import YOLO model YOLO(yolov8m.pt) model.export(formatonnx, imgsz640, opset12, simplifyTrue)参数里 opset 是 ONNX 算子集版本opset 12 对大部分推理引擎兼容性最好如果部署目标是边缘平台的 NPU需要先查该平台支持的 opset 版本有的老平台只支持 11。simplifyTrue 会调用 onnx-simplifier 对计算图做精简能减小文件体积并加快运行速度但个别自定义算子有可能在简化后出错导出后务必跑一次推理验证。导出后在工程目录下会生成一个 yolov8m.onnx 文件。这个文件不依赖 torch任何支持 ONNX 的推理库都能加载这也是资源同时给 onnx 版本的原因。5.3 onnxruntime 推理代码骨架用 ONNX Runtime 跑 YOLOv8 和用 ultralytics 完全不同你不再是调用模型对象而是要自己做预处理、推理、后处理三步。这也是 ONNX 部署中最容易让人放弃的坎。我给出一个最小可运行的推理骨架import cv2 import numpy as np import onnxruntime as ort sess ort.InferenceSession(yolov8n.onnx) input_name sess.get_inputs()[0].name def letterbox(img, size640): h, w img.shape[:2] scale min(size / h, size / w) nh, nw int(h * scale), int(w * scale) resized cv2.resize(img, (nw, nh)) canvas np.full((size, size, 3), 114, dtypenp.uint8) canvas[:nh, :nw] resized return canvas, scale frame cv2.imread(zidane.jpg) inp, scale letterbox(frame) # YOLOv8 输入是 NCHW、RGB、0-1 归一化 inp inp[:, :, ::-1].transpose(2, 0, 1).astype(np.float32) / 255.0 inp np.expand_dims(inp, axis0) # 输出 shape 是 [1, 84, 8400]84 4 坐标 80 类 out sess.run(None, {input_name: inp})[0] print(out.shape) # (1, 84, 8400)这里的坑点集中在三点。预处理必须做 RGB 转换因为 OpenCV 读出来是 BGR直接送入模型会颜色错乱。letterbox 的缩放方式要和训练时保持一致否则检测精度会下降。输出后处理则是 YOLOv8 特有的8400 是 640 分辨率下三个尺度特征图的锚点总数你真的需要解析这 8400 个候选框并做 NMS才能得到最终检测框。好在有 onnxruntime 自带的非极大值抑制算子可以做这一步但代码量不短。如果你只是想在本地跑ultralytics 直接加载 onnx 其实更省事model YOLO(yolov8n.onnx) results model(car.mp4, conf0.4)ultralytics 会自动帮你完成后处理但你付出的代价是推理速度不如裸 onnxruntime 快因为中间多了一层封装。6. 再进一步训练自己的数据集前先跑一轮验证6.1 val 先行别急着花钱标注很多同学拿到这套资源就想立刻训练自己的数据集。我的建议是先别急着收集图片、打标注而是用现成模型对你自己的视频做一次验证。把手机架在路边拍 3 分钟视频然后用资源里的 yolov8m.pt 直接推理看检测效果再决定下一步。如果画面里的车都能被稳定框出来说明 COCO 预训练权重足够覆盖你的场景如果频繁漏检三轮车、货车等特殊车型才需要考虑自己训练。这一步能帮你判断时间和钱该不该花。验证命令很简单yolo detect val modelyolov8m.pt datacoco.yaml imgsz6406.2 一个能落地的训练起点如果确认需要训练推荐用迁移学习方式基于现成的 pt 权重继续训练而不是从零随机初始化。自己收集的数据集做到几百张图就能有不错效果标注格式用 YOLO 的 txt 格式目录结构固定为datasets/ mydata/ images/ train/ val/ labels/ train/ val/data.yaml 指向路径训练命令只需要两行yolo detect train datamydata.yaml modelyolov8m.pt epochs50 imgsz640 yolo detect val datamydata.yaml modelruns/detect/train/weights/best.pt训练时重点看两个指标mAP50 反映检测框与真实框的贴合程度一般车流量场景达到 0.8 以上就够用mAP50-95 是更严苛的指标常见模型在 0.5 左右已经算合格。训练完别焦虑先用 best.pt 跑视频看实际效果再决定要不要继续加数据。我从那以后每次做这类项目都强制自己先跑一轮 val 再决定训练策略这个习惯帮我省掉过至少两次从头标注的返工。这套资源模型、视频和数据集都齐了直接下载放本地就能开始验证希望帮到你。本文还有配套的精品资源点击获取
返回列表