《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——6. 传统算法实战:用OpenCV测量螺丝尺寸

目录

  • 一、概述
    • 1.1 背景介绍:从“看见”到“看懂”
    • 1.2 学习目标
  • 二、图像预处理:让目标更突出
  • 三、轮廓发现与尺寸测量
  • 四、总结与展望

一、概述

1.1 背景介绍:从“看见”到“看懂”

在上一篇文章中,我们成功地为应用程序安装了“眼睛”——集成了OpenCV并实现了图像的加载与显示。现在,我们的程序已经能够“看见”螺丝了。然而,仅仅看见是不够的,机器视觉的核心价值在于能像人一样“看懂”图像,从中提取出有用的信息。

本篇文章的核心任务,就是实现从“看见”到“看懂”的第一次跨越。我们将利用OpenCV强大的图像处理能力,编写第一个真正的视觉算法——自动测量螺丝的尺寸。这是一种经典的、非接触式的测量应用,在工业生产中非常常见。通过这个实战,读者将直观地感受到传统视觉算法是如何通过一系列步骤,从像素中提取出几何信息的。

1.2 学习目标

通过本篇的学习,读者将能够:

  1. 掌握图像预处理的基本技术,如灰度转换二值化,这是让计算机能够“理解”图像的关键步骤。
  2. 学习并实践OpenCV中一个核心的算法——轮廓发现(Contour Finding)
  3. 利用发现的轮廓,计算其最小外接矩形(Min Area Rect),从而精确地获得螺丝的长度和宽度。
  4. 将计算出的尺寸信息和判定结果,通过信号传递回QML界面进行显示。

二、图像预处理:让目标更突出

计算机不像人眼那样智能,一张彩色的原始图像对它来说只是一堆复杂的RGB像素值。为了让计算机能够轻松地识别出我们感兴趣的目标(螺丝),必须先对图像进行预处理,其核心目的就是简化图像,增强目标特征,减弱背景干扰

【例6-1】 图像灰度化与二值化。

1. 修改Backend (backend.cpp)
我们将继续在Backend::startScan()函数中进行修改。在加载图像之后,增加灰度转换和二值化的步骤。

// backend.cpp
#include "backend.h"
// ... (之前的include保持不变)// ... (matToQImage辅助函数保持不变)Backend::Backend(QObject *parent) : QObject(parent) {}void Backend::startScan()
{// ... (加载图像的代码保持不变)QString imagePath = QDir::currentPath() + "/../../dataset/screw/test/scratch_head/000.png";cv::Mat sourceMat = cv::imread(imagePath.toStdString());if (sourceMat.empty()) { /* ... 错误处理 ... */ return; }emit statusMessageChanged("图像加载成功,开始预处理...");// --- 1. 灰度转换 ---// 将BGR彩色图像转换为单通道的灰度图像cv::Mat grayMat;cv::cvtColor(sourceMat, grayMat, cv::COLOR_BGR2GRAY);// --- 2. 二值化 ---// 将灰度图像转换为只有黑白两种颜色的二值图像// 此处使用OTSU方法自动寻找最佳阈值cv::Mat binaryMat;cv::threshold(grayMat, binaryMat, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);// 为了在UI上直观展示处理结果,我们暂时只显示二值化后的图像QImage imageQ = matToQImage(binaryMat);if (imageQ.isNull()){ /* ... 错误处理 ... */ return; }m_imageProvider->updateImage(imageQ);emit imageReady("screw_processed");emit statusMessageChanged("图像预处理完成!");
}

2. 运行结果
再次运行程序并点击“开始检测”,现在界面上显示的不再是原始的彩色螺丝图片,而是一张清晰的黑白轮廓图。在这张图中,螺丝主体是白色(像素值为255),背景是黑色(像素值为0),目标物被完美地凸显了出来。
在这里插入图片描述
关键代码分析:
(1) cv::cvtColor(...): OpenCV中用于色彩空间转换的函数。cv::COLOR_BGR2GRAY是一个预定义的常量,表示从BGR色彩空间转换到灰度空间。
(2) cv::threshold(...): 二值化函数。它将图像中所有像素值大于阈值的像素设为一个值(如255),小于等于阈值的设为另一个值(如0)。
(3) THRESH_BINARY_INV: 表示反向二值化。因为我们的螺丝比背景暗,普通二值化后螺丝会变黑。使用反向二值化,可以让暗的螺丝变成白色,方便后续处理。
(4) THRESH_OTSU: 这是一个非常智能的标志。当使用它时,我们传递的阈值参数(这里是0)会被忽略,threshold函数会自动计算出一个最优的全局阈值来分割前景和背景。这对于光照不均的场景非常有效。

三、轮廓发现与尺寸测量

经过预处理后,图像中的螺丝已经变成了一个清晰的白色区域。现在,我们可以让OpenCV去“寻找”这个白色区域的边界,这个边界就是轮廓

【例6-2】 寻找轮廓并计算最小外接矩形。

1. 修改Backend (backend.cpp)
在二值化之后,加入轮廓发现和几何计算的逻辑。

// backend.cpp
// ...
#include <vector> // C++标准库,用于存储轮廓// ... (matToQImage辅助函数)void Backend::startScan()
{// ... (加载图像、灰度化、二值化的代码保持不变) ...cv::Mat sourceMat = cv::imread(...);cv::Mat binaryMat;// ... cv::threshold(...) ...emit statusMessageChanged("预处理完成,开始寻找轮廓...");// --- 3. 轮廓发现 ---std::vector<std::vector<cv::Point>> contours;cv::findContours(binaryMat, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);// 假设最大轮廓就是我们的螺丝if (contours.empty()) {emit statusMessageChanged("错误:未在图像中找到任何轮廓!");return;}// 寻找面积最大的轮廓double maxArea = 0;int maxAreaIdx = -1;for (int i = 0; i < contours.size(); i++) {double area = cv::contourArea(contours[i]);if (area > maxArea) {maxArea = area;maxAreaIdx = i;}}if (maxAreaIdx == -1) {// ... 错误处理return;}// --- 4. 尺寸测量 ---// 计算最大轮廓的最小外接矩形cv::RotatedRect rotatedRect = cv::minAreaRect(contours[maxAreaIdx]);// 获取矩形的尺寸。注意:width和height不一定是物理的长和宽cv::Size2f rectSize = rotatedRect.size;float width = std::min(rectSize.width, rectSize.height);float length = std::max(rectSize.width, rectSize.height);qDebug() << "Measured dimensions (pixels): Length =" << length << ", Width =" << width;QString resultMessage = QString("测量结果: 长度= %1 px, 宽度= %2 px").arg(length, 0, 'f', 2).arg(width, 0, 'f', 2);// --- 5. 结果可视化 ---// 为了直观展示,我们在原始彩色图上把轮廓和矩形画出来// 获取矩形的四个顶点cv::Point2f vertices[4];rotatedRect.points(vertices);// 将轮廓和矩形画在sourceMat上cv::drawContours(sourceMat, contours, maxAreaIdx, cv::Scalar(0, 255, 0), 2); // 绿色轮廓for (int i = 0; i < 4; i++) {cv::line(sourceMat, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 0, 255), 2); // 红色矩形}// 将带有绘制结果的图像发送到UIQImage imageQ = matToQImage(sourceMat);m_imageProvider->updateImage(imageQ);emit imageReady("screw_processed");emit statusMessageChanged(resultMessage);
}

2. 运行结果
点击“开始检测”后,界面上将显示原始的彩色螺丝图片,但上面已经叠加了绿色的轮廓线和红色的最小外接矩形。同时,状态栏会显示出计算出的像素尺寸。
在这里插入图片描述
关键代码分析:
(1) cv::findContours(...): OpenCV中用于寻找轮廓的核心函数。
- binaryMat: 输入必须是二值图像。
- contours: 输出参数,一个存储向量的向量集(std::vector<std::vector<cv::Point>>),用于存储所有找到的轮廓。每个轮廓本身是一个由点(cv::Point)组成的向量。
- cv::RETR_EXTERNAL: 表示只检测最外层的轮廓,忽略内部的孔洞,这对于我们的需求是最高效的。
- cv::CHAIN_APPROX_SIMPLE: 一种轮廓点的压缩算法,只保留轮廓的端点,可以节省大量内存。
(2) cv::contourArea(...): 计算一个轮廓所包围的面积。我们通过遍历所有轮廓并比较面积,来找到最大的那个,并假定它就是我们的目标螺丝。
(3) cv::minAreaRect(...): 计算并返回一个包围轮廓点的、面积最小的旋转矩形(cv::RotatedRect)。这个矩形能够紧密地贴合倾斜的目标。
(4) rotatedRect.size: cv::RotatedRect对象包含中心点、角度和尺寸(cv::Size2f)信息。sizewidthheight不保证哪个是长哪个是短,因此我们用std::minstd::max来获取物理上的宽度和长度。
(5) cv::drawContours(...)cv::line(...): 用于在图像上进行绘制的函数,非常适合在调试和结果展示时,将算法的中间结果可视化。

四、总结与展望

在本篇文章中,我们成功地实现了第一个真正的机器视觉算法。通过图像预处理(灰度、二值化)、核心算法(轮廓发现)和 几何计算(最小外接矩形)这一经典流程,我们让程序从一张普通的图片中精确地提取出了螺丝的像素尺寸。

我们不仅学习了几个关键的OpenCV函数,更重要的是,我们建立了一套解决此类问题的思维框架。然而,读者可能已经发现,这种方法虽然能测量尺寸,但对于识别表面划痕、锈斑等纹理类、无固定形状的瑕疵却无能为力。

这正是传统视觉算法的局限性所在,也为我们引入更强大的AI技术埋下了伏笔。在下一篇文章【《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——7. AI赋能(上):训练你自己的YOLOv8瑕疵检测模型】中,我们将进入深度学习领域,亲手训练一个能够“认识”多种瑕疵的AI模型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/90737.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/90737.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《人性的弱点》重构【01】

手上有本《人性的弱点》&#xff08;韩文桥 译&#xff0c;浙江文艺出版社&#xff0c;2017.1出版&#xff09;&#xff0c;前些年买的&#xff0c;近期翻出来看看。这门书虽成书于80多年前&#xff0c;但卡耐基对人性洞察之深刻&#xff0c;时至今日&#xff0c;并未觉得过时。…

k8s开启审计日志

k8s默认是关闭审计功能的&#xff0c;想看的话需要到apiserver的pod中才可以。 开启此功能是为了进行k8s审计日志的收集&#xff0c;方便我们查看k8s中用户的各自操作。 开启此功能之前&#xff0c;我们要先创建个审计策略文件audit-policy.yaml 例如以下的测验文件 apiVersion…

Kafka MQ 消费者应用场景

Kafka MQ 消费者应用场景 1 消费者自动提交的时机 在 Kafka 中默认的消费位移的提交方式是自动提交,这个由消费者客户端参数 enable.auto.commit 配置,默认值为 true。当然这个默认的自动提交不是每消费一条消息就提交一次,而是定期提交,这个定期的周期时间由客户端参数 …

Git版本控制系统

Git作为目前最流行的分布式版本控制系统&#xff0c;已经成为开发者必备的技能之一。本文将全面介绍Git的核心概念、基本操作、分支管理以及与GitHub的协作开发&#xff0c;帮助读者从零开始掌握Git的使用。 一、Git概述 1.1 Git发展历史 Git诞生于2005年&#xff0c;由Linu…

如何编译RustDesk(Unbuntu 和Android版本)

编译Linux版本的RustDesk备注&#xff1a;官方文档上&#xff0c;一边都是基于sciter&#xff0c;这个在后面已经不建议使用了&#xff0c;但是依然可以编译刚开始的时候看官方的文档&#xff0c;涉及的东西比较多&#xff0c;也搞的一头雾水&#xff0c;通过B站上一个视频&…

Spring中的循环依赖:解密、破局与架构启示

> 当两个Bean紧紧相拥,Spring容器却陷入死锁——这是Java开发者的经典噩梦 某电商平台凌晨上线时突然宕机,日志里反复滚动着`BeanCurrentlyInCreationException`的报错。经排查,**优惠券服务与库存服务在初始化时相互依赖**,形成致命闭环。这个价值百万的故障案例,揭开…

DataFrame​(数据框)

一种二维表格型数据结构&#xff0c;类似于电子表格&#xff08;如 Excel&#xff09;或 SQL 表&#xff0c;由行&#xff08;记录&#xff09;​和列&#xff08;字段&#xff09;​组成。它是数据分析、机器学习和科学计算中最常用的数据结构之一&#xff0c;尤其在 ​Python…

B站视频评论数据爬取

爬取B站视频评论数据爬取与分析 如果只要单纯的脚本可以直接看项目结构里的b_comments.py 一、技术架构 1、环境配置 Python 3.8PyCharm 2、模块配置 requests&#xff1a;用于发送HTTP请求time&#xff1a;用于处理时间相关的操作csv&#xff1a;用于读写CSV文件json&#xff…

OpenAI最新大模型GPT-4o体验之Code Copilot AI编程大模型

一、前言GPT-4o&#xff08;"o"代表"全能"&#xff09;具备处理各种文本、声音和图像资料的能力&#xff0c;能够输出多种格式的文本、声音和图像。GPT-4o 的推出标志着 AI 技术的重大突破。它不再局限于单一媒介&#xff0c;而是首次实现了文本、语音和图…

社交电商推客系统全栈开发指南:SpringCloud+分润算法+Flutter跨端

一、推客系统概述与市场背景推客系统&#xff08;TuiKe System&#xff09;是一种基于社交关系的营销推广平台&#xff0c;通过用户分享商品或服务链接&#xff0c;实现裂变式传播和精准营销。近年来&#xff0c;随着社交电商的蓬勃发展&#xff0c;推客系统已成为企业获客的重…

网安-中间件-Redis未授权访问漏洞

目录 Redis Redis持久化 动态修改配置 使用反弹连接的情况 常见监听端口的方式 常见建立反弹连接的方式 流程 Linux crontab cron文件存储路径 利用Redis实现攻击 1.webshell提权案例 2.定时任务shell反弹案例 3.SSH Key getshell案例 ​编辑Redis其他利用方式 …

【c++深入系列】:万字详解栈和队列和deque(附模拟实现的源码)

&#x1f525; 本文专栏&#xff1a;c &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 石头能被水滴穿&#xff0c;不是因为水有多强&#xff0c;而是因为它从未停过。 ★★★ 本文前置知识&#xff1a; 模版 栈 那么栈这个…

速通python加密之RSA加密

RSA加密 RSA加密是一种非对称加密算法&#xff08;与AES等对称加密不同&#xff09;&#xff0c;由罗纳德李维斯特&#xff08;Ron Rivest&#xff09;、阿迪萨莫尔&#xff08;Adi Shamir&#xff09;和伦纳德阿德曼&#xff08;Leonard Adleman&#xff09;于1977年提出&…

Java BeanUtils 类详解:作用、语法与示例

一、BeanUtils 的核心作用BeanUtils 是 Apache Commons 和 Spring Framework 提供的工具类&#xff0c;主要用于简化 JavaBean 的操作。核心功能包括&#xff1a;属性拷贝&#xff1a;对象间同名属性自动复制动态访问&#xff1a;通过字符串名称操作属性类型转换&#xff1a;自…

PyCharm高效开发全攻略

安装与基础配置下载PyCharm专业版或社区版&#xff08;免费&#xff09;并完成安装。首次启动时选择默认设置或自定义主题、字体大小等界面偏好。配置Python解释器路径&#xff08;推荐使用虚拟环境&#xff09;&#xff0c;确保项目依赖隔离。快捷键与导航熟悉核心快捷键能大幅…

Pycharm 给 python 程序打包EXE的配置和方法

前言: Python 语言的设计变得越来越简单,它有很多可以使用的库,所以尤其在人工智能时代,Python语言被广泛应用。但是Python语言和windows系统的兼容性稍微偏弱,如何生成windows可以执行的exe文件。是要一个很复杂的配置过程,本文就会做一个介绍。 本文,通过一个Python…

【Linux | 网络】传输层(UDP和TCP)

目录一、再谈端口号1.1 端口号1.2 端口号的范围划分1.3 常见知名端口号1.4 netstat 命令1.5 进程与端口号的关系1.6 pidof 命令二、UDP协议2.1 UDP协议段格式2.2 如何理解UDP报头和UDP报文2.2.1 UDP报头2.2.2 UDP报文和UDP报文的管理2.2.3 UDP封装过程2.3 UDP的特点2.4 UDP的缓…

mybatisX的自定义模板生成

在idea中使用mybtais的自定义模板生成&#xff0c;可以帮我们省去很多重复的代码。 打开一个项目&#xff0c;我们要修改的主要就两个文件&#xff0c;一个是生成的mapper接口&#xff0c;另一个是xml文件&#xff1a; 相应的mapper接口模板为&#xff1a; package ${mapper…

miniz:一个轻量级、高性能的开源压缩库

目录 1.简介 2.核心特性 3.基本使用示例 4.与 ZLIB 的对比 5.使用场景 6.注意事项 1.简介 miniz 是一个轻量级、高性能的开源压缩库&#xff0c;专注于提供 ZLIB/GZIP 兼容的压缩和解压缩功能。它的核心优势在于体积小巧&#xff08;单文件实现&#xff09;、跨平台支持和…

Jenkins接口自动化测试(构建)平台搭建

Python接口自动化测试零基础入门到精通&#xff08;2025最新版&#xff09;自动化测试流程 在进行平台搭建前&#xff0c;我们首先要问自己&#xff1a;我需要搭建的平台的功能是什么&#xff0c;要实现什么目标&#xff1f; 在我的理解中&#xff0c;自动化构建平台的执行流…