news 2026/9/17 0:32:52

基于QT与yolov5s的缺陷检测系统开发实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于QT与yolov5s的缺陷检测系统开发实践

简介:基于QT与YOLOv5s的缺陷检测系统,围绕图像检测与目标检测两大功能展开,完整覆盖了从界面搭建到模型推理的工程流程,适合作为毕业设计、课程设计或入门实战项目参考。压缩包总共76个文件,类型涵盖源码中的cpp与h、QT界面ui与资源qrc、png和jpg图片素材、mp4演示视频、onnx模型权重以及names类别文件,整体大小约151MB,结构按功能模块划分,便于检索和学习。目前已有120人学习/下载,尤其适合正在学习QT GUI编程或YOLO目标检测的开发者。资料中既包含QT的界面设计、信号槽通信、多线程处理和网络通信等C++核心代码,也包含YOLOv5s的模型文件、推理调用和测试数据,可直接运行体验,也可对照源码梳理工程细节,支持在此基础上继续改进和二次开发。

1. 基于QT和yolov5s的缺陷检测系统是在做什么

毕业设计题目一旦带上“QT”和“yolov5s”,几乎就默认了要做一套带界面的视觉检测系统。这里最容易被误解的是“图像检测”和“目标检测”两个词:前者指传统图像处理里的阈值、边缘、形态学分析,后者指用yolo这类深度学习模型输出目标框和类别。放在同一个项目里,意味着既要写OpenCV的规则逻辑,也要调通yolov5的训练与推理,最后还要把它们塞进一个能正常操作、不闪退的QT界面。这篇文章按“选型—环境—实现—联调”的顺序,把从零跑通这套系统时最容易被卡住的细节拆开讲,适合正在做毕设、以及想快速搭一套缺陷检测演示项目的人。

2. 缺陷检测的两条技术路线:图像检测与目标检测

2.1 图像检测:用OpenCV做阈值、边缘和形态学处理

传统图像检测的原理,是把缺陷定义为像素统计上的异常。以电机转子绕线缺陷检测为例,绕线区域在灰度上通常和背景有明确分层,断线、错位会表现为局部灰度突变或边缘断裂。处理流程基本都是:灰度化、滤波、二值化、形态学操作、连通域分析。这套流程不依赖标注数据,参数调好后能够跑到毫秒级,而且在QT里用OpenCV的C++接口实现非常直接。下面的代码是一个最常见的最小实现,适合作为第一个验证版本。

cv::Mat gray, blur, binary; cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); cv::medianBlur(gray, blur, 5); cv::adaptiveThreshold(blur, binary, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY_INV, 31, 10); std::vector<std::vector<cv::Point>> contours; cv::findContours(binary, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); for (auto& c : contours) { double area = cv::contourArea(c); if (area > minArea && area < maxArea) { cv::Rect r = cv::boundingRect(c); cv::rectangle(src, r, cv::Scalar(0, 0, 255), 2); } }

这段代码里,adaptiveThreshold的blockSize取31,表示以当前像素为中心、31×31邻域内计算局部阈值;C取10,让判定条件比局部均值高10个灰度级。如果检测结果出现大量细碎噪点,就把中值滤波的ksize从5改到7。minArea和maxArea是两级面积过滤,用来排除灰尘和反光造成的大块亮斑。实际调试时我一般会把轮廓画出来之后,在界面上打印每个轮廓的面积,方便确定上下限。这比一遍遍改代码重新编译高效得多。

2.2 目标检测:yolov5s如何定位缺陷

yolov5s是yolo家族里参数量相对小、适合端侧部署的一个变体。它把输入图像划分成网格,每个网格预测若干个候选框,再用置信度阈值和NMS得到最终结果。与图像检测相比,它的优势是能学习到缺陷的高层语义特征,例如裂纹的走向、划痕和背景的纹理差异。毕设里用yolov5s不需要自己实现网络结构,训练好best.pt后,直接调用官方推理脚本即可。

但要注意,在QT里集成yolov5s时,并不需要把C++版的模型推理重新写一遍。常见做法是:QT界面调用Python脚本,脚本负责加载模型、做letterbox、前向推理、输出JSON。这样换权重文件或换检测类别时,QT一侧的代码可以保持稳定。推理流程中的letterbox是不可省略的,它把原图按比例缩放到640×640,剩余区域用灰色填充。很多同学在C++里自己写前向时,忘记记录填充尺寸导致画框偏移,所以稳妥起见,先用QProcess调用Python是更可控的方案。

2.3 为什么毕设里两个部分都要做

如果只做yolov5s,题目里的“图像检测”就没有落点;如果只做OpenCV,又体现不出深度学习的内容。两套方案放在一起,其实是一件产品里经常出现的混合架构:规则逻辑负责快速过滤,模型负责复杂判断。对毕设项目来说,这种组合天然适合写论文里的对比实验:同一批样本分别用两种方法检测,统计检出率和误检率。

维度图像检测yolov5s目标检测
数据需求无需标注需要标注框
可解释性阈值、面积可调特征不可直接解释
推理速度毫秒级CPU约30-50ms
泛化能力依赖场景稳定对形态变化更鲁棒
部署复杂度OpenCV即可需要Python运行环境

另外还有一个实用点:先跑图像检测得到的候选框,可以直接导出成伪标签,供yolov5训练前做数据清洗;反过来,yolov5检测出的高置信度样本,也可以用来验证图像检测的判定阈值是否过松。两套方法互相补位,答辩时老师问到“为什么做了两套”,可以从这个角度回答。

3. 搭建QT+yolov5s开发环境与数据集准备

3.1 环境清单和最容易踩的版本坑

这里给一套经过验证的版本组合:QT使用5.15.2 MSVC2019_64,OpenCV使用4.5.5,Python使用3.8,PyTorch使用CPU版1.13,yolov5以6.0版本为基准。这套组合在Windows 10/11上都能跑通,Ubuntu下则需要把QT换成gcc版本的安装包。下表列出每个组件的作用和常见坑:

组件版本建议主要用途常见坑
QT5.15.2 MSVC2019_64界面框架混用MinGW会链接失败
OpenCV4.5.5 MSVC版图像检测需要把bin目录加入PATH
Python3.8及以上深度学习推理必须和QT是相同位数
PyTorchCPU版1.13yolov5s推理无GPU时不要装CUDA版
yolov56.0训练与推理脚本6.0后的版本接口有变化

安装顺序建议:先装QT和OpenCV并写一个显示图片的最小程序;再建Python虚拟环境,安装yolov5的requirements.txt;最后才做QT和Python的联调。不要一开始就把两侧工程混在一起,否则排错时无法判断是哪一边的问题。

这里特别强调两点:一是OpenCV的bin目录要放到系统PATH里,否则QT程序运行时找不到opencv_world453.dll;二是Python虚拟环境创建时,一定要使用和QT编译环境相同的架构,x64程序去加载32位Python,终端会直接崩溃。

提示:所有组件务必保持64位统一。调试阶段用QT的qDebug输出关键路径、模型路径和当前线程id,能省下大量排查时间。如果程序启动后一闪而过,优先检查OpenCV环境变量和QT插件目录。

3.2 标注数据并转成yolo格式

图像检测不需要标注,但yolov5s必须有带标注框的数据集。标注工具推荐labelImg,导出为VOC xml。建议每类缺陷至少收集500个实例,实在不够就做离线增强:旋转、平移、翻转、亮度变化。数据量少的时候不要一上来就训练100轮,先在验证集上评估过拟合程度。

import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_file, out_file, class_names): tree = ET.parse(xml_file) root = tree.getroot() size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) lines = [] for obj in root.findall('object'): name = obj.find('name').text cls_id = class_names.index(name) box = obj.find('bndbox') xmin = int(box.find('xmin').text) ymin = int(box.find('ymin').text) xmax = int(box.find('xmax').text) ymax = int(box.find('ymax').text) x_center = (xmin + xmax) / 2 / w y_center = (ymin + ymax) / 2 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}") with open(out_file, 'w') as f: f.write('\n'.join(lines))

转换脚本里的class_names顺序,要和后续训练时的data.yaml完全一致。比如顺序写为['scratch', 'dirt', 'crack'],那么txt里的0代表scratch,1代表dirt,2代表crack。很多人的模型训练出来类别全错,就是这里顺序没对齐。转换后的txt文件应该和图片同名,且放在不同目录下。

3.3 训练yolov5s,生成可部署的best.pt

用yolov5官方仓库训练,建议把数据集组织成如下目录结构。目录名不要带中文,images和labels必须分开放,同一个图片的标注文件不要求在同一目录,但yaml配置文件里的路径必须指向正确。

dataset/ images/train/ images/val/ labels/train/ labels/val/

然后修改data.yaml,填写nc和names。这里的names顺序必须与转换脚本保持一致,否则模型会学到错误的类别映射。完成配置后,在yolov5目录下执行训练命令,建议先跑5个epoch确认数据路径没有报错,再跑完整训练。

python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --device cpu

训练命令里的--batch 16在8G内存的CPU机器上也能跑,因为CPU版PyTorch不占显存,但会吃内存。如果内存不够,把batch降到4。训练结束后,runs/train/exp/weights/best.pt就是后续QT要调用的权重。不要用last.pt,它是最后一步的权重,验证效果通常比best.pt差。

4. 用QT实现图像检测部分(OpenCV处理)

4.1 在QT里加载图像并做预处理

QT显示图像的常规流程是:cv::imread读入BGR图像,转换成QImage,再设置到QLabel上。这里有几个细节要注意:如果路径里有中文,QString转std::string时要用toLocal8Bit(),否则imread会失败;QImage和cv::Mat共享数据时要拷贝,防止Mat析构后QImage还在引用旧内存。

QImage DefectWindow::cvMatToQImage(const cv::Mat& mat) { if (mat.type() == CV_8UC3) { return QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888).rgbSwapped().copy(); } if (mat.type() == CV_8UC1) { return QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_Grayscale8).copy(); } return QImage(); }

注意Format_RGB888对应BGR图像时,需要rgbSwapped()把B和R调换。copy()是为了让QImage持有自己的像素数据,避免与Mat共享内存导致的悬空指针。

预处理不要放在主线程。缺陷检测在单张图上虽然只有几十毫秒,但在拖动滑块时连续调用,还是会阻塞UI事件循环。最简单的方式是每次图像加载后,把原图存为成员变量,检测时用QtConcurrent::run在后台执行,再通过信号槽回传结果。

4.2 缺陷判定逻辑:阈值、面积和位置约束

图像检测的判定逻辑不能只做一次全局阈值。生产环境下的打光、产品位置都会有微小变化,所以需要加位置约束。以电机转子绕线缺陷检测为例,先用Hough圆检测找到中心,再以中心为圆心截取环形ROI。只在环形区域内做梯度检测,能大幅减少背景干扰。

cv::Mat DefectWindow::imageDetect(const cv::Mat& src) { cv::Mat gray, grad, binary; cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); cv::Mat circles; cv::HoughCircles(gray, circles, cv::HOUGH_GRADIENT, 1, 50, 150, 60, 100, 200); if (circles.empty()) return src; float cx = circles.at<float>(0, 0); float cy = circles.at<float>(0, 1); float r = circles.at<float>(0, 2); cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1); cv::circle(mask, cv::Point(cx, cy), (int)(r * 0.9), cv::Scalar(255), -1); cv::bitwise_and(gray, mask, gray); cv::Laplacian(gray, grad, CV_16S, 3); cv::convertScaleAbs(grad, grad); cv::threshold(grad, binary, 40, 255, cv::THRESH_BINARY); // 形态学闭运算与连通域分析略 }

HoughCircles参数中,param2=60表示累加器阈值越低,检测出的圆越多。如果灰尘被误检成圆,提高param2。r*0.9是为了减小边缘干扰,让检测范围退进圆内。Laplacian阈值40是一个经验值,背景亮度高时可以提高到60,但要防止漏检。

连通域分析我会使用findContours加contourArea。过滤条件包括:面积下限、外接矩形的长宽比、填充率。比如线缺陷长宽比大于3,点状缺陷长宽比接近1,这两类缺陷可以分开计数。

参数作用推荐范围
Laplacian阈值梯度响应截断30~60
minArea忽略过小噪声50~200像素
圆形ROI半径限定检测区域0.8~0.95 R

4.3 结果显示和参数调节

为了让答辩时能直观演示参数影响,界面上建议放三个QSlider:阈值、最小面积、圆形半径。拖动阈值滑块时,只需要重新执行二值化和连通域分析,不需要重新读取图片。为了减少卡顿,可以在滑块释放时才触发检测,而不是连续滑动。

绘制结果时,用cv::rectangle在原图上画红框,再在左上角写上缺陷类型和面积。注意cv::putText不支持中文,需要显示中文时,把文字信息传到QT后,用QPainter绘制在QLabel上。这也是很多人忽略的坑。

这一部分完成之后,图像检测就已经具备了独立运行的能力。你可以先用几张典型缺陷图测试阈值,再决定是否继续集成深度学习部分。接下来要做的,是把yolov5s的推理结果也汇入同一个界面,让两个检测器共享同一套显示控件。

5. 在QT里集成yolov5s目标检测

5.1 用QProcess调用Python推理脚本

把yolov5s放在QT里,最稳妥的集成方式是QProcess外部调用Python。这样做的好处是:C++侧不需要依赖libtorch,模型升级只需替换Python脚本;坏处是每次启动都要初始化模型,第一次调用会明显慢。如果只检测单张图片或离线条目,完全够用。

# detect_qt.py import sys, json import cv2 import torch model = torch.hub.load('ultralytics/yolov5', 'custom', path='best.pt', force_reload=True) model.conf = 0.25 def apply(img_path): img = cv2.imread(img_path) results = model(img) dets = results.xyxy[0].cpu().numpy() boxes = [] for x1, y1, x2, y2, conf, cls in dets: boxes.append({ "class": int(cls), "conf": float(conf), "box": [int(x1), int(y1), int(x2), int(y2)] }) print(json.dumps(boxes)) if __name__ == "__main__": apply(sys.argv[1])

上面脚本中,results.xyxy返回的是已经还原到原图坐标的检测框,所以QT端不需要再处理letterbox。如果你使用ONNX或自己实现前向,才需要手动记录填充参数。model.conf=0.25可以在脚本里设置,也可以在results.pandas()里过滤。print(json.dumps(boxes))要确保只输出JSON,不要调用results.print(),否则会混入进度条。

QT端通过QProcess启动该脚本,并接收标准输出:

QProcess *proc = new QProcess(this); QStringList args; args << "detect_qt.py" << imagePath; proc->setWorkingDirectory(yoloDir); proc->start("python", args); connect(proc, &QProcess::finished, this, [=](int exitCode) { if (exitCode != 0) return; QByteArray out = proc->readAllStandardOutput(); QJsonDocument doc = QJsonDocument::fromJson(out); // 解析boxes数组 });

QProcess的workingDirectory要设置成yolov5项目目录,否则找不到best.pt。可以通过setWorkingDirectory指定。finished信号里readAllStandardOutput必须在进程结束后调用,不然可能读到空数据。

5.2 解析yolo输出并映射回QT图像坐标

QT端收到QByteArray后,用QJsonDocument解析。下面这段代码演示了从进程输出到显示框的完整路径。

void DefectWindow::onInferFinished(int exitCode) { if (exitCode != 0) return; QByteArray data = proc->readAllStandardOutput(); QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) return; QJsonArray arr = doc.array(); for (const auto& item : arr) { auto obj = item.toObject(); auto box = obj["box"].toArray(); int x1 = box[0].toInt(); int y1 = box[1].toInt(); int x2 = box[2].toInt(); int y2 = box[3].toInt(); double conf = obj["conf"].toDouble(); // 在原图上画框 } }

如果图像在QLabel里按等比缩放显示,画框时要把原图坐标转换到控件坐标。假设QLabel显示区域比原图小,需要计算scale = min(labelW/oriW, labelH/oriH),再通过偏移量offsetX和offsetY把框平移到居中位置。这个映射函数在显示层会频繁用到,建议单独封装。

5.3 两个检测部分的切换与串联

在界面上做一个模式切换,常见做法是QComboBox加三个选项:图像检测、目标检测、串联检测。串联模式执行顺序:先跑图像检测,如果找到候选框,再跑目标检测确认。如果目标检测置信度超过阈值,最终输出类别和定位,否则标记为疑似。

模式耗时参考适用场景
图像检测5~10ms规则明确、背景稳定
目标检测30~60ms缺陷形态多样
串联检测35ms左右需要二次确认

串联合并时要注意两个检测的坐标系是否一致。图像检测是在ROI内分析的,输出框要加上ROI的offset再传给yolov5。否则yolov5会认为框的坐标已经对应整张原图,产生矛盾结果。我一般会在数据结构里让每个检测框都携带一个source字段,记录它来自哪个检测器,方便排错。

6. 两种检测模式的效果比对与调优技巧

6.1 用同一张缺陷图对比检测结果

调试阶段,建议给QT加一个“对比检测”按钮。点击后,把当前图片分别送入图像检测和目标检测,并在界面上分栏显示两张结果图。对比时重点看三类情况:两者都检出、只有图像检测检出、只有目标检测检出。只有图像检测检出,多半是缺陷的对比度很强但形状不规则,yolov5训练样本里缺少这类样本;只有目标检测检出,说明缺陷没有稳定的灰度特征,传统阈值无法分离。根据这个差异决定补数据还是调阈值。

6.2 针对QT界面卡顿的优化手段

图像检测的Laplacian和findContours在3000×3000大图上会消耗几十毫秒,连续操作会有迟滞感。优化方法是先做图像金字塔缩小到800像素宽,检测后在原图上放大回坐标。yolov5推理如果每次冷启动,界面会僵住1~2秒。可以在应用启动时就预加载Python进程,检测时通过标准输入发送图片路径。如果只是单张检测,用QProcess加环境变量OMP_NUM_THREADS=4限制线程数。

import os os.environ["OMP_NUM_THREADS"] = "4"

在Python脚本顶部设置这个环境变量必须在import torch之前执行,否则不会生效。设置后推理线程数被限制,CPU占用率下降,QT界面的鼠标响应会明显好转。

6.3 一个可复用的验证技巧:批量输出检测可视化

准备一个validate.sh或Python脚本,遍历验证集图片,分别调用两套检测,把原图、图像检测结果、yolov5结果并排保存到HTML文件。HTML里用相对路径引用图片,文件名带上参数时间戳。这样做一次就能得到几十组对比,答辩时直接翻网页,比现场截图更有说服力。脚本核心逻辑是在循环里读取图片、调用两个接口、用PIL或OpenCV拼接成三列长图。

把脚本放在项目tools目录下,每次调参后重跑一遍,生成带时间戳的报告。这样答辩时老师问“你这个阈值怎么确定的”,你可以直接打开HTML报告说明不同阈值下的漏检和误检比例。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 0:32:49

OpenClaw 接管邮件日程文档报表时,模型通道改走 TaoToken 行不行?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 0:25:03

Jetson Orin NX Nano刷机全指南:镜像烧录、Secure Boot与AI环境部署

1. 项目概述&#xff1a;为什么刷机是Jetson Orin NX Nano开发绕不开的第一道门槛Nvidia Jetson Orin NX Nano不是一块插上电就能跑AI模型的“即插即用”板子&#xff0c;它本质上是一台高度定制化的嵌入式Linux工作站——核心是ARM架构的SoC&#xff0c;集成GPU、NPU、ISP和多…

作者头像 李华
网站建设 2026/9/17 0:19:47

D2Bridge Framework:让Delphi VCL控件快速变为Web应用

简介&#xff1a;一份面向 Delphi 开发者的 D2Bridge Framework 控件包&#xff0c;基于 Delphi 13.1 环境&#xff0c;用于解决多层应用之间数据传递、组件联动与耦合度高的问题。这套框架通过桥接模式将不同数据源和应用组件连接起来&#xff0c;有助于提升大型项目的灵活性与…

作者头像 李华
网站建设 2026/9/17 0:10:51

职场Skills矩阵:硬技能与软技能的黄金组合

1. Skills 究竟是什么&#xff1f;Skills 这个词最近在各大职场社区和社交平台上频繁出现&#xff0c;但很多人对它还停留在模糊的概念层面。简单来说&#xff0c;Skills 指的是个人在特定领域或岗位中积累的专业能力和软实力。不同于传统的"技能"概念&#xff0c;现…

作者头像 李华
网站建设 2026/9/17 0:10:46

ASP.NET Core中间件:原理、实现与性能优化

1. 中间件在ASP.NET Core中的核心价值每次收到HTTP请求时&#xff0c;ASP.NET Core应用就像一条精密的流水线&#xff0c;而中间件就是这条流水线上的各个加工环节。我常把中间件比作俄罗斯套娃——每个套娃都能对请求进行处理&#xff0c;然后决定是继续传递还是直接返回响应。…

作者头像 李华
网站建设 2026/9/17 0:09:24

时延与抖动:平均值相同为何体验天差地别?网络损伤仪实战解析

在上一期的项目中&#xff0c;我遇到了一个非常典型的咨询&#xff1a;客户报障说视频会议系统“卡成PPT”&#xff0c;但把核心网两侧抓包一测&#xff0c;端到端时延平均值只有1.5ms&#xff0c;抖动平均值不到0.3ms&#xff0c;从均值看链路简直是“完美”的。但实际业务就是…

作者头像 李华