YOLOv8轮胎缺陷检测实战:源码解析、PyQt5 GUI与ONNX部署
简介本资源是一套基于YOLOv8的轮胎缺陷检测系统完整工程包面向从事工业质检、智能制造方向的开发者与深度学习学习者可用于轮胎图像中debris、side_cut、ground、side等缺陷的自动识别与分类。包内共149个文件以jpg与png图像样本、xml标注、py源码、pt与onnx模型文件为主另含yaml配置、csv评估记录及qrc界面资源压缩包约34.74MB覆盖数据、训练、推理与部署全流程。系统配套PyQt5精美GUI界面包含图像显示窗口与结果展示区域非专业人员也能直观操作同时提供评估指标曲线便于查看准确率、召回率与mAP等性能表现。目前已有494人学习下载适合希望快速复现轮胎缺陷检测方案、研究YOLOv8工程落地或进行二次开发的读者参考使用。1. 轮胎缺陷检测这套 YOLOv8 源码包拆开到底值不值得跑一遍工厂质检线上轮胎侧壁的 side_cut 和 debris 这两类缺陷肉眼盯久了极易漏判尤其是夜班。这套资源把「基于 YOLOv8 的轮胎缺陷检测系统」打包成了 python 源码 onnx 模型 评估指标曲线 PyQt5 GUI 的完整形态解压就能看到 results.csv、train_batch0.jpg、val_batch0_pred.jpg 这些训练过程产物说明它不是空壳 demo而是真跑过一轮训练留下的痕迹。它适合两类人一类是想拿现成权重快速验证轮胎缺陷检测可行性的算法工程师另一类是想学 YOLOv8 从训练到 GUI 落地全链路的 python 学习者。下面我按自己拆包复现的顺序把环境、推理、GUI、onnx 导出和踩坑点讲透。2. 环境与依赖torch 1.9.0cu111 这套组合为什么不能乱升2.1 版本锁定的理由摘要里写得很死windows10 anaconda3 python3.8 torch1.9.0cu111 ultralytics8.2.70。很多人第一反应是「torch 太老了直接上 2.x 不香吗」我一开始也这么想结果翻车。ultralytics 8.2.70 这个版本对 torch 2.x 的某些算子兼容性并不稳尤其是导出 onnx 时 torch.onnx.export 的 dynamic_axes 行为在 1.9 和 2.x 之间有差异导出的 onnx 模型在后续推理时会出现维度对不上的报错。所以这套源码包的环境不是随便写的是作者验证过的组合新手别自作主张升级。python3.8 也是同理PyQt5 在 3.8 上的 wheel 最全3.11 之后部分 PyQt5 版本要自己编译纯属给自己找麻烦。2.2 从零搭环境的可抄步骤先建独立环境别污染 baseconda create -n tire_yolov8 python3.8 -y conda activate tire_yolov8装 torch 时一定要带 cu111 的 index-url否则 conda 默认给你装 CPU 版后面推理慢到怀疑人生pip install torch1.9.0cu111 torchvision0.10.0cu111 -f https://download.pytorch.org/whl/torch_stable.html这里-f指定的是 PyTorch 官方 wheel 索引页cu111表示 CUDA 11.1 编译版本。装完用下面这行验证 GPU 是否真的挂上了import torch print(torch.__version__) # 期望输出 1.9.0cu111 print(torch.cuda.is_available()) # 期望 True如果第二行是 False八成是显卡驱动版本低于 CUDA 11.1 要求或者你装成了 CPU 版。前者去更新驱动后者重装。接着装 ultralytics 和 GUI 依赖pip install ultralytics8.2.70 pip install pyqt5 opencv-python numpy pandas matplotlibultralytics是 YOLOv8 的官方库负责模型加载和推理pyqt5撑起 GUIopencv-python做图像读写和预处理。这几个版本不用卡太死但 ultralytics 必须锁 8.2.70因为源码里的 API 调用是按这个版本写的。2.3 验证环境是否可用环境装完别急着跑 GUI先用命令行验证模型能不能加载。把 onnx 模型和源码放同一目录跑一段最小推理from ultralytics import YOLO model YOLO(best.onnx, taskdetect) # 加载 onnx 模型 results model(test_tire.jpg) # 对单张图推理 for r in results: print(r.boxes.cls, r.boxes.conf) # 打印类别和置信度taskdetect明确告诉 ultralytics 这是检测任务不加有时会误判。r.boxes.cls是类别索引r.boxes.conf是置信度。如果这一步能打印出 debris、side_cut 这些类别对应的索引说明环境和模型都没问题可以进 GUI 了。3. 推理与 GUIPyQt5 界面怎么把模型调用串起来3.1 推理主流程拆解这套系统的核心逻辑其实就四步读图 → 预处理 → 模型推理 → 结果可视化。源码里通常封装成一个 detect 函数GUI 点「开始检测」时调用它。预处理部分 YOLOv8 内部已经做了 letterbox 缩放你不需要手动 resize但要注意输入图像通道顺序opencv 读进来是 BGR直接喂给模型会偏色常见做法是转成 RGBimport cv2 img cv2.imread(test_tire.jpg) # BGR img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转 RGBcv2.COLOR_BGR2RGB这个转换看着不起眼但不做的话检测框位置可能对类别置信度却会掉一截属于典型的玄学问题其实是通道顺序导致的。3.2 GUI 与模型解耦的写法PyQt5 界面最容易写崩的地方是把推理逻辑直接塞进按钮的槽函数里一旦推理卡住整个界面就假死。正确做法是用 QThread 把推理放到子线程from PyQt5.QtCore import QThread, pyqtSignal class DetectThread(QThread): finished pyqtSignal(object) # 推理完成信号携带结果 def __init__(self, model, img_path): super().__init__() self.model model self.img_path img_path def run(self): results self.model(self.img_path) # 子线程里跑推理 self.finished.emit(results) # 发信号回主线程更新 UIpyqtSignal(object)定义一个能传任意对象的信号finished.emit(results)把推理结果发回主线程。主线程收到信号后再更新图像显示区域这样界面不会卡。这是 PyQt5 做深度学习 GUI 的标准套路源码里如果没这么写你自己改一下体验提升明显。3.3 结果可视化与评估指标曲线检测框画图用 ultralytics 自带的results[0].plot()就行返回的是带框的 numpy 数组直接转 QImage 显示。评估指标曲线这块资源里给了 results.csv里面记录了每轮的 box_loss、cls_loss、mAP50、mAP50-95 等。想自己画曲线import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(results.csv) df.columns df.columns.str.strip() # 列名可能带空格先清理 plt.plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) plt.plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) plt.xlabel(epoch); plt.ylabel(mAP); plt.legend(); plt.show()df.columns.str.strip()这行是血泪经验YOLOv8 导出的 csv 列名前后常带空格不清理的话df[metrics/mAP50(B)]会 KeyError。mAP50 看的是 IoU0.5 时的平均精度mAP50-95 是 IoU 从 0.5 到 0.95 取平均后者更能反映框的定位精度。轮胎缺陷检测里 side_cut 这种细长目标mAP50-95 通常比 mAP50 掉得多属于正常现象。4. ONNX 模型部署从 pytorch 导出到跨框架推理的边界4.1 为什么资源里同时给 pt 和 onnxonnx 是开放格式好处是脱离 pytorch 环境也能跑。工厂现场部署时目标机器不一定装得了 torch但 onnxruntime 轻量得多。资源里给 onnx 模型说明作者考虑到了实际部署场景。从 pt 导 onnx 的命令yolo export modelbest.pt formatonnx imgsz640 opset12 simplifyTrueimgsz640是导出时的输入尺寸必须和训练时一致opset12是 onnx 算子集版本太低不支持某些算子太高部分推理引擎不认12 是比较稳的选择simplifyTrue会调用 onnx-simplifier 做图优化去掉冗余节点推理能快一点。4.2 onnxruntime 推理的输入输出对齐用 onnxruntime 跑推理时最常翻车的是输入 shape 和数据类型import onnxruntime as ort import numpy as np sess ort.InferenceSession(best.onnx, providers[CUDAExecutionProvider]) input_name sess.get_inputs()[0].name img np.random.randn(1, 3, 640, 640).astype(np.float32) # NCHW 格式 outputs sess.run(None, {input_name: img})providers[CUDAExecutionProvider]指定用 GPU没有 GPU 就换成CPUExecutionProvider。输入必须是(1, 3, 640, 640)的 float32NCHW 顺序归一化到 0-1。YOLOv8 导出的 onnx 输出通常是(1, 84, 8400)84 4 个框坐标 80 类8400 是候选框数量。如果你训的是 4 类输出应该是(1, 8, 8400)拿到输出后还要自己做 NMS这部分 ultralytics 内部帮你做了但纯 onnxruntime 部署得自己写。4.3 量化到 int8 的取舍热搜里有人问 onnx 量化 int8这里说清楚int8 量化能把模型体积压到 1/4推理速度提升明显但轮胎缺陷检测里 debris 这种小目标量化后精度掉得厉害。我的建议是先在验证集上对比量化前后的 mAP掉超过 3 个点就别用。量化命令yolo export modelbest.pt formatonnx int8True datadata.yamldatadata.yaml是量化校准用的数据集配置必须提供否则量化误差会很大。没有校准数据就硬量化等于开盲盒。5. 避坑与排查这几处报错我替你踩过了5.1 现象GUI 启动报「No module named PyQt5.sip」原因PyQt5 安装不完整或者 conda 和 pip 混装导致 sip 版本冲突。解决先pip uninstall pyqt5 pyqt5-sip再pip install pyqt55.15.9 pyqt5-sip12.12.1版本对齐后基本能解决。5.2 现象推理时 CUDA out of memory原因默认 batch 或输入尺寸太大显存不够。解决推理时把 imgsz 从 640 降到 416或者加halfTrue用半精度。GUI 里如果没暴露这个参数去源码里找model()调用处手动加。5.3 现象检测框位置整体偏移原因预处理时做了 resize 但没做 letterbox导致长宽比失真。解决别自己 resize直接用 ultralytics 的model(img_path)它内部做了 letterbox框位置会自动映射回原图。5.4 现象onnx 模型推理结果和 pt 不一致原因导出时 opset 或 imgsz 和训练不一致或者归一化参数不同。解决导出时严格对齐训练配置推理时确认输入归一化到 0-1别用 0-255。5.5 现象results.csv 画曲线时 KeyError原因列名带空格或 BOM 头。解决df.columns df.columns.str.strip()如果还不行用df.columns df.columns.str.replace(\ufeff, )去掉 BOM。6. 进阶技巧用 results.csv 反推模型该不该继续训拿到这套资源后很多人跑通 GUI 就停了其实 results.csv 里藏着模型还能不能提升的线索。我一般会做三件事。第一看 mAP50 曲线是否还在涨。如果最后 20 个 epoch 的 mAP50 曲线已经平了说明模型收敛了再加 epoch 意义不大如果还在缓慢上升可以适当加训。第二看 box_loss 和 cls_loss 的比值。cls_loss 明显高于 box_loss说明分类难可能是 debris 和 ground 这两类特征太像需要补充区分性更强的样本。第三看 val 和 train 的 mAP 差距。差距超过 10 个点过拟合无疑加数据增强或者加 dropout。具体操作上我会写个小脚本自动判断import pandas as pd df pd.read_csv(results.csv) df.columns df.columns.str.strip() last20 df.tail(20) map_gain last20[metrics/mAP50(B)].iloc[-1] - last20[metrics/mAP50(B)].iloc[0] if map_gain 0.005: print(已收敛建议停训) else: print(f仍在提升近20轮涨了 {map_gain:.4f}可继续)tail(20)取最后 20 轮map_gain是这 20 轮的 mAP 增量。阈值 0.005 是我自己定的经验值低于这个数基本就是噪声了。这个判断比拍脑袋决定要不要继续训靠谱得多。还有个技巧是拿 val_batch0_pred.jpg 和 val_batch0_labels.jpg 对比看。前者是预测结果后者是标注真值两张图叠着看能直观发现模型在哪些场景漏检。我一般会把这两张图并排打开重点看 side_cut 这种细长目标漏检往往集中在这类。从那以后我每次拿到新的检测权重都强制走一遍「看曲线 对比预测与标注」的流程比盲目调参省时间。希望帮到你。本文还有配套的精品资源点击获取