当前位置: 首页 > ds >正文

《使用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.xdnf.cn/news/16460.html

相关文章:

  • nginx一个域名下部署多套前端项目
  • GRE、MGRE实验
  • RK3568笔记九十三:基于RKNN Lite的YOLOv5目标检测
  • FreeMarker模板引擎
  • 【C++】C++11特性的介绍和使用(第三篇)
  • 【RHCSA 问答题】第 10 章 配置和保护 SSH
  • 航空发动机高速旋转件的非接触式信号传输系统
  • 技术赋能与营销创新:开源链动2+1模式AI智能名片S2B2C商城小程序的流量转化路径研究
  • 工具 | 解决 VSCode 中的 Delete CR 问题
  • 小程序的客服咨询(与企业微信建立沟通)
  • (React入门上手——指北指南学习(第一节)
  • LeetCode——1957. 删除字符使字符串变好
  • 力扣---------238. 除自身以外数组的乘积
  • Ruby 数据库访问 - DBI 教程
  • Android-广播详解
  • Go-Elasticsearch v9 安装与版本兼容性
  • Flask input 和datalist结合
  • 图论:Dijkstra算法
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现沙滩小人检测识别(C#代码UI界面版)
  • 【机器学习深度学习】LLamaFactory微调效果与vllm部署效果不一致如何解决
  • 手动开发一个串口调试工具(二):Qt 串口类基本认识与使用
  • 系统性提升大模型回复准确率:从 RAG 到多层 Chunk 策略
  • 人工智能论文辅导:Prompt Engineering(特征工程)
  • C++学习之深入学习模板(进阶)
  • 力扣 hot100 Day56
  • 深度学习入门(2)
  • J2EE模式---数据访问对象模式
  • JavaWeb项目(纯Servlet+JSP+前端三大件)入门(从0开始)
  • 传统框架与减震楼盖框架地震动力响应分析与有限元模拟
  • HashMap的线程安全性 vs ConcurrentHashMap