简介本资源是一套开箱即用的YOLOv5钢材表面缺陷检测完整方案面向工业视觉初学者、自动化质检工程师及高校科研人员解决钢铁制造中常见缺陷如裂纹、划痕、氧化斑等的快速识别与定位问题。压缩包共180个文件总计141.66MB涵盖34个核心Python脚本含训练/推理/QT界面逻辑、29个配置yaml文件数据集路径、类别定义、模型超参、32张标注样本JPG图及配套XML/TXT双格式标签、5个预训练.pt权重文件、4个PyQt设计.ui界面文件以及PR曲线、loss变化图、results.csv评估结果等训练成果。已有1750人学习下载资源附带可直接运行的PyQt图形界面支持图片批量检测、视频逐帧分析及实时摄像头调用并提供Dockerfile与环境部署说明便于在边缘设备或产线工控机上快速部署验证。 每次看到有人把YOLOv5目标检测和Qt界面放在一起做毕业设计或工程项目我其实挺有感触的。这类项目在工业视觉里非常典型因为一套完整的检测系统如果没有图形界面模型权重训得再好到产线或者实验室验收时也会显得缺乏最后一公里的交付感。我之前接触过的钢材表面缺陷检测基本上就是这个套路用YOLOv5训练出一套能识别划痕、麻点、氧化皮等缺陷的权重再套上一个基于Qt开发的界面让检测过程不再只是黑乎乎的终端输出。整个过程确实有人觉得容易有人觉得坑特别多。这篇打算把我实际做过的思路和踩坑记录整理出来重点聊数据集准备、权重训练、Qt界面封装这三个大块以及它们之间衔接的细节。希望能给正在做YOLOv5钢材缺陷检测或者正卡在Qt部署环节的朋友一点参考。1. 钢材缺陷检测项目全貌为什么偏偏是YOLOv5配Qt1.1 项目能做什么适用场景在哪钢材表面缺陷检测通俗点说就是通过工业相机拍摄热轧钢带或钢板表面用深度学习模型对图像中的缺陷目标进行定位和分类再通过软件的界面把检测结果实时展示出来。这个项目最常见的交付形态就是一套可以点开运行的桌面程序左边是图像显示区域右边是检测结果的列表和统计信息下方有打开图片开始检测相机采集保存结果之类的按钮。我实际做过的版本主要是针对钢铁厂平整分检线、卷材表面质检这类场景做的原型验证。钢带在运动过程中表面会出现不同的瑕疵比如轧制划痕平行于轧制方向细长条状颜色深浅不一。麻点密集分布的小点状凹陷通常呈现成群出现。氧化铁皮压入呈现不规则的块状颜色比周边暗多伴随在钢材冷却过程中压入。夹杂深浅不一的点状或块状颗粒。裂纹形状不规则的折线状危害等级较高。这套方案能做的事情就是把原始图像输入给模型模型输出每个缺陷框的类别、坐标和置信度然后程序把这些结果画在图像上同时生成统计报告。适合的场景不止是钢企质检站实验室做材料表面评估、科研人员做算法对比这类场景也用得上。从岗位技能讲它覆盖了环境配置、模型训练、代码工程化、界面开发这几个维度所以会成为很多工程类课题和简历项目的首选。1.2 为什么选YOLOv5而不是YOLOv8或Faster R-CNN这几年新出的检测模型非常多YOLOv8、YOLOv9甚至YOLOv11都已经很成熟了。但在我做的钢材缺陷检测这个具体需求里仍然倾向于继续用YOLOv5原因不是追求最先进而是要平衡几个实际条件。第一格式转换和权重兼容性问题。YOLOv5的PyTorch权重转为ONNX再用OpenCV的DNN模块或者ONNXRuntime来推理社区里已经有非常成熟的方案几乎不会有坑。而YOLOv8虽然推理代码也不复杂但其输出的格式和YOLOv5在某些版本上有细微区别处理Anchor或DFL解码时要额外写不少代码。对于Qt界面集成来说稳定大于花哨。第二钢材缺陷数据集的标签风格与YOLOv5契合度很高。钢材缺陷数据集里的目标相对紧凑缺陷的长宽比变化很大比如划痕可能是狭长矩形YOLOv5的Anchor机制可以通过聚类适配这些形状。很多公开的钢材缺陷数据集比如NEU-DET早期版本附带的示例配置就直接对标YOLOv5用起来顺理成章。第三硬件资源与推理实时性。很多项目最终要跑在工控机上这类机器往往只有CPU或者一张入门级显卡。YOLOv5s可以在i5处理器的CPU上跑到接近实时每秒十几帧取决于图像分辨率这在演示和验证阶段已经非常够用。YOLOv8在相同条件下推理速度略慢一点并非不可接受但调试资料相对少。既然目标是交付一套能用的界面不是刷排行榜YOLOv5就是那个不太容易出错的选择。注意这里并非说YOLOv8不好。如果你对最新机制有兴趣YOLOv8完全没问题。但如果你想快速做出一套带Qt界面的可演示检测系统YOLOv5的生态成熟度是最高的。2. 数据集准备与整理钢材缺陷样本的底子决定模型上限2.1 使用公开数据集还是自制数据钢材表面缺陷检测最常用的公开数据集是NEU-DET东北大学表面缺陷数据库。它包含1800张热轧带钢表面的灰度图像每张尺寸是200x200像素涵盖六类缺陷Rolled-in Scale氧化皮压入、Patches麻点、Crazing裂纹、Pitted Surface麻点/凹坑、Inclusion夹杂、Scratches划痕。这里有个容易踩坑的点NEU-DET的标签数据并不都是标准的YOLO格式。早期版本给的是XML文件Pascal VOC格式需要自己转换成YOLOv5需要的txt标注每行类别id x_center y_center width height所有坐标均归一化到[0,1]。而网上下载的某些转换后版本可能因为转换脚本问题出现坐标错位。所以我建议不要直接拿着网上的成品txt训练最好自己走一遍格式转换和可视化确认。2.2 标签格式转换与数据集划分实操以NEU-DET为例我一般这样处理确认原始图像和XML文件路径。写一个脚本解析XML中的object节点提取name和bndbox坐标xmin, ymin, xmax, ymax。将xmin、ymin、xmax、ymax转换为YOLOv5格式import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_path, out_txt_path, class_names): tree ET.parse(xml_path) root tree.getroot() img_width int(root.find(size/width).text) img_height int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines))将整个数据集按train:val:test 8:1:1划分。划分时建议用脚本按缺陷类别做分层抽样而不是简单随机切分否则可能出现某个类别在验证集中数量极少的情况。数据集划分需要注意钢材缺陷图像往往一个样本包含多个目标而且目标分布不平衡。像**裂纹Crazing和夹杂Inclusion**这类缺陷出现频率较低如果随机划分训练时这些类别的loss会一直偏高。我在做的时候会提前统计每个类别在训练集和验证集中的数量确保比例大致一致。2.3 数据增强的注意点灰度图别乱转RGBNEU-DET图像原本是灰度图但很多代码习惯用三通道读图。YOLOv5在训练时会对图像做归一化但不会因为输入是单通道就自动复制成三通道。我的做法是在读取时统一转成三通道RGB每个通道数值相同即可。这种操作不会丢失信息还会避免后续模型输入维度不对的报错。关于增强策略YOLOv5自带HSV扰动、随机翻转、Mosaic增强等。对钢材表面缺陷来说以下几点需要特别注意不要用太过激的旋转增强。钢材缺陷中划痕的方向是有物理意义的尤其平行于轧制方向的划痕是主要缺陷形态。如果旋转90度或大角度旋转可能会让模型学到错误的语义。Mosaic增强对钢材缺陷非常有用。200x200的小图本身分辨率低Mosaic可以把四张图拼在一起变相扩大感受野和背景多样性但训练时要注意mosaic概率不要设得太高比如0.5~0.7之间比较合适否则模型对真实图像的形态分布适应会变差。blur增强可以适度加。工业相机在产线上运动拍摄时会有运动模糊简单的高斯模糊或均值模糊模拟这种场景能提升泛化能力。3. 模型训练与权重生成YOLOv5训练钢材缺陷的核心步骤3.1 环境搭建与目录组织YOLOv5训练环境建议直接用官方仓库我一般用Python 3.8 ~ 3.10PyTorch 1.8 ~ 2.1带CUDA如果机器有NVIDIA显卡opencv-python、numpy、matplotlib、pandas、seaborn、tensorboard 等依赖安装训练依赖很简单git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt数据集目录结构按YOLOv5约定来组织这也是最容易出错的地方。YOLOv5在训练时需要数据配置yaml文件指向图片和标签的路径。我常用的钢材缺陷数据目录结构如下steel_defect_dataset/ ├── images/ │ ├── train/ │ │ ├── 1.jpg │ │ └── ... │ └── val/ │ ├── 2.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 1.txt │ │ └── ... │ └── val/ │ ├── 2.txt │ └── ... └── steel_defect.yamlsteel_defect.yaml的内容train: ./steel_defect_dataset/images/train val: ./steel_defect_dataset/images/val nc: 6 names: [crazing, inclusion, patches, pitted_surface, rolled_in_scale, scratches]这里有个小经验训练时建议把标签和图片放在同一根目录下分属两个文件夹而不是放在datasets目录里让YOLOv5自动创建。这样排查路径问题更直观。如果标签与图片不一致比如某张图没有对应txtYOLOv5训练时会自动忽略空标签但很多缺陷样本其实是有标签的如果忽略了会极大影响训练效果。我通常会在训练前用脚本跑一遍全量检查确保每张图都有对应的txt文件且txt内容不为空。3.2 选择预训练权重还是从头训练YOLOv5官方提供了五个预训练模型yolov5n、yolov5s、yolov5m、yolov5l、yolov5x。它们的参数量和推理速度依次增加精度一般也依次略好。对于钢材缺陷检测我一般按照以下逻辑选择模型参数量推理速度适用场景yolov5s较小快大多数原型验证和工控机部署yolov5m中等中需要提点且算力不紧张时yolov5l/x大较慢追求极致精度、离线检测、GPU算力充足我实际测试过NEU-DET这种小目标密集的数据集用yolov5s预训练权重微调mAP能达到0.75以上视划分方式有波动用yolov5m可以达到0.80左右。再往上提升有限但推理时间成倍增加。如果是做界面演示yolov5s已经足够部署到CPU上也能流畅运行。选择预训练权重时有一个细节YOLOv5官方仓库里下载的yolov5s.pt是基于COCO数据集80类训练的。它的特征提取层Backbone通用性强可以直接用来微调钢材缺陷类别。训练时关键词是--weights yolov5s.pt。如果不加预训练权重而是用--weights 模型从头训练收敛会慢很多且最终精度普遍偏低。3.3 训练参数调整与过程监控训练命令参考如下python train.py --img 640 --batch 16 --epochs 150 --data steel_defect.yaml --weights yolov5s.pt --device 0 --project ./runs/train_steel --name exp_steel几个参数说明--img 640虽然原图是200x200但YOLOv5会resize到640x640训练。这个操作有点反直觉因为把低分辨率图放大到640其实并没有增加信息量只是把像素插值放大了。如果想要速度更快可以试试--img 320。实测下来NEU-DET在640输入下AP会高一点点但模型对微小缺陷的定位能力提升有限因为原图分辨率本身就低。如果你的图像源是产线相机分辨率可能达到1000~2000像素以上这种情况下建议让--img尽量接近原图分辨率比如1280否则小目标缺陷很难被检测出来。--batch 16如果显存不够可以调小到8或4。YOLOv5对batch size比较敏感太小的batch会导致BN层的统计量不稳定loss波动明显。如果不改其他参数只把batch调小可以考虑同时开启--label-smoothing 0.05来稳定训练。--epochs 150NEU-DET图片少一般100~150轮足够。如果加了很多数据增强可以适当增加轮数。--device 0指定显卡CPU则用--device cpu但训练巨慢不建议。训练过程中最重要的事情是盯loss曲线和验证集mAP曲线。YOLOv5默认输出results.png和TensorBoard日志。一种常见的失败模式是训练轮数一多验证集mAP在某个epoch之后下降或震荡而训练集loss还在下降。这说明模型过拟合——本来NEU-DET数据就只有1800张图过拟合是常态。解决途径包括调低epochs设置--patience 30让模型在验证集停止提升时早停。增加数据增强的权重尤其是随机翻转和HSV扰动让模型不容易记住训练图像的细节。换成更小的模型比如从yolov5s换成yolov5n。训练完成后在runs/train_steel/exp_steel/weights/下会有best.pt和last.pt。尽量选择best.pt作为最终部署权重。3.4 权重转换从PyTorch到ONNX再到Qt可加载的格式在Qt界面中加载YOLOv5模型有几种主流方式直接加载PyTorch权重.pt需要界面环境中安装PyTorch并且通过torch.load加载模型。这样方便但会让程序体积变大且部署环境必须装齐依赖。转为ONNX再用ONNXRuntime推理推荐。ONNX模型体积小跨平台好CPU加速效果也不错且不需要再依赖PyTorch就可以加载。转为OpenCV DNN支持的格式.onnx或.pb通过OpenCV的dnn.readNetFromONNX读取ONNX文件完全脱离深度学习框架。但YOLOv5的输出层有很多后处理逻辑写到OpenCV里代码会稍微繁琐一些。我自己的方案是导出ONNX并在Qt中用ONNXRuntime做InferenceSession。这样界面程序只依赖opencv和onnxruntime启动快部署也干净。导出命令python export.py --weights runs/train_steel/exp_steel/weights/best.pt --img 640 --include onnx这里有个长期以来的坑YOLOv5的export.py在导出ONNX时输出层是三维的形状类似于1x25200x85需要自己解码。25200是三个尺度特征图80x80、40x40、20x20的anchor总数量85是5个框属性x,y,w,h,confidence加80个类别概率。钢材缺陷只有6类时这个维度会变成1x25200x11。我们可以直接用模型输出shape来得到类别数或者干脆在导出后把原始shape用脚本打出来确认。ONNX导出成功后用Python快速验证一下import cv2 import onnxruntime as ort import numpy as np session ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) input_name session.get_inputs()[0].name input_shape session.get_inputs()[0].shape print(finput: {input_name}, shape: {input_shape}) output_names [o.name for o in session.get_outputs()] print(foutputs: {output_names})确认无误后再开始写Qt界面的推理代码。4. Qt界面开发把检测权重变成一个能操作的程序4.1 界面功能设计与整体布局Qt界面不一定要做得多炫但功能要讲清楚。以钢材缺陷检测为例我设计的界面方案是这样的左侧区域图像显示控件QLabel或自定义QWidget用于显示原始图像和检测后的结果图像。右侧区域类别信息表格QTableView或QTableWidget列出每帧图像中检测到的缺陷类别、坐标、置信度。统计标签比如当前检测到多少缺陷、平均置信度是多少。打开图片开始检测相机采集保存结果退出等按钮。状态栏显示当前模型加载状态、推理耗时、FPS等信息。界面布局推荐用QHBoxLayout和QVBoxLayout组合控制在500-1000行代码量内。如果工程实际有大图显示需求建议重写一个QImageWidget在paintEvent中绘制图像和检测框而不是直接用QLabel加pixmap缩放因为QLabel在实时刷新时容易闪烁。4.2 Qt中加载ONNX模型与推理在Qt里使用ONNX模型核心依赖是onnxruntime C库或者用Python版QtPySide2/PyQt5直接调用onnxruntime的Python接口。用Python写Qt界面是最快的开发路径import sys import cv2 import numpy as np import onnxruntime as ort from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget, QFileDialog, QTableWidget, QTableWidgetItem, QSplitter from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import Qt, QTimer class SteelDefectWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(YOLOv5钢材缺陷检测系统) self.session ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) self.input_name self.session.get_inputs()[0].name # 加载类别名 self.class_names [crazing, inclusion, patches, pitted_surface, rolled_in_scale, scratches] # ... 后续初始化UI这里有几个容易错的地方需要提醒ONNX模型输入尺寸是固定值比如640x640。在推理前需要将任意尺寸的输入图像按比例缩放并填充到640x640而不是直接resize成640x640否则目标比例会失真检测精度明显下降。图像归一化YOLOv5导出ONNX时输入需要除以255并且要求通道顺序为RGB。如果在Qt里用OpenCV读图默认BGR要先做cv2.cvtColor(img, cv2.COLOR_BGR2RGB)再归一化。建议把resize时的缩放比例记录下来推理后把检测框坐标映射回原始图像尺寸时要用。推理核心代码示例def detect(self, frame_bgr): h, w frame_bgr.shape[:2] letterbox_img, ratio, (dw, dh) self.letterbox(frame_bgr, 640) img_rgb cv2.cvtColor(letterbox_img, cv2.COLOR_BGR2RGB) img_norm img_rgb.astype(np.float32) / 255.0 blob np.transpose(img_norm, (2, 0, 1))[None] outputs self.session.run(None, {self.input_name: blob}) preds self.postprocess(outputs[0], ratio, dw, dh, (w, h)) return preds后处理中要实现的NMS非极大值抑制代码相对固定包括置信度阈值过滤、每个框坐标还原、类别置信度最高的类作为最终类别、用cv2.dnn.NMSBoxes抑制重复框。这部分建议直接参考YOLOv5官方utils/general.py里的non_max_suppression函数逻辑重新实现一版注意输入输出是numpy数组而不是torch张量。4.3 线程卡顿问题别把推理放在UI主线程新手做Qt界面最容易犯的错就是点击开始检测按钮后直接在主线程里写一个while True循环去推理结果界面卡死无法拖动窗口也无法点击停止按钮。这是Qt事件循环被阻塞导致的。正确处理方法是使用QThread或QTimer方案一适合实时视频/相机流新建一个QThread子类重写run()方法在其中循环读帧、推理、通过信号finished_frame(QImage)发送结果回主线程更新界面。方案二适合单张图片/离线检测用QThreadPool和QRunnable做一次性任务点击按钮时开启任务结束后通过信号通知。我最常用的是QThread方案因为钢材缺陷检测项目在展示时常需要接摄像头或者录好的视频文件。简单示意class DetectThread(QThread): change_pixmap pyqtSignal(QImage) update_table pyqtSignal(list) def __init__(self): super().__init__() self.running True def run(self): cap cv2.VideoCapture(0) # 或视频文件路径 while self.running: ret, frame cap.read() if not ret: break detections self.detect(frame) result_frame self.draw_detections(frame, detections) rgb_image cv2.cvtColor(result_frame, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape bytes_per_line ch * w qimage QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) self.change_pixmap.emit(qimage) self.update_table.emit(detections) cap.release()写线程时需要注意不要在子线程里直接操作UI控件必须使用信号槽机制更新界面。这是Qt的铁律违反它会随机崩溃或出现诡异刷新问题。4.4 检测结果的可视化与表格输出检测结果的绘制我一般用OpenCV完成在原生图像上画矩形框、类别名和置信度。颜色根据类别分配划痕类用红色裂纹类用蓝色等。这里有一个小技巧如果钢材图片是灰度图画框后最好加一层伪彩色映射或把背景稍微提亮否则红绿蓝的检测框在灰底图上区别不大。表格输出方面用QTableWidget设置行列self.table.setColumnCount(5) self.table.setHorizontalHeaderLabels([类别, x_min, y_min, x_max, y_max, 置信度])注意上面的列数其实写的是5但表头有6个标签写代码时务必保持一致。这类小问题目测根本没法发现只有跑起来报错才意识到。我在实际项目里遇到过一次当时在初始化时写反了结果表格一直显示异常检查了半天才发现是列数定义不对。如果想把检测结果导出建议保存为CSV或者JSON方便后面做报表统计。Qt里可以用QFileDialog.getSaveFileName让用户选路径再把表格内容逐行写入文件。4.5 界面运行环境与打包部署开发完成后如果要交付给别人用建议用PyInstaller打包成exeWindows或可执行文件Linux。打包命令大致如下pyinstaller -w -F main.py --hidden-importonnxruntime有几个打包经验如果直接用--hidden-importonnxruntime还不够可能还需要指定--collect-all onnxruntime因为onnxruntime里的动态链接库和proto文件比较多。图像文件、模型文件、UI文件等要放在相对路径下。打包后程序的工作目录可能和开发时不同建议在代码里用sys.path[0]或Path(__file__).parent定位资源路径。否则会报找不到best.onnx。如果界面用到了中文字体打包后在某些精简系统上可能显示乱码。可以设定一个使用QSS样式表包含常用字体或直接指定微软雅黑。5. 实际运行中的性能观察与典型问题排查5.1 推理耗时对比CPU和GPU的差距我用yolov5s的ONNX模型在640x640输入下测过一次推理耗时Intel i5-10400CPUONNXRuntime约120~180ms/帧大约每秒6-8帧。NVIDIA GTX 1660 SuperGPUONNXRuntime约15~25ms/帧每秒40-60帧。如果想在CPU上把速度提上去可以尝试量化。ONNX模型可以转成int8量化模型速度能提升不少但精度会有损失。钢材缺陷检测里置信度比较低的微弱缺陷在量化后更容易漏检所以建议先测清楚再决定是否量化。另一个常见优化是输入尺寸减半。比如从640x640降到416x416甚至320x320推理时间能明显下降但小目标比如麻点群中的单点缺陷可能会丢失。如果界面主要处理的是产线上200x200的原始小图其实320x320输入是很好的折中选择因为原图分辨率在那里放大到640并不会增加细节。5.2 缺陷漏检率偏高的原因分析在钢材表面缺陷检测的实战中漏检率往往是用户最关心的指标。我遇到过的漏检场景主要有三种低对比度缺陷像很浅的划痕肉眼都看不清模型更难。这种情况建议在预处理阶段加入对比度增强比如CLAHE提高缺陷与背景的灰度差异。遮挡或重叠目标多个缺陷靠得很近时NMS会抑制掉部分框。可以通过降低NMS IoU阈值比如从0.5降到0.3来保留更多候选框。训练集样本不足NEU-DET每类只有300张图个别类别形态变化有限。如果项目要求更高建议自己补充产线采集的多样本这样效果提升会很明显。5.3 Qt界面运行时的卡顿与内存泄漏在Qt界面中推理线程持续运行如果每帧都创建大量临时对象如cv::Mat副本、numpy数组可能导致内存占用不断上升。一个惨痛教训是早期版本的绘图代码在每帧里np.copy原始图像后再画框结果运行十几分钟后内存涨了几百MB。后来改成在原始帧上直接绘制只有在发信号时才拷贝一份内存就稳定了。如果界面刷新闪烁可以试试双缓冲机制。Qt的QWidget默认启用了双缓冲但如果你自定义了paintEvent且绘制耗时较高建议用QPixmap作为缓冲先绘制到pixmap再整个贴到控件上。还有一点与摄像头相关如果相机的帧率很高比如60fps而推理速度只有10fps就需要在读取线程里跳帧而不是让队列无限制堆积。否则界面延迟会越来越大实时性变成回放系统。常见的做法是每读3帧取1帧送入推理队列中只保留最新一帧。6. 从检测到可视化完整代码骨架的浓缩版为了让大家对整体结构有个直观感受这里我整理一个浓缩版的代码骨架涵盖界面初始化和推理流程。# main.py from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QFileDialog, QTableWidget, QVBoxLayout, QHBoxLayout, QWidget, QSplitter, QTableWidgetItem from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import QTimer import sys import cv2 import numpy as np import onnxruntime as ort class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(钢材表面缺陷检测系统) self.setGeometry(100, 100, 1200, 700) self.session ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) self.class_names [crazing, inclusion, patches, pitted_surface, rolled_in_scale, scratches] self.conf_thres 0.25 self.iou_thres 0.45 self.init_ui() self.timer QTimer(self) self.timer.timeout.connect(self.update_frame) self.cap None def init_ui(self): # 左图像显示 self.image_label QLabel(请打开图片或启动相机) self.image_label.setMinimumSize(640, 640) self.image_label.setAlignment(Qt.AlignCenter) # 右按钮和表格 btn_open QPushButton(打开图片) btn_open.clicked.connect(self.open_image) btn_camera QPushButton(启动相机) btn_camera.clicked.connect(self.start_camera) btn_stop QPushButton(停止) btn_stop.clicked.connect(self.stop_camera) self.table QTableWidget() self.table.setColumnCount(6) self.table.setHorizontalHeaderLabels([类别, x_min, y_min, x_max, y_max, 置信度]) self.table.horizontalHeader().setStretchLastSection(True) layout_right QVBoxLayout() layout_right.addWidget(btn_open) layout_right.addWidget(btn_camera) layout_right.addWidget(btn_stop) layout_right.addWidget(self.table) widget_right QWidget() widget_right.setLayout(layout_right) splitter QSplitter() splitter.addWidget(self.image_label) splitter.addWidget(widget_right) splitter.setStretchFactor(0, 4) splitter.setStretchFactor(1, 1) self.setCentralWidget(splitter) def open_image(self): path, _ QFileDialog.getOpenFileName(self, 选择图片, , Images (*.png *.jpg *.bmp)) if not path: return frame cv2.imread(path) detections self.detect(frame) result self.draw_detections(frame, detections) self.show_frame(result) self.update_table(detections) def detect(self, frame): # letterbox 推理 后处理省略具体实现 pass def draw_detections(self, frame, detections): # 绘制矩形框和类别 pass def show_frame(self, result_bgr): rgb_image cv2.cvtColor(result_bgr, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape bytes_per_line ch * w qimage QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) self.image_label.setPixmap(QPixmap.fromImage(qimage)) def update_table(self, detections): self.table.setRowCount(0) for det in detections: row self.table.rowCount() self.table.insertRow(row) class_name, x1, y1, x2, y2, conf det self.table.setItem(row, 0, QTableWidgetItem(class_name)) self.table.setItem(row, 1, QTableWidgetItem(str(int(x1)))) self.table.setItem(row, 2, QTableWidgetItem(str(int(y1)))) self.table.setItem(row, 3, QTableWidgetItem(str(int(x2)))) self.table.setItem(row, 4, QTableWidgetItem(str(int(y2)))) self.table.setItem(row, 5, QTableWidgetItem(f{conf:.3f})) def start_camera(self): self.cap cv2.VideoCapture(0) self.timer.start(30) def stop_camera(self): if self.timer.isActive(): self.timer.stop() if self.cap: self.cap.release() def update_frame(self): ret, frame self.cap.read() if not ret: return detections self.detect(frame) result self.draw_detections(frame, detections) self.show_frame(result) self.update_table(detections) if __name__ __main__: app QApplication(sys.argv) window MainWindow() window.show() sys.exit(app.exec_())这个骨架不是完整可运行的代码detect和draw_detections需要自己补全。但把界面和推理的逻辑边界理清楚之后填充细节就很顺手了。关于detect方法我再补充一个关键细节letterbox实现时填充颜色用灰色(114,114,114)这是YOLOv5的标准做法。填充在右下角还是居中取决于实现但推理后坐标映射时偏移量要跟填充方式对应。我建议统一采用居中填充左右各pad一半上下各pad一半这样映射时不容易出错。7. 项目实际落地的一点体会整个YOLOv5钢材缺陷检测加Qt界面的项目做下来最大的感受是模型训练不是最难的边边角角的工程化问题才是最消耗时间的。比如数据集的类别名称要保证在训练yaml、标签txt、界面代码三处完全一致。很多人在训练时用的是缩写或数字到界面里要映射成中文名结果发现某一处写错了显示出的类别总是对不上。我的习惯是从一开始就在一个Python字典里维护类别ID到中文名的映射不论是训练标注、评估结果还是界面表格都复用这一份映射避免到处硬编码。再比如模型推理时的输入尺寸和letterbox参数导出ONNX时、Python推理时、Qt推理时三处都要保持一致。如果训练时用的--img 640导出时用的默认值也是640但对界面里的resize尺寸不小心写成了416那么实际输入的是416x416而ONNX模型的输入层是640x640运行时要么报维度不匹配要么自动resize到640但性能受影响。排查这种问题非常耗时所以我的建议是在所有配置节点显式写明输入尺寸。如果是做毕业设计或项目演示建议把界面做得稍微完整一点比如在检测结果图上显示FPS、模型名称、当前处理的图片路径甚至可以加一个简单的历史截图功能。这些细节虽然不加技术分但能让演示效果更饱满。我自己在答辩演示时就是靠着在界面右下角添加累计检测缺陷数、按类别统计柱状图这些功能让评委觉得项目更完整。如果你下一步想在这个项目基础上扩展比较合理的路径是在Qt界面中接入相机的实时拉流比如GigE工业相机或RTSP流然后把检测结果上传到MQTT或者本地数据库形成检测数据看板。这类功能在工业现场往往比模型精度更受关注。当然这些都建立在基础检测链路已经跑通的基础上——先把模型权重和Qt界面这套链路彻底吃透后面的扩展就是水到渠成的事。本文还有配套的精品资源点击获取
