ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于YOLOv8与PyQt5的路面缺陷检测系统设计与实现

2026/9/7 22:35:57 拓冰建站 浏览量
基于YOLOv8与PyQt5的路面缺陷检测系统设计与实现 坑洼、裂缝这类路面缺陷人工巡检成本高、漏检率不低尤其到了雨季道路病害会快速扩散。这次我们来看一个把 YOLOv8 目标检测和 PyQt5 桌面界面结合起来的路面缺陷检测系统识别对象就是坑洼、破损等马路表面问题。它不依赖云服务模型跑在本地可以通过图片、视频或摄像头实时检测也能批量处理文件夹里的图像核心价值是给道路养护、市政巡检、工程验收提供一个可离线运行的辅助筛查工具。这个项目最值得关注的地方有三个第一技术栈明确YOLOv8 负责检测PyQt5 负责图形界面两者通过 Python 环境整合工程上很常见第二可扩展性强训练好的best.pt权重文件既能嵌入 PyQt5 桌面端也能单独做批量推理或启动 API 服务第三硬件门槛相对可控模型可以从yolov8n到yolov8x自由选择显存占用不同普通办公电脑也能用 CPU 跑推理只是速度不如 GPU。本文会带你完整走一遍环境准备、依赖安装、数据集准备、模型训练、PyQt5 界面开发、批量任务和 API 调用流程最后给出常见问题排查方法。1. 核心能力速览能力项说明项目类型Python 桌面应用 深度学习目标检测技术栈YOLOv8Ultralytics、PyQt5、OpenCV检测对象路面坑洼、破损缺陷等模型来源公开 COCO 预训练权重或自训练权重best.pt图像输入单张图片、视频文件、摄像头实时画面批量任务支持对目录内图片批量推理接口 API可通过 Flask 或 FastAPI 二次封装 HTTP 接口推荐硬件GPU 优先GTX 16 系及以上可流畅训练小模型CPU 可推理显存占用取决于模型规格yolov8n较低yolov8m及以上明显升高需以实测为准启动方式Python 脚本启动 PyQt5 窗口 / 命令行调用模型适合场景道路巡检、市政设施普查、工程质量辅助验收关于显存占用不同模型规格之间差别很大。按 Ultralytics 公开的模型规模和社区常见测试来看yolov8n在 640 输入尺寸下显存占用约 2G 左右yolov8s约 4G 级别yolov8m及以上需要更大显存。具体数值受批量大小、输入分辨率、是否开启训练模式影响建议以本机实际测试为准不要照搬一切网上的数字。2. 适用场景与使用边界这类检测系统适合的典型场景包括市政道路日常巡检用巡检车记录路面视频后切片批量识别。工程质量验收对新建路段的路面破损情况进行辅助筛查。高校科研和课程设计用完整项目学习 YOLOv8 训练与 PyQt5 桌面开发。小型团队自研路面养护工具在本地完成缺陷分类和标注统计。不适合的场景也要说清楚。对于深度较大的结构性损坏、塌陷或带有复杂背景遮挡的坑洼单靠 2D 目标检测无法判断病害深度系统只能做到“发现疑似坑洼”不能替代现场人工复检。另外如果要在夜间或雨天使用需要准备足够好的补光设备和样本数据否则漏检率会上升。合规和隐私方面道路巡检数据通常包含车牌、行人面部、周边建筑等敏感信息。在采集、标注、存储和展示环节要遵守所在地区和单位的隐私管理规定。不要把巡检数据随意上传到外部云服务如果系统接入摄像头要确保监控范围合法并在界面中做好数据访问控制。默认只保存检测结果和统计信息不额外留存无关的个人信息。3. 环境准备与安装部署3.1 环境检查清单建议先确认本机环境避免装到一半才发现版本冲突。推荐使用 Python 3.8 到 3.11Ultralytics 对 Python 版本有持续适配使用较新稳定版本问题最少。操作系统方面 Windows 10/11、Ubuntu 20.04/22.04 都可以。GPU 训练需要 NVIDIA 显卡建议驱动更新到较新版本并安装匹配的 CUDA 和 cuDNN没有 GPU 也能跑推理和训练小模型但速度差距明显。磁盘空间建议预留 15G 以上YOLOv8 模型文件本身不大但训练中间结果和数据集会占空间。打开终端或 Anaconda Prompt执行以下命令创建虚拟环境并激活conda create -n pothole python3.9 -y conda activate pothole如果你更习惯原生 Python也可以用python -m venv创建虚拟环境。3.2 安装依赖库安装ultralytics、pyqt5、opencv-python、pillow这几个是核心依赖。建议在一个命令中安装让 pip 统一处理版本依赖pip install ultralytics pyqt5 opencv-python pillow如果你的网络环境下载速度较慢可以临时使用国内镜像源pip install ultralytics pyqt5 opencv-python pillow -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后先验证 YOLOv8 是否能正常导入并下载预训练权重。网络正常情况下首次运行会自动下载yolov8n.pt。from ultralytics import YOLO # 会自动下载 yolov8n.pt 到当前目录 model YOLO(yolov8n.pt) print(YOLOv8 安装成功)再验证 PyQt5 是否可以创建窗口。运行下面这段代码如果弹出一个空白窗口说明界面环境正常。import sys from PyQt5.QtWidgets import QApplication, QWidget app QApplication(sys.argv) w QWidget() w.resize(400, 300) w.setWindowTitle(PyQt5 测试窗口) w.show() sys.exit(app.exec_())3.3 验证 GPU 是否可用训练目标检测模型时GPU 能明显加速。先确认 PyTorch 是否能识别到显卡import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 未检测到 GPU)如果输出True说明 PyTorch 正常使用 GPU。如果输出False需要检查显卡驱动、CUDA 版本或者重新安装对应 CUDA 版本的 PyTorch。即使没有 GPU继续往下走也没问题训练时间会变长但可以用devicecpu指定。4. 数据集准备与 YOLOv8 模型训练4.1 数据集目录结构训练自己的坑洼检测模型数据集通常采用 YOLO 格式目录结构图片放在images文件夹标注文本放在labels文件夹训练集和验证集分开放置。dataset/ images/ train/ road_001.jpg road_002.jpg val/ road_101.jpg labels/ train/ road_001.txt road_002.txt val/ road_101.txt pothole.yaml在标注阶段建议使用 LabelImg 或 LabelStudio标注类别名设置为pothole导出格式选择 YOLO。每张图片对应一个.txt文件每行内容为类别编号 cx cy w h坐标是归一化后的值。4.2 编写数据集配置文件pothole.yaml是训练时的数据集配置里面指定训练集、验证集路径和类别名train: dataset/images/train val: dataset/images/val nc: 1 names: [pothole]注意train和val路径要写实际路径。如果数据集与代码在同一目录下建议使用相对路径方便项目整体迁移。4.3 数据采集与增强建议坑洼样本在真实道路中分布不均晴天、阴天、雨天、顺光、逆光、阴影遮挡等因素都会影响检测效果。采集数据时尽量覆盖不同时段、不同光照、不同道路材质。如果样本量不足可以利用 YOLOv8 自带的 Mosaic、随机翻转、色彩抖动等增强策略。在train方法中加入augmentTrue可以默认开启部分增强也可以根据自己的数据情况调整超参数。4.4 执行模型训练训练前先决定模型规格。yolov8n速度最快、显存占用最低适合快速验证流程yolov8s是精度和速度比较平衡的选择yolov8m及以上精度可能更高但显存占用和推理时间都会增加。对路面检测这种场景样本特征比较明显yolov8n或yolov8s通常就够用。from ultralytics import YOLO # 加载预训练模型作为起点微调到自己的数据集 model YOLO(yolov8s.pt) # 训练 100 轮输入尺寸 640批量大小 8 model.train( datadataset/pothole.yaml, epochs100, imgsz640, batch8, device0, # 0 表示第一块 GPUCPU 则写 cpu workers4, namepothole_train )训练结束后模型权重位于runs/detect/pothole_train/weights/best.pt。best.pt是验证集上表现最好的权重后续检测和部署都使用这个文件。如果训练过程中发现 loss 曲线不收敛可以适当增加轮数或者调整学习率如果验证集精度一直很低优先检查数据集标注是否正确、类别是否平衡、图片和标注文件是否一一对应。4.5 训练效果验证训练完成后用验证集图片和 1 张从未见过的测试图片来观察效果。运行下面的命令会在runs/detect/predict目录下生成带标注框的结果图yolo predict modelruns/detect/pothole_train/weights/best.pt sourcetest_images/road_sample.jpg conf0.25多测试几张图片重点观察漏检和误检坑洼漏检说明模型学到的特征不够或者样本覆盖不足把普通阴影、路面水渍误判为坑洼说明背景干扰样本不够需要补充负样本或降低置信度阈值。5. PyQt5 检测界面开发与功能验证5.1 界面功能规划一个实用的 PyQt5 检测界面至少需要以下功能区域模型路径选择框默认指向best.pt。图片选择按钮支持 JPG、PNG 等常见图片格式。摄像头检测按钮打开本机摄像头并实时显示检测结果。图片预览区用QLabel显示原图和检测结果。检测信息栏显示当前图片检测到的坑洼数量、置信度和耗时。5.2 核心代码结构下面是一个简化版的 PyQt5 检测界面代码包含图片检测和模型加载可以直接运行做功能验证import sys from pathlib import Path import cv2 from PyQt5.QtCore import Qt from PyQt5.QtGui import QPixmap, QImage from PyQt5.QtWidgets import ( QApplication, QMainWindow, QLabel, QPushButton, QFileDialog, QVBoxLayout, QHBoxLayout, QWidget, QTextEdit ) from ultralytics import YOLO class PotholeWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(坑洼路面检测系统 - YOLOv8 PyQt5) self.setMinimumSize(900, 650) self.model YOLO(best.pt) # 默认加载训练好的权重 self.init_ui() def init_ui(self): self.image_label QLabel(请选择图片或打开摄像头) self.image_label.setAlignment(Qt.AlignCenter) self.image_label.setStyleSheet(background-color: #2d2d2d; color: #cccccc;) self.result_text QTextEdit() self.result_text.setFixedHeight(120) self.result_text.setReadOnly(True) btn_open QPushButton(选择图片) btn_open.clicked.connect(self.open_image) btn_camera QPushButton(打开摄像头) btn_camera.clicked.connect(self.open_camera) top_layout QHBoxLayout() top_layout.addWidget(btn_open) top_layout.addWidget(btn_camera) main_layout QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.image_label) main_layout.addWidget(self.result_text) container QWidget() container.setLayout(main_layout) self.setCentralWidget(container) def open_image(self): file_path, _ QFileDialog.getOpenFileName( self, 选择图片, , 图片文件 (*.jpg *.jpeg *.png *.bmp) ) if not file_path: return results self.model.predict(sourcefile_path, conf0.25, verboseFalse) annotated_img results[0].plot() # 返回 BGR 图像 # 统计检测到的坑洼数量 boxes results[0].boxes self.result_text.append(f检测文件: {Path(file_path).name}) self.result_text.append(f检测到坑洼数量: {len(boxes)}) # BGR 转 RGB 后在 QLabel 中显示 rgb_img cv2.cvtColor(annotated_img, cv2.COLOR_BGR2RGB) h, w, ch rgb_img.shape bytes_per_line ch * w qt_img QImage(rgb_img.data, w, h, bytes_per_line, QImage.Format_RGB888) pixmap QPixmap.fromImage(qt_img).scaled( self.image_label.width(), self.image_label.height(), Qt.KeepAspectRatio ) self.image_label.setPixmap(pixmap) def open_camera(self): # 正式的摄像头检测需要放到线程中避免界面卡死 self.result_text.append(摄像头检测功能需配合 QThread 实现这里先空实现) pass if __name__ __main__: app QApplication(sys.argv) window PotholeWindow() window.show() sys.exit(app.exec_())运行这个脚本点击“选择图片”程序会在当前文件夹中查找best.pt权重文件如果找不到需要把训练得到的权重复制到项目目录或在代码中写绝对路径。5.3 摄像头实时检测摄像头实时检测和图片检测的区别在于视频帧是连续输入的不能把predict写在界面主线程里否则画面会卡顿、窗口无响应。更稳妥的方案是用QThread子线程读取摄像头帧并执行推理检测完成后再通过信号把结果传回界面线程。关键思路是在run()循环中读取cap.read()。对当前帧执行model.predict(sourceframe)。把标注后的帧转成QImage通过信号发送给主窗口的QLabel刷新。如果只是做功能验证也可以先用cv2.VideoCapture(0)读取摄像头逐帧调用模型验证效果然后再优化到线程方案。5.4 界面卡死的排查界面点击按钮后无响应最可能的原因就是推理任务阻塞了主线程。图片检测耗时一般在几百毫秒到几秒摄像头逐帧推理耗时更长。如果在open_image里直接执行长时间的模型推理窗口会被冻结。解决方法是把所有耗时操作放到QThread或QRunnable中界面线程只负责接收结果并刷新控件。6. 批量任务与 API 接口扩展6.1 批量图片检测路面巡检经常会对一批图片进行统一筛查比如从巡检视频中按帧导出图片。用 YOLOv8 自带的source参数传入一个目录可以实现批量推理yolo predict modelbest.pt sourcetest_images/ saveTrue projectresults/ conf0.25也可以在 Python 中遍历目录灵活控制输出逻辑from pathlib import Path from ultralytics import YOLO model YOLO(best.pt) input_dir Path(test_images) output_dir Path(output_images) output_dir.mkdir(exist_okTrue) for img_path in input_dir.glob(*.jpg): result model.predict( sourcestr(img_path), conf0.25, saveTrue, projectstr(output_dir), namebatch, verboseFalse ) box_count len(result[0].boxes) print(f{img_path.name}: 检测到 {box_count} 个缺陷)批量任务跑起来后建议把识别结果同步写入 CSV方便和人工复检对照。输出目录最好按日期归档避免后期查找困难。6.2 封装成 HTTP API 接口如果不想每次都打开 PyQt5 界面可以把模型封装成接口供其他系统调用。这里用 Flask 做一个最小可用的接口示例pip install flaskfrom flask import Flask, request, jsonify from ultralytics import YOLO import cv2 import numpy as np app Flask(__name__) model YOLO(best.pt) app.route(/detect, methods[POST]) def detect(): if image not in request.files: return jsonify({error: 未上传图片}), 400 file request.files[image] img_bytes np.frombuffer(file.read(), np.uint8) img cv2.imdecode(img_bytes, cv2.IMREAD_COLOR) result model.predict(sourceimg, conf0.25, verboseFalse) boxes result[0].boxes.xyxy.cpu().numpy().tolist() confs result[0].boxes.conf.cpu().numpy().tolist() return jsonify({ count: len(boxes), boxes: boxes, confs: confs }) if __name__ __main__: app.run(host127.0.0.1, port5000)启动后用下面的 Python 脚本模拟一张图片请求import requests url http://127.0.0.1:5000/detect files {image: open(test_images/road_sample.jpg, rb)} response requests.post(url, filesfiles, timeout30) print(response.status_code) print(response.json())接口服务启动后需要在防火墙或网络策略中控制访问范围。部署到服务器时建议把host设置为127.0.0.1仅本机访问或者加一层接口鉴权避免被外部直接调用。6.3 批量任务队列设计做大规模巡检时单线程批量推理会比较慢尤其是 CPU 推理场景。如果想提升吞吐量可以基于多进程或任务队列实现使用concurrent.futures.ProcessPoolExecutor并行处理多个图片文件。使用 Redis RQ 或 Celery 构建异步任务队列。在 GPU 上增大batch参数用一次前向推理处理多张图片。需要注意的是多进程并行会占用更多内存GPU 显存也要根据单卡容量控制并发数。7. 资源占用与性能观察7.1 如何观察资源占用训练和推理过程中可以用系统工具实时观察资源占用。Windows 下打开任务管理器查看 GPU 显存和 CPU 使用率Linux 下用# 每 1 秒刷新一次 GPU 状态 watch -n 1 nvidia-smiPython 中也可以读取显存信息import torch if torch.cuda.is_available(): print(f显卡名称: {torch.cuda.get_device_name(0)}) print(f显存总量: {torch.cuda.get_device_properties(0).total_memory / 1024**3:.2f} GB) print(f当前已用: {torch.cuda.memory_allocated() / 1024**3:.2f} GB) print(f当前缓存: {torch.cuda.memory_reserved() / 1024**3:.2f} GB)7.2 CPU 推理与 GPU 推理的差异没有 NVIDIA 显卡的机器也能跑 YOLOv8 推理Ultralytics 支持 CPU 后端。CPU 推理的优势是兼容几乎所有电脑但速度比 GPU 慢很多。以常见的 640 输入尺寸为例GPU 上单张图片推理可能在几十毫秒到几百毫秒CPU 上则可能需要 1 到 5 秒甚至更久具体和 CPU 型号、模型规格都有关系。如果只是做几百张图片的离线批量检测CPU 完全够用如果要做摄像头实时检测建议至少让yolov8n跑在支持 CUDA 的显卡上。7.3 模型规格与推理参数对性能的影响影响资源占用和速度的主要因素有三个模型规格n最轻s中等m、l、x依次变重。输入分辨率imgsz640是默认值改成imgsz1280会显著增加计算量和显存占用对小目标检测有帮助。批量大小训练时batch越大梯度更新越稳定但显存占用线性增长推理时一次处理多张图也能提高吞吐但别超过显存上限。置信度阈值conf0.25比conf0.5会保留更多检测框误检可能增多但不会明显影响速度。7.4 降低显存占用的方法显存不足时可以按顺序尝试这些手段# 1. 换更小的模型 model YOLO(yolov8n.pt) # 2. 降低输入尺寸 model.train(datadataset/pothole.yaml, epochs50, imgsz512, batch4) # 3. 减少批量大小 model.train(datadataset/pothole.yaml, epochs50, imgsz640, batch2) # 4. 训练时开启梯度检查点 model.train(datadataset/pothole.yaml, epochs50, imgsz640, batch8, ampTrue)ampTrue表示自动混合精度能减少显存占用并提升训练速度。如果仍然 OOM说明当前显卡不太适合训练这个规格的模型建议换yolov8n或减小imgsz。7.5 端口冲突与进程残留启动 Flask 接口时如果遇到端口占用换一个端口即可app.run(host127.0.0.1, port5001)如果程序异常退出后 GPU 显存没有释放检查是不是有残留 Python 进程。Windows 下打开任务管理器结束相关进程Linux 下用ps -ef | grep python找到进程后kill。训练中断后再次训练记得清理原有的runs/detect输出目录避免结果混淆。8. 常见问题与排查方法问题现象可能原因排查方式解决方案pip install ultralytics失败网络问题或 Python 版本不兼容查看 pip 报错信息使用镜像源升级到 Python 3.9 以上import ultralytics报错依赖库版本冲突检查pip list中的 torch 版本重装 ultralytics或升级 PyTorchPyQt5 窗口无法弹出PyQt5 安装不完整或显示环境问题运行测试窗口脚本重装pyqt5检查显示服务加载best.pt报错权重文件路径不对或模型损坏确认文件是否存在、大小是否正常重新训练或从训练输出复制权重摄像头打开失败摄像头索引错误、驱动或权限问题用cv2.VideoCapture(0)单独测试尝试索引 1 或 2检查系统摄像头权限GPU 推理报 CUDA error显卡驱动与 PyTorch CUDA 版本不匹配torch.cuda.is_available()验证安装匹配的 CUDA 版本 PyTorch训练时显存不足 OOM模型规格过大或 batch 过大观察nvidia-smi是否接近满显存降低 batch、imgsz换小模型界面点击按钮后无响应推理在主线程执行阻塞界面观察窗口是否冻结把推理移到 QThread漏检严重训练样本不足或场景差异大分析错误案例图片补充对应场景数据增加训练轮数误检严重背景干扰、置信度阈值过低查看输出框的置信度分数调高conf补充负样本批量任务卡在某一文件图片损坏或模型推理异常定位具体文件路径跳过损坏文件添加异常捕获Flask 接口无法访问服务未启动、端口占用或防火墙拦截检查服务日志和端口换端口控制 host检查防火墙9. 最佳实践、合规与下一步9.1 工程化使用建议第一次跑通后建议按以下思路做工程化收尾保留一套最小可运行配置。项目目录固定为models/、datasets/、outputs/、ui/四个文件夹权重文件放在models/下数据增强脚本和训练脚本分离。批量任务必须加日志。每处理一张图片都记录时间、文件名、检测框数、置信度方便复检和问题回溯。接口服务要限制访问范围。未做鉴权的接口不要直接暴露到公网可以通过内网部署、API Token 或 Nginx 反向代理控制访问。发布前做效果复核。让标注人员和现场巡检人员各自抽检一批结果确认模型漏检率、误检率是否在可接受范围。定期用新采集的巡检图片做回测。道路环境随着季节变化建议每个月补充一批新样本并重新评估模型必要时做增量训练。9.2 隐私与合规提醒路面检测系统在真实场景中会采集到大量视频和图片其中可能包含车牌、人脸、建筑物外立面等敏感信息。要明确几个原则数据采集前确认用途和范围只保存和业务相关的路面画面标注、训练、存储流程中做好权限管理单位内部使用时要遵守网络安全和个人信息保护要求。涉及商用前要对模型输出结果做人工抽检避免因误检导致错误开工单。9.3 后续扩展方向当前系统已经覆盖了“图片检测 界面展示 批量推理 接口调用”这条主线后续可以从这几个方向继续扩展多类别检测把坑洼、裂缝、龟裂、修补痕迹分开标记做更细粒度统计。多摄像头接入PyQt5 界面中增加摄像头切换或者用多个QThread同时读取多个视频流。检测结果 GIS 化把巡检图片的 GPS 信息写到结果中在地图上标记病害位置。模型轻量化部署把best.pt导出为 ONNX 或 TensorRT 格式在嵌入式设备上部署。自动巡检报告批量任务结束后自动生成带统计图和表格的巡检报告直接交付给养护单位。坑洼路面检测系统的核心价值不在于界面多酷炫而在于能否把本地化目标检测能力和桌面工具稳定地组合在一起。建议先拿 20 到 50 张路面图片用yolov8n跑通完整流程确认每个环节都能工作再逐步扩大数据集、优化模型。最容易踩的坑是数据集标注不一致、模型路径写错、PyQt5 界面卡死这三类按上面排查表逐项处理即可。下一步可以从摄像头实时检测和批量巡检报告这两个方向继续做二者在实际项目中都是高频需求。