尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于YOLOv8与PyQt5的路面坑洞检测系统实战

基于YOLOv8与PyQt5的路面坑洞检测系统实战 1. 路面坑洞检测系统整体设计思路拆解1.1 为什么选择YOLOv8而不是传统图像处理方法做路面坑洞检测这件事我最早试过用传统的边缘检测加阈值分割。OpenCV的Canny算子配合形态学操作在光照均匀、坑洞边缘清晰的理想图片上确实能跑出结果但一放到真实道路场景就崩了——阴影、水渍、修补痕迹、井盖边缘全被误判成坑洞误报率高得没法看。后来换成YOLOv8核心原因就一个坑洞这玩意儿的视觉特征太不固定了。有的坑洞是深色圆形有的是不规则裂纹状有的被雨水填满反光传统方法根本没法用一套固定规则覆盖所有情况。YOLOv8作为单阶段检测器把目标检测当作回归问题来处理直接在特征图上预测边界框和类别概率。相比Faster R-CNN这类两阶段方案它的推理速度快了一个数量级在GTX 1660 Ti这种级别的显卡上跑640×640输入FP16精度下轻松跑到60帧以上。路面巡检场景要么是车载实时检测要么是无人机航拍后批量处理速度都是硬指标。而且YOLOv8的Anchor-Free设计省去了调Anchor框的麻烦对小目标和不规则形状的适应性更好这对坑洞检测来说很关键。另一个选YOLOv8的实际理由是生态成熟。Ultralytics这个库把训练、验证、导出、推理全流程封装得很干净你不需要自己去写数据加载器、学习率调度器、NMS后处理这些轮子。对于个人开发者或者小团队来说能把精力集中在数据质量和业务逻辑上而不是跟框架搏斗。1.2 PyQt5在桌面端检测系统中的角色定位模型训练出来只是第一步真正交付给用户的是一个能双击打开、能选图片、能看结果、能导出报告的桌面软件。PyQt5在这里承担的就是这个“最后一公里”的活儿。选PyQt5而不是Tkinter或者Web方案有几个很实际的考量。Tkinter的控件太简陋做个带缩略图列表、可缩放图像预览、进度条、日志面板的界面会非常痛苦。Web方案比如Flask前端虽然界面漂亮但部署时要装Python环境、开服务、配端口对非技术用户不友好而且实时视频流的延迟也不好控制。PyQt5基于Qt框架控件丰富、跨平台、性能好打包成exe后用户直接运行不需要任何额外配置。PyQt5和YOLOv8的集成方式也很直接主线程负责界面渲染和用户交互子线程跑模型推理通过信号槽机制把检测结果传回主线程更新界面。这样既不会卡死界面又能保证检测的流畅性。我实测下来在i5-10400GTX 1660 Ti的配置上单张图片检测加界面刷新在200ms以内视频流也能稳定在25帧以上。1.3 系统整体架构与数据流设计整个系统的架构可以分成四层数据输入层、推理引擎层、业务逻辑层、界面展示层。数据输入层负责接收图片、视频文件或者摄像头流。这里要注意的是图像预处理——YOLOv8要求输入是RGB格式、尺寸为32的倍数所以需要做resize和padding。我一般把输入统一缩放到640×640保持长宽比不足的部分用灰色填充这样不会因为拉伸导致坑洞变形。推理引擎层就是加载训练好的.pt权重或者导出的ONNX/TensorRT引擎。如果追求极致速度可以把模型导出成TensorRT在NVIDIA显卡上能再快30%到50%。不过TensorRT的部署稍微麻烦一点需要装TensorRT、pycuda还要处理版本兼容问题。对于大多数场景直接用PyTorch的.pt文件推理已经够用了。业务逻辑层处理检测结果的后处理置信度过滤、NMS去重、坑洞面积估算、严重程度分级。这里可以加一些业务规则比如连续多帧检测到同一位置有坑洞才判定为真实坑洞避免单帧误检。界面展示层用PyQt5的QMainWindow做骨架左边是文件列表和参数控制面板中间是图像显示区域右边是检测结果统计和日志。图像显示用QLabel配合QPixmap如果要支持缩放和拖拽可以重写QGraphicsView。2. 环境搭建与YOLOv8训练自己的数据集2.1 环境配置的坑与推荐组合YOLOv8的环境配置说简单也简单说坑也多。官方推荐的是Python 3.8以上、PyTorch 1.8以上。但实际搭配的时候CUDA版本、PyTorch版本、显卡驱动版本三者必须匹配否则要么装不上要么跑起来报错。我踩过最典型的坑是显卡是GTX 1660 Ti驱动版本比较老直接pip install ultralytics装上了最新的PyTorch 2.x结果一跑推理就报CUDA error。后来查了半天才发现是PyTorch 2.x要求的CUDA版本和驱动不匹配。解决办法要么升级显卡驱动要么降PyTorch版本。实测下来比较稳的组合是Python 3.9 PyTorch 1.13.1 CUDA 11.7 cuDNN 8.5。这个组合在GTX 1660 Ti、RTX 3060、RTX 4090上都跑过没出过兼容性问题。安装命令如下pip install torch1.13.1cu117 torchvision0.14.1cu117 --extra-index-url https://download.pytorch.org/whl/cu117 pip install ultralytics8.0.145PyQt5的安装相对独立直接pip install PyQt5就行。但要注意如果你用的是Anaconda环境conda install pyqt和pip install PyQt5可能会冲突建议统一用pip安装。另外PyQt5的版本不要装太新的5.15.x系列比较稳定5.15.9是我用得最多的版本。注意安装PyQt5时如果遇到“opengl导致pyqt5界面无显示”的问题通常是因为显卡驱动和Qt的OpenGL渲染后端不兼容。可以在代码开头加上QApplication.setAttribute(Qt.AA_UseSoftwareOpenGL)强制使用软件渲染或者设置环境变量QT_OPENGLsoftware。这个坑我在一台老笔记本上遇到过界面能出来但一片黑加了这个设置就正常了。2.2 路面坑洞数据集的采集与标注数据质量决定模型上限这句话在坑洞检测里体现得淋漓尽致。我一开始用网上找的公开数据集大概两千多张图训练出来的模型在测试集上mAP能到0.75但一放到实际道路上就各种漏检。后来自己拿着手机去拍了三千多张覆盖了晴天、雨天、清晨、傍晚、不同路面材质、不同坑洞大小重新训练后mAP提升到0.89实际路测的漏检率也降下来了。采集的时候有几个要点角度要多样不要只从正上方拍车载视角、斜45度、近距离特写都要有光照要覆盖强光、阴影、逆光、夜间路灯下都得拍负样本要足井盖、修补痕迹、油渍、阴影这些容易被误检的东西要专门拍一批作为背景类。标注用LabelImg或者Roboflow都行格式选YOLO格式每张图对应一个txt文件内容是类别 x_center y_center width height坐标都归一化到0到1之间。坑洞检测一般就一个类别所以类别id都是0。标注的时候边界框要尽量贴紧坑洞边缘但不要把周围的裂纹也框进去否则模型会学到错误的特征。数据集划分按8:1:1的比例分训练集、验证集、测试集。如果数据量少于两千张建议用7:2:1验证集多一点方便调参。划分的时候要注意同一段路的图片不要同时出现在训练集和验证集里否则验证结果会虚高。2.3 YOLOv8训练参数详解与freeze技巧YOLOv8的训练入口很简洁一行命令就能跑yolo detect train datapothole.yaml modelyolov8s.pt epochs100 imgsz640 batch16 lr00.01但参数怎么设是有讲究的。model选哪个尺寸很关键yolov8n最快但精度最低yolov8s是速度和精度的平衡点yolov8m和yolov8l精度更高但推理慢。坑洞检测我推荐从yolov8s起步如果精度不够再换yolov8m。imgsz设640是标配如果坑洞在图像里占比很小可以设到1280但显存占用会翻倍。freeze参数是迁移学习的神器。如果你用的是预训练权重前几层学的是通用特征边缘、纹理、颜色这些对坑洞检测也有用不需要重新学。freeze10表示冻结前10层只训练后面的检测头。这样做的好处是训练快、不容易过拟合尤其适合数据量不大的情况。我一般先用freeze10跑50轮再解冻全部跑50轮效果比直接全量训练好。学习率lr0默认0.01如果loss震荡得厉害就降到0.001。batch根据显存来GTX 1660 Ti 6GB显存跑yolov8s640输入batch8比较稳batch16会OOM。epochs一般100到300看验证集loss什么时候不再下降就停。训练过程中可以用TensorBoard看损失曲线tensorboard --logdir runs/detect/train重点关注box_loss、cls_loss、dfl_loss三条曲线。如果训练loss下降但验证loss上升说明过拟合了要加数据增强或者减小模型。YOLOv8默认开了mosaic、mixup、随机翻转等增强一般够用。2.4 模型评估与导出部署格式训练完之后在验证集上跑评估yolo detect val modelruns/detect/train/weights/best.pt datapothole.yaml看mAP50和mAP50-95两个指标。mAP50是IoU阈值0.5时的平均精度一般能到0.85以上就算不错。mAP50-95更严格能到0.6以上就说明框的位置很准。导出格式根据部署平台来选。如果是在PC上用PyTorch推理直接用.pt就行。如果要部署到RK3588这类边缘设备需要导出成ONNX再转RKNN。如果是在NVIDIA显卡上追求极致速度导出TensorRTyolo export modelbest.pt formatengine halfTrue device0halfTrue表示用FP16精度速度能快一倍精度损失很小。TensorRT引擎和显卡型号绑定换显卡要重新导出。3. PyQt5界面设计与YOLOv8集成实操3.1 界面布局设计与分辨率适配PyQt5做界面我习惯用QMainWindow加QWidget的组合。主窗口分成三个区域左侧控制面板、中间图像显示区、右侧结果面板。用QSplitter做分割用户可以拖动调整比例。分辨率适配是个容易被忽视的问题。我在1920×1080的显示器上设计好的界面拿到1366×768的笔记本上就显示不全。解决办法是用布局管理器QVBoxLayout、QHBoxLayout、QGridLayout而不是固定坐标同时设置窗口的最小尺寸。对于图像显示区用QGraphicsView代替QLabel它自带滚动条和缩放功能适配不同分辨率更省心。from PyQt5.QtWidgets import QMainWindow, QSplitter, QWidget, QVBoxLayout from PyQt5.QtCore import Qt class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setMinimumSize(1200, 700) splitter QSplitter(Qt.Horizontal) splitter.addWidget(self.left_panel) splitter.addWidget(self.center_panel) splitter.addWidget(self.right_panel) splitter.setSizes([250, 700, 250]) self.setCentralWidget(splitter)左侧面板放文件选择按钮、模型加载按钮、置信度滑块、IoU滑块。中间放QGraphicsView显示图像。右侧放QTreeWidget显示检测结果列表每一行是一个坑洞包含序号、置信度、面积估算。QTreeWidgetItem里可以嵌入QComboBox让用户手动修正类别这个在需要人工复核的场景很实用。3.2 多线程推理与信号槽机制PyQt5的界面刷新必须在主线程而YOLOv8推理是计算密集型任务如果直接在主线程跑界面会卡死。标准做法是把推理放在QThread子线程里通过pyqtSignal把结果发回主线程。from PyQt5.QtCore import QThread, pyqtSignal from ultralytics import YOLO class InferenceThread(QThread): result_ready pyqtSignal(object) def __init__(self, model_path, image_path, conf): super().__init__() self.model YOLO(model_path) self.image_path image_path self.conf conf def run(self): results self.model(self.image_path, confself.conf) self.result_ready.emit(results)主线程里连接信号self.thread InferenceThread(model_path, image_path, conf) self.thread.result_ready.connect(self.update_ui) self.thread.start()这里有个细节YOLO模型加载比较慢首次加载要几秒不要在每次推理时都重新加载。可以把模型加载放在初始化阶段或者用一个全局的模型缓存。另外如果连续处理多张图片可以用队列机制避免频繁创建销毁线程。注意子线程里绝对不能操作UI控件否则会报“QObject::setParent: Cannot set parent, new parent is in a different thread”之类的错误。所有UI更新都必须通过信号槽在主线程完成。3.3 检测结果可视化与坑洞面积估算YOLOv8返回的Results对象里包含boxes、masks、keypoints等信息。对于坑洞检测我们主要用boxes。每个box有xyxy坐标、置信度、类别id。可视化的时候用OpenCV画框和标签import cv2 for box in results[0].boxes: x1, y1, x2, y2 box.xyxy[0].cpu().numpy() conf box.conf[0].cpu().numpy() cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2) cv2.putText(img, fpothole {conf:.2f}, (int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)面积估算需要知道像素和实际尺寸的映射关系。如果摄像头是固定高度拍的可以事先标定在路面上放一个已知尺寸的参照物比如A4纸算出每像素对应多少平方厘米。然后坑洞面积 边界框面积 × 像素当量。这个估算比较粗糙因为边界框是矩形而坑洞是不规则的但作为严重程度分级够用了。严重程度可以按面积分三级小于500平方厘米为轻度500到2000为中度大于2000为重度。不同级别用不同颜色标注轻度绿色、中度黄色、重度红色界面上看起来一目了然。3.4 视频流处理与实时检测优化视频流检测比单张图片复杂因为要处理帧率、缓冲、显示同步的问题。我用OpenCV的VideoCapture读帧然后在子线程里循环推理每处理完一帧就发信号更新界面。class VideoThread(QThread): frame_ready pyqtSignal(object, object) def run(self): cap cv2.VideoCapture(self.video_path) while self.running: ret, frame cap.read() if not ret: break results self.model(frame, confself.conf, verboseFalse) annotated results[0].plot() self.frame_ready.emit(frame, annotated)实时检测的性能瓶颈通常在模型推理和图像格式转换上。优化手段有几个用TensorRT引擎替代PyTorch推理速度能提升30%到50%降低输入分辨率到416或320速度更快但小坑洞可能漏检跳帧处理每两帧检测一次中间帧用上一帧的结果视觉上也能接受。GTX 1660 Ti跑yolov8s640输入单帧推理大概15到20毫秒加上前后处理整体能到30帧左右。如果换成yolov8n能到50帧以上。RK3588上跑yolov8s用NPU加速大概能到15到20帧也够用了。4. 常见问题排查与部署避坑指南4.1 环境与依赖问题速查问题现象可能原因解决方法import torch报DLL load failedCUDA版本与PyTorch不匹配检查显卡驱动版本重装对应CUDA的PyTorchPyQt5界面黑屏无显示OpenGL渲染后端不兼容设置QT_OPENGLsoftware或代码中强制软件渲染yolo命令找不到ultralytics未正确安装pip install ultralytics检查Scripts目录是否在PATH训练时CUDA out of memorybatch太大或imgsz太大减小batch到8或4或降低imgsz到416推理速度慢未用GPU或未用FP16确认device0导出时加halfTrue4.2 模型训练中的典型问题与调优loss不下降先检查数据标注有没有问题用yolo detect train的plotsTrue生成标注可视化图看看框是不是画歪了。如果标注没问题可能是学习率太大降到0.001试试。还有可能是预训练权重没加载成功检查modelyolov8s.pt路径对不对。验证集mAP远低于训练集典型过拟合。加数据增强degrees10、translate0.1、scale0.5或者用更小的模型yolov8n或者加dropout。如果数据量实在少用freeze冻结主干网络。小坑洞漏检严重坑洞在图像里占比小的时候下采样到640后可能只剩几个像素。解决办法是把imgsz提到1280或者用切片推理把大图切成小块分别检测再合并。YOLOv8的P2层对小目标更友好但需要改网络结构新手不建议动。误检井盖和阴影负样本不够。专门收集一批井盖、阴影、修补痕迹的图片不标注任何目标作为背景图加入训练集。YOLOv8会自动把这些区域学成背景类。4.3 PyQt5界面卡顿与崩溃排查界面卡顿九成是因为在主线程跑了耗时操作。检查所有按钮的槽函数凡是涉及模型推理、文件IO、网络请求的统统扔到QThread里。另外频繁更新UI也会卡比如视频流每帧都刷新QGraphicsView可以限制刷新率到25fps或者用双缓冲。界面崩溃常见于对象生命周期管理不当。比如子线程还在跑的时候用户关闭了窗口线程没正确退出程序就崩了。解决办法是在closeEvent里先停止线程def closeEvent(self, event): self.running False self.thread.quit() self.thread.wait() event.accept()还有一个坑是PyQt5和OpenCV的冲突。OpenCV的imshow和PyQt5的窗口一起用的时候有时候会闪退。建议统一用PyQt5显示图像不要混用cv2.imshow。4.4 边缘设备部署的注意事项如果要把系统部署到RK3588这类边缘设备上流程是PC上训练得到best.pt导出ONNX再用RKNN-Toolkit2转成RKNN模型最后在板子上用RKNN的Python API推理。这里有几个坑ONNX的opset版本要和RKNN-Toolkit2兼容一般用opset12输入尺寸要固定不能动态量化的时候要有足够的校准图片否则精度掉得厉害。正点原子的RK3588教程里有一整套YOLOv8部署流程跟着走基本能跑通。但要注意他们的系统镜像版本和RKNN-Toolkit2版本要匹配版本不对会报各种奇怪的错误。我建议先在PC上把整个流程跑通再往板子上移植这样出问题容易定位。5. 系统扩展与性能提升方向5.1 模型轻量化改进思路如果要在手机或者低功耗设备上跑yolov8n还是太重。可以考虑几个轻量化方向用MobileNetV3或ShuffleNetV2替换YOLOv8的CSPDarknet主干用深度可分离卷积替换普通卷积剪枝掉冗余的通道。这些改进能显著降低参数量和计算量但需要一定的模型改造经验。另一个思路是用知识蒸馏用大模型yolov8m教小模型yolov8n让小模型学到大模型的泛化能力。Ultralytics官方没有直接支持蒸馏但可以自己写训练循环实现。5.2 多任务扩展分类与分割坑洞检测只给出位置和大小但实际养护决策还需要知道坑洞的类型龟裂、坑槽、车辙和深度。可以在YOLOv8的基础上加一个分类头做多任务学习。YOLOv8本身支持detect、segment、classify、pose四种任务如果要同时做检测和分类可以改模型结构加一个分支。分割任务用YOLOv8-seg能输出坑洞的像素级掩码面积估算更准。但分割的标注成本比检测高很多需要画多边形而不是矩形框。如果预算有限检测加面积估算已经能满足大部分需求。5.3 数据闭环与持续优化系统上线后用户每次检测的图片和结果都可以存下来形成一个数据闭环。定期把误检和漏检的样本挑出来人工修正标注后加入训练集重新训练模型。这样模型会越用越准形成一个正向循环。实现上可以在界面加一个“反馈”按钮用户点击后把当前图片和检测结果保存到指定目录同时记录用户的修正操作。后台定期跑一个脚本把新数据合并到训练集自动触发训练和评估。这套流程搭起来之后模型的迭代效率会高很多。我个人在实际操作中的体会是坑洞检测这个任务数据质量比模型结构重要得多。与其花时间改网络、调参数不如多花精力在数据采集和标注上。我见过太多人用yolov8n加高质量数据效果比yolov8l加脏数据好一大截。另外PyQt5的界面不用追求花哨稳定、响应快、操作直观才是第一位的。用户不关心你用了什么动画效果他们只关心点一下按钮能不能立刻出结果。
返回列表