ARTICLE DETAIL

资讯详情

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

YOLOv8钢材缺陷检测:从数据集到Qt GUI完整实现

YOLOv8钢材缺陷检测:从数据集到Qt GUI完整实现 简介面向工业质检场景的YOLOv8钢材缺陷检测完整方案整合了训练好的pt模型权重、PR与loss曲线以及可视化PyQt5图形界面适合需要快速落地缺陷识别或学习检测流程的开发者。压缩包共2000个文件、约94.62MB其中1400余个txt标签与346张jpg图像构成已标注数据集xml与txt两种格式分别保存另有171个py脚本覆盖训练、推理、评估和界面逻辑yaml配置、UI文件、PDF说明与模型权重等一应俱全目录划分清晰便于按模块调取。资源还附有用labelimg标注完成的多种常见钢材缺陷类别图片支持在GUI中直接检测图片、视频并调用摄像头也能对照PR曲线与loss曲线评估模型表现快速判断收敛情况。已有605人学习下载适合作为从数据标注、模型训练到界面部署的全链路参考可显著减少重复调参与整理数据的耗时。1. 钢材缺陷检测为什么值得用YOLOv8做一套带GUI的完整工具做工业质检的人大概都有过这种体验产线上每天几千张钢板表面图像靠人工盯屏幕眼睛酸了就开始漏检CRT裂纹、麻点、夹杂这类小目标偏偏又最考验眼力。把YOLOv8训练好的缺陷检测权重接进一个Qt界面让操作员框选图片就能看到缺陷类型和位置这不是实验室自嗨是能直接顶到产线工位上的落地方案。这条技术路线的核心价值在于YOLOv8的权重文件只是一个产物真正决定检测效果的是数据集怎么整理、标注怎么规范、训练参数怎么调以及最后怎么把模型封装成非技术人员也能用的工具。数据、权重、GUI三件事环环相扣任何一环偷懒整套系统都会在真实场景里翻车。本文面向的读者是那些手里有钢材图像、想做缺陷检测但还没跑通全流程的工程师跟着这条路径走完你手里会多出一套能用的检测工具而不是一个孤零零的模型文件。2. 先搞定数据集从源图像到YOLOv8训练格式的完整整理流程2.1 公开钢材缺陷数据集与自建数据的取舍钢材缺陷检测领域有几个公开数据集可以当起点最常用的是东北大学发布的NEU-DET包含1800张热轧带钢表面图像每张分辨率200x200覆盖 crazing、inclusion、patches、pitted_surface、rolled-in_scale、scratches这六类缺陷。另一个可选是GC10-DET包含十类冷轧带钢缺陷图像尺寸更大、场景更接近工业实际但标注格式和类别定义需要额外处理。公开数据集的问题在于NEU-DET单张图只有200x200目标占比大、背景单一训练出来的模型到了产线真实环境会明显掉点。GC10-DET样本量偏少每类只有几十到一百多张直接训练容易过拟合。我一般把公开数据当作基准测试集用真实项目里还是要从现场采集图像至少覆盖不同光照、不同轧制速度、不同钢种表面状态。自建数据要控制的变量有四个拍摄角度固定、光源方向一致、曝光参数统一、图像分辨率不要低于模型输入分辨率。钢材表面是反光材质光源角度一变同一处缺陷在图像里的形态差异比缺陷本身的类间差异还要大这一点在标注阶段就会坑人。2.2 用Labelme标注并转换为YOLOv8格式的脚本YOLOv8要求标签文件是txt格式每行一个目标格式为class_id x_center y_center width height坐标全部归一化到0-1。Labelme默认输出JSON多边形需要写脚本转换。这个转换脚本是整个数据管线里第一个容易出错的环节坐标归一化计算和类别映射必须仔细下面给出一个我常用的转换脚本import json import os import glob from pathlib import Path def labelme_to_yolov8(json_path, output_dir, classes): os.makedirs(output_dir, exist_okTrue) with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] txt_lines [] for shape in data[shapes]: label shape[label] if label not in classes: print(f[跳过] 标签 {label} 不在类别表: {json_path}) continue class_id classes.index(label) points shape[points] # 计算多边形外接矩形 xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # 归一化到0-1YOLO格式 x_center ((x_min x_max) / 2) / img_w y_center ((y_min y_max) / 2) / img_h box_w (x_max - x_min) / img_w box_h (y_max - y_min) / img_h # 边界裁剪防止坐标越界 x_center max(0, min(1, x_center)) y_center max(0, min(1, y_center)) box_w max(0, min(1, box_w)) box_h max(0, min(1, box_h)) txt_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) if txt_lines: txt_path Path(json_path).stem .txt with open(os.path.join(output_dir, txt_path), w) as f: f.write(\n.join(txt_lines)) classes [crazing, inclusion, patches, pitted_surface, rolled-in_scale, scratches] json_files glob.glob(labelme_json/*.json) for jf in json_files: labelme_to_yolov8(jf, yolov8_labels, classes) print(f转换完成共处理 {len(json_files)} 个JSON文件)这个脚本的核心是把Labelme的多边形标注转换成外接矩形框。注意钢材缺陷里有一部分是细长条状比如划痕和裂纹外接矩形会引入大量背景噪声。坐标归一化计算里中心点坐标是(x_min x_max) / 2 / img_w有些新手会误写成直接除以宽这就是框位置漂移的常见原因。如果标注的形状接近多边形而非矩形直接转外接框会导致检测框过大。此时可以选择用旋转矩形标注但YOLOv8原生不支持旋转目标要么接受外接框的误差要么换成YOLOv8-OBB变体。对钢材表面缺陷来说大多数缺陷形态趋近椭圆或长条外接框造成的IoU损失在可接受范围内。2.3 数据集划分与类别平衡训练集、验证集、测试集的正确分法数据集划分看似简单但钢材缺陷数据集有个典型陷阱同一张钢板表面连续的图像之间特征高度相似如果不按来源分组直接随机划分训练集和验证集会出现数据泄漏验证指标虚高上了产线立刻现原形。我一般按图像来源分组划分而不是逐张随机打乱。如果一批图像来自同一卷钢带的不同位置就把它们分到同一个集合里。对于NEU-DET这种公开数据直接随机划分问题不大因为每张图独立采集现场采集的数据必须按卷、按批次划分。划分脚本建议写到文件里保存映射关系方便复现。python随机数种子固定random.seed(42)防止每次划分结果不同导致实验结果对比失真。还要检查每个集合里每一类的样本数特别是小样本类别如rolled-in_scale往往占比很低如果测试集里某一类只有几张图指标波动会很大模型调参时会被噪声干扰。3. 训练出自己的缺陷检测权重参数、损失曲线与模型选型3.1 环境配置Ubuntu 20.04 CPU版本也能跑通的最小方案YOLOv8的环境配置门槛不高但坑不少。采CPU版本在Ubuntu 20.04上跑是可行的训练慢但能跑推理速度在单张200x200的图上CPU也就一两百毫秒完全够GUI演示使用。下面是CPU环境的最小配置路径conda create -n yolo python3.9 conda activate yolo pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu这里有个关键点CPU版本的PyTorch一定要用--index-url指定CPU源直接pip install torch默认拉取的是CUDA版本虽然CUDA版的在纯CPU机器上也能跑但体积大且依赖NVIDIA驱动装完之后经常因为驱动版本问题导致导入报错。ultralytics包会连带装上opencv、matplotlib、pandas等依赖。如果机器的Python环境里已有老版本的numpy装完ultralytics后建议重新检查依赖版本numpy版本不匹配会导致训练时矩阵运算报错这是YOLOv8环境搭建里的高频问题。3.2 训练命令与必调参数详解batch size、imgsz、epochs怎么设训练前先准备好数据集目录结构YOLOv8要求images/train、images/val、labels/train、labels/val四目录配合一个data.yaml文件指定路径和类别名。data.yaml里的路径建议写绝对路径相对路径在换目录跑训练时容易找不到文件。train: /home/user/steel_defect/images/train val: /home/user/steel_defect/images/val test: /home/user/steel_defect/images/test nc: 6 names: [crazing, inclusion, patches, pitted_surface, rolled-in_scale, scratches]训练命令用官方CLI即可但参数需要根据自己的数据规模调整yolo detect train datasteel_defect.yaml modelyolov8s.pt epochs120 imgsz640 batch16 lr00.01 patience20逐个拆解参数modelyolov8s.pt是yolov8s的预训练权重在COCO上训练过迁移到钢材数据时能更快收敛默认yolov8n.pt性能太弱yolov8m及以上在CPU上训练时间翻倍。起步建议用yolov8s。imgsz640不能改小。NEU-DET原图才200x200很多人习惯性设成320省显存但会丢失小目标细节。钢材缺陷里的麻点和夹杂都是小目标分辨率至少640起步。batch16在CPU上跑会较慢有NVIDIA显卡时batch大小一般按显存2G对应8来算显存8G上batch可以16-32调大能加速训练但超过某个阈值后精度不再提升反而可能震荡。lr00.01是默认值小数据集建议降到0.005不然前几个epoch loss可能直接飙到NaN。patience20表示20个epoch内验证指标没提升就早停避免资源浪费。训练结束后权重文件存在runs/detect/train/weights/best.pt和last.ptbest是验证集指标最好的last是最后一次迭代的。导出权重时只带best.pt就行。3.3 画损失函数曲线图判断模型收敛还是过拟合YOLOv8训练日志里的loss曲线是判断训练状态的一手数据。训练完成后results.png会生成在训练输出目录里包含train/val的box_loss、cls_loss、dfl_loss和mAP曲线。但只用官方图不够我习惯自己画一版带平滑处理的loss曲线方便对比不同超参实验import pandas as pd import matplotlib.pyplot as plt for exp in [runs/detect/train, runs/detect/train2]: results pd.read_csv(f{exp}/results.csv) epochs results[epoch] smooth_window 5 train_box_loss results[train/box_loss].rolling(smooth_window).mean() plt.plot(epochs, train_box_loss, labelexp.split(/)[-1]) plt.xlabel(Epoch) plt.ylabel(Box Loss) plt.title(Train Box Loss Comparison) plt.legend() plt.grid(True) plt.savefig(loss_comparison.png, dpi150)画曲线的意义在于看趋势train loss和val loss同步下降说明模型在学习val loss先降后升、train loss还在降就是典型的过拟合信号此时应该加早停、调大patience重新训或者去检查标注质量而不是盲目加epoch。钢材缺陷数据集普遍偏小过拟合是常态早停是止损手段。3.4 权重选择与导出best.pt转ONNX与INT8量化的边界条件训练完的best.pt是PyTorch格式如果只给Qt的Python端用直接加载就行。但想把推理提速尤其在CPU上建议转成ONNX再用ONNX Runtime推理速度能提升一倍左右。转换命令很简单yolo export modelbest.pt formatonnx imgsz640 opset12转ONNX时有个坑必须提opset默认值因torch版本而异如果Qt GUI用的ONNX Runtime版本较旧opset太高会不识别。碰到加载报错时先看ONNX Runtime版本号再按版本把opset降到11或12兼容性就解决了。量化到INT8能进一步提速但钢材缺陷检测对小目标敏感INT8量化掉点比较明显。实测NEU-DET六类数据上mAP50大概会从0.87降到0.79左右。产线上要的是稳定检出率不是最高帧率所以不到万不得已不要上INT8。FP16精度损失小得多但如果CPU推理不支持FP16加速还要再转回FP32。4. 训练与部署常见问题排查一个坑一个坑地填4.1 显存溢出OOM的三类原因与对应解法训练时最经典的翻车就是CUDA out of memory。第一类是batch设太大8G显存硬扛batch64不用想直接爆。解决方式是batch16起步逐步往上加显存占用用nvidia-smi实时观察。第二类是imgsz设置过大很多人想提高精度把imgsz调到1280显存占用是640的四倍除非显存充足否则不建议。第三类是开了过多的dataloader worker。worker数量设置成4-8就够设成16反而可能因为内存瓶颈导致OOM报错里经常会混着CUDA错误。4.2 标注文件格式错了模型也能训练但结果全错YOLOv8对标注格式很宽容坐标超范围、格式错位训练照样能跑起来。等指标出来了才发现框位置完全错位这是最折腾人的一种隐性错误。用下面的脚本做训练前置检查python -c from ultralytics.data.dataset import YOLODataset ds YOLODataset(data.yaml, imgsz640) print(数据集规模:, len(ds)) 能跑通不代表没问题。正确做法是随机抽几张训练图像把标注框画在原图上人工确认from ultralytics import YOLO from PIL import Image import matplotlib.pyplot as plt import matplotlib.patches as patches model YOLO(yolov8s.pt) img_path images/train/0001.jpg results model.predict(img_path, conf0.5) img Image.open(img_path) fig, ax plt.subplots(1) ax.imshow(img) for box in results[0].boxes.xyxy.tolist(): x1, y1, x2, y2 box rect patches.Rectangle((x1, y1), x2-x1, y2-y1, linewidth2, edgecolorred, facecolornone) ax.add_patch(rect) plt.savefig(check_annotation.png)这个检查只花一分钟但能过滤掉绝大多数标注格式问题。4.3 类别不均衡小类别mAP普遍比大类别低10个点的处理思路钢材缺陷数据集几乎必然存在类别不均衡问题。NEU-DET里六类各300张是均衡的但现场采集数据往往某类几千张、某类只有几十张。你在YOLOv8训练参数里会看到class weights的选项默认会根据类别频率自动加权但效果有限。我常用的处理顺序是先按原始分布训练一个版本看各类别的mAP对明显偏低的少数类先去数据增强上找办法copy-paste增强、离线翻转还是不行再考虑扩充数据。YOLOv8的mosaic增强本身对新数据友好小类别样本少时mosaic会把不同图拼在一起少数类样本参与训练的频次被动增加这个特性对小类别是有利的不建议关掉。4.4 Qt加载模型时的平台插件错误用PyQt或PySide加载YOLOv8后在嵌入式Linux或者缺少显示服务器的环境运行出现qt.qpa.plugin: could not find the Qt platform plugin linuxfb这种报错的情况很常见。这不是YOLO的问题是Qt缺少对应平台插件。在桌面Ubuntu上用xcb插件在板端用linuxfb或eglfs插件。解决方式是检查Qt插件的安装路径把PYTHONPATH指向插件目录或者设置环境变量强制指定插件export QT_QPA_PLATFORMlinuxfb。如果用的不是嵌入式平台常规桌面环境下应该设置QT_QPA_PLATFORMxcb。4.5 训练中断后续跑断点续训与结果对比训练到第80个epoch时断电前面的算力全白费。YOLOv8训练时会定期保存last.pt续训的命令是yolo detect train datasteel_defect.yaml modelruns/detect/train/weights/last.pt epochs120 imgsz640 batch16 resumeTrue有个坑要提醒续训和重新训练的loss曲线没法直接对比因为epoch计数不连续结果文件会继续append而不是覆盖。要严谨地评估最终效果还是用model.val()在验证集上跑一轮拿标准指标val逻辑独立于训练过程结果可复现。5. 用Qt把权重包成GUI工具推理逻辑、界面搭建与验证技巧5.1 技术选型PyQt5 YOLOv8推理还是C Qt ONNX RuntimeGUI外壳的选择有两条路径。Python路线是PyQt5/PySide6 ultralytics库或ONNX Runtime开发速度快、迭代方便适合产线试验和快速验证C路线是Qt Widgets ONNX Runtime C API部署体积小、启动快适合要交付可执行exe的正式项目。对于钢材缺陷检测这个场景推荐Python路线。原因有三YOLOv8的Python接口成熟处理图像前后滤波、形态学操作都方便PyQt的信号槽机制和Python线程搭配合理不会因为回调阻塞UI后期要接PLC、OPC UA等产线通信协议Python的生态更省事。C路线的优势在性能但钢材缺陷检测的推理频率不需要很高Python完全能顶住。5.2 最小可用的Qt推理界面加载图片、框选缺陷、导出结果下面是一个基于PyQt5 ultralytics的最小编程实现界面包含图片展示区和缺陷结果显示区import sys from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QFileDialog, QTextEdit) from PyQt5.QtGui import QPixmap, QImage from PyQt5.QtCore import Qt from ultralytics import YOLO import cv2 import numpy as np class SteelDefectGUI(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(钢材缺陷检测系统) self.model YOLO(best.pt) self.init_ui() def init_ui(self): central QWidget() self.setCentralWidget(central) # 图片显示区域 self.image_label QLabel() self.image_label.setAlignment(Qt.AlignCenter) self.image_label.setMinimumSize(640, 480) # 结果文本区域 self.result_text QTextEdit() self.result_text.setReadOnly(True) # 按钮 self.open_btn QPushButton(打开图片) self.open_btn.clicked.connect(self.open_image) self.save_btn QPushButton(导出检测结果) self.save_btn.clicked.connect(self.save_result) # 布局 layout QVBoxLayout() layout.addWidget(self.image_label) layout.addWidget(self.result_text) btn_layout QHBoxLayout() btn_layout.addWidget(self.open_btn) btn_layout.addWidget(self.save_btn) layout.addLayout(btn_layout) central.setLayout(layout) def open_image(self): path, _ QFileDialog.getOpenFileName( self, 选择图片, , Image Files (*.png *.jpg *.jpeg *.bmp) ) if path: self.detect(path) def detect(self, path): # 读取图像并推理 img cv2.imread(path) results self.model(img, conf0.5) # 画框 annotated results[0].plot() h, w, ch annotated.shape bytes_per_line ch * w qimg QImage(annotated.data, w, h, bytes_per_line, QImage.Format_RGB888).rgbSwapped() self.image_label.setPixmap(QPixmap.fromImage(qimg)) # 显示检测结果文本 text f共检测到 {len(results[0].boxes)} 个缺陷\n for box in results[0].boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) x1, y1, x2, y2 box.xyxy[0].tolist() text f类型: {self.model.names[cls_id]}, 置信度: {conf:.2f}, 位置: ({x1:.0f},{y1:.0f})-({x2:.0f},{y2:.0f})\n self.result_text.setText(text) def save_result(self): # 保存当前标注图像和结果文本 pass app QApplication(sys.argv) window SteelDefectGUI() window.show() sys.exit(app.exec_())这个代码实现了核心流程加载image、调用模型推理、在界面上画出检测框并显示每个缺陷的类型和置信度。detect方法里的results[0].plot()是官方提供的可视化方法内部已经把框、标签、置信度画好了不必自己用OpenCV画减少出错环节。QImage构造时用了Format_RGB888加上rgbSwapped()因为OpenCV的通道顺序是BGR而QImage默认RGB这个细节不处理图像色偏会很明显。5.3 把推理放到子线程防止界面卡顿的两种做法直接在按钮回调里调用模型推理图片一到界面就卡死直到推理完成才恢复。在CPU推理时尤其明显。PyQt的解决方案是QThread或线程池推理任务放子线程UI线程只负责更新标签。官方推荐的QThread实现方式from PyQt5.QtCore import QThread, pyqtSignal class DetectWorker(QThread): finished pyqtSignal(object, object) def __init__(self, model, img_path): super().__init__() self.model model self.img_path img_path def run(self): img cv2.imread(self.img_path) results self.model(img, conf0.5) annotated results[0].plot() self.finished.emit(annotated, results)主界面的open_image改为创建worker并连接finished信号。这里有一个注意点model对象在子线程里使用时要确保加载发生在worker首次运行前否则多线程并发加载权重的报错会让你怀疑人生。模型加载放主线程初始化阶段做worker只做推理。另一个方案是连接摄像头做实时检测用QTimer周期性触发检测。钢材产线上有些场景需要相机连续拍摄可以扩展open_image逻辑读取视频流或IP相机检测结果叠加后显示在界面上这个就按实际需求去扩展。5.4 验证与验收单张检测延迟、漏检率统计与K折交叉验证GUI做好了最终要回答两个问题这个模型在真实数据上的漏检率多高单张检测延迟多少漏检率比mAP更能反映产线价值因为mAP是IoU阈值下的综合统计而漏检率关心的是该检出的缺陷有没有被框出来。验证方案至少包含三部分第一在之前划分的test集合上跑model.val(datasteel_defect.yaml)记录每一类的mAP50和mAP50-95第二把test集里所有检测失败漏检或错检的案例整理成表格逐张看是标注问题还是模型能力问题第三记录CPU和GPU不同条件下的单张推理延迟如果GUI交互延迟超过2秒体验就开始变差。进阶一点可以按缺陷类别做分层统计比如裂纹是最需关注的缺陷单独看裂纹的召回率。有一类常见问题是模型把划痕误检为裂纹实例间距大、形态纤细这类混淆在钢材缺陷里经常发生分析混淆矩阵就能定位是哪两类在打架。我个人的习惯是每个方案交付前都固定跑一遍同一套test集记录mAP、漏检率、推理延迟三个数字。模型换过、参数调过、GUI改过之后这组数字就是判断有没有退化的基准。这套流程坚持下来后面再做别的工业检测项目复制这套框架会非常快。希望这些经验对你有帮助。本文还有配套的精品资源点击获取
返回列表