《使用Qt Quick從零構建AI螺絲瑕疵檢測系統》——5. 集成OpenCV:讓程序擁有“視力”

目錄

  • 一、概述
    • 1.1 背景介紹:賦予應用“視力”
    • 1.2 學習目標
  • 二、集成OpenCV
    • 2.1 安裝OpenCV
    • 2.2 在Qt項目中配置CMake
  • 三、項目數據集介紹與準備
  • 四、圖像的橋梁:`ImageProvider`與格式轉換
  • 五、加載、轉換并顯示圖像
  • 六、總結與展望

一、概述

1.1 背景介紹:賦予應用“視力”

在此前的文章中,我們已經成功搭建了UI與邏輯之間的通信橋梁。現在,我們的應用程序擁有了美觀的“面孔”和響應迅速的“神經系統”。然而,作為一個視覺檢測應用,它還缺少最核心的器官——“眼睛”

本篇文章的核心任務,就是為我們的應用安裝一雙強大的“眼睛”——集成全球最流行的開源計算機視覺庫OpenCV (Open Source Computer Vision Library)。通過集成OpenCV,我們的程序將首次獲得處理和顯示真實圖像的能力,這是后續實現一切AI視覺功能的前提和基礎。

1.2 學習目標

通過本篇的學習,讀者將能夠:

  1. 在Windows環境下正確配置Qt項目以集成OpenCV庫。
  2. 掌握在C++中OpenCV的圖像數據結構cv::Mat與Qt的QImage之間的相互轉換,這是兩者協同工作的關鍵。
  3. 實現從本地加載一張螺絲圖片,并通過我們架設好的前后端橋梁,最終在QML界面上成功顯示出來。

二、集成OpenCV

2.1 安裝OpenCV

首先,需要在開發環境中準備好OpenCV。

  1. 下載: 訪問OpenCV官網的發布頁面,下載適用于Windows的最新預編譯版本(例如 4.12.0)。
  2. 解壓: 運行下載的.exe文件,它實際上是一個自解壓程序。將其解壓到一個不含中文和空格的穩定路徑并重命名為opencv4.12.0,例如 D:\toolplace\opencv4.12.0。解壓后,關鍵的目錄是 D:\toolplace\opencv4.12.0\build

2.2 在Qt項目中配置CMake

要讓我們的ScrewDetector項目能夠找到并使用OpenCV,需要在CMakeLists.txt中添加配置。這是集成任何第三方C++庫的標準流程。

1. 編寫代碼 (CMakeLists.txt)
打開項目根目錄下的CMakeLists.txt文件,在find_package(Qt6 ...)之后,添加以下代碼:

# --- 開始集成OpenCV ---
# 1. 設置OpenCV的根目錄,請根據您的實際安裝路徑修改
set(OpenCV_DIR "D:/toolplace/opencv4.12.0/build")# 2. 查找OpenCV包,Core和Imgproc是我們目前需要的核心和圖像處理模塊
find_package(OpenCV REQUIRED COMPONENTS core imgproc)# 3. 包含OpenCV的頭文件目錄,以便#include指令能夠找到它們
include_directories(${OpenCV_INCLUDE_DIRS})
# --- 結束集成OpenCV ---# ... (qt_add_executable等保持不變) ...# 在鏈接Qt庫之后,鏈接OpenCV庫
target_link_libraries(appScrewDetector PRIVATE# ... (原有的Qt6::Core, Qt6::Gui等)${OpenCV_LIBS} # 鏈接OpenCV庫
)

關鍵代碼分析:
(1) set(OpenCV_DIR ...): 這一行是關鍵,它明確告訴CMake去哪里尋找OpenCV的配置文件。路徑必須指向包含OpenCVConfig.cmake文件的build目錄。請務必根據自己的實際解壓路徑進行修改,并注意使用正斜杠/
(2) find_package(OpenCV REQUIRED ...): CMake會根據OpenCV_DIR的路徑,查找OpenCV的配置,并加載其頭文件路徑、庫文件路徑等信息到CMake變量中(如OpenCV_INCLUDE_DIRSOpenCV_LIBS)。
(3) include_directories(...)target_link_libraries(...): 這兩行分別將OpenCV的頭文件目錄和庫文件添加到了我們項目的編譯和鏈接步驟中。

3. 驗證集成
backend.h的頂部添加以下兩行:

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>

然后按Ctrl+B重新構建項目。如果項目能成功編譯,沒有任何“找不到文件”的錯誤,則說明OpenCV已經成功集成!

三、項目數據集介紹與準備

【核心概念:數據是AI的燃料】

無論是傳統圖像處理還是現代AI,一切算法都離不開數據。一個高質量、標準化的數據集是項目成功的基石。從本章開始,我們將引入一個貫穿后續所有算法章節的公開數據集。

我們將采用 MVTec Anomaly Detection Dataset (MVTec AD)。這是一個用于異常檢測(在工業領域通常等同于瑕疵檢測)的行業基準數據集,由德國MVTec公司發布,質量極高且下載方便。它包含了多種工業品類,我們正好可以使用其中的**“螺絲(screw)”**類別。

  • 官方網站:https://www.mvtec.com/company/research/datasets/mvtec-ad

1. 下載與組織數據集

  • 從上述官網鏈接進入,填寫相關信息后跳轉到下載頁面,找到對應的Screw下載鏈接并下載screw.tar.xz文件。這是一個壓縮包,需要使用支持.tar.xz格式的解壓軟件(如7-Zip)進行解壓。
    在這里插入圖片描述

  • 在我們的ScrewDetector項目根目錄(與CMakeLists.txt同級)下,創建一個名為dataset的文件夾。

  • 將解壓后的screw文件夾完整地拷貝到這個dataset文件夾中。

最終的項目目錄結構應如下所示:

ScrewDetector/
├── CMakeLists.txt
├── main.cpp
├── Main.qml
├── logo.rc
├── backend.h
├── backend.cpp
├── icons/
│   └── appicon.png
│   └── appicon.ico
└── dataset/      <-- 新建的數據集文件夾└── screw/    <-- 從壓縮包解壓出的文件夾├── train/│   └── good/│       ├── 000.png│       └── ...└── test/├── good/├── scratch_head/│   ├── 000.png│   └── ...└── ...

說明:將數據集放在項目源碼目錄之外,是一種良好的工程實踐,可以保持代碼倉庫的整潔,避免將龐大的數據文件包含進版本控制系統。

數據集部分樣本圖片如下:
在這里插入圖片描述

四、圖像的橋梁:ImageProvider與格式轉換

為了讓QML能夠顯示由C++動態加載的圖像,我們需要一個特殊的橋梁——QQuickImageProvider。您可以把QQuickImageProvider想象成一個內置在您應用中的、私有的、輕量級的“圖像服務器”。

  1. QML (客戶端): 像瀏覽器一樣,向一個特殊的URL地址發起請求,例如 image://liveImage/current_screw
  2. QQuickImageProvider (服務器): 它會接收到這個請求,解析出URL中的ID(current_screw),然后在C++代碼中找到或生成對應的QImage對象,并將其作為響應“發回”給QML。同時,我們還需要實現cv::MatQImage的轉換。

【例5-1】 創建ImageProvider并實現格式轉換。

1. 創建ImageProvider

  • 在Qt Creator中,右鍵點擊項目,添加新文件... -> C++ -> C++ Class
    • 類名: ImageProvider
    • 基類: 選擇 QObject
  • 完成后,我們需要手動修改imageprovider.hImageProvider.cpp使其繼承自QQuickImageProvider

2. 編寫代碼 (imageprovider.h)

#ifndef IMAGEPROVIDER_H
#define IMAGEPROVIDER_H#include <QQuickImageProvider>
#include <QImage>// 繼承自 QQuickImageProvider
class ImageProvider : public QQuickImageProvider
{
public:ImageProvider();// QML引擎會調用這個純虛函數來請求圖片QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;// 一個公共方法,用于從C++的Backend更新圖片void updateImage(const QImage &image);private:// 用于存儲當前要顯示的圖像QImage m_image;
};#endif // IMAGEPROVIDER_H

3. 編寫代碼 (imageprovider.cpp)

#include "imageprovider.h"ImageProvider::ImageProvider()// 構造函數中必須指定Provider的類型: QQuickImageProvider(QQuickImageProvider::Image)
{
}QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{Q_UNUSED(id);Q_UNUSED(requestedSize);if (size) {*size = m_image.size();}return m_image;
}void ImageProvider::updateImage(const QImage &image)
{if (m_image != image) {m_image = image;}
}

4. cv::MatQImage的轉換函數 (backend.cpp)

修改backend.cpp,添加圖像轉換函數:

// 在 backend.cpp 的頂部
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>static QImage matToQImage(const cv::Mat &mat) {if (mat.empty()) { return QImage(); }if (mat.type() == CV_8UC3) {return QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888).rgbSwapped();} else if (mat.type() == CV_8UC1) {return QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_Grayscale8);}return QImage();
}

五、加載、轉換并顯示圖像

現在,我們將把BackendImageProvider串聯起來,完成整個流程。

【例5-2】 改造Backend以使用ImageProvider

1. 注冊ImageProvider (main.cpp)
QML引擎必須“知道”我們的ImageProvider的存在。

// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QIcon>
#include <QQmlContext>
#include "backend.h"
#include "imageprovider.h" // 1. 包含ImageProvider頭文件int main(int argc, char *argv[])
{QGuiApplication app(argc, argv);app.setWindowIcon(QIcon(":/icons/appicon.png"));QQmlApplicationEngine engine;// 2. 實例化ImageProviderImageProvider *imageProvider = new ImageProvider();// 3. 將Provider注冊到QML引擎,并給它一個URL方案名,例如"liveImage"engine.addImageProvider(QLatin1String("liveImage"), imageProvider);// 4. 創建Backend實例時,將provider的指針傳給它Backend backend(imageProvider);engine.rootContext()->setContextProperty("backend", &backend);// ... (后續代碼不變)return app.exec();
}

2. 修改Backend (backend.h & backend.cpp)
Backend通過OpenCV讀取圖像,轉換為QImage后更新ImageProvider,然后通知QML去刷新圖像。

// backend.h
#ifndef BACKEND_H
#define BACKEND_H
#include <QObject>
#include <QString>
// 移除 #include <QImage>
class ImageProvider; // 使用前向聲明class Backend : public QObject
{Q_OBJECT
public:// 構造函數需要接收ImageProvider的指針explicit Backend(ImageProvider *provider, QObject *parent = nullptr);Q_INVOKABLE void startScan();
signals:// 信號不再傳遞QImage,只傳遞一個“刷新”指令和圖像IDvoid imageReady(const QString &imageId);void statusMessageChanged(const QString &message);
private:ImageProvider *m_imageProvider;
};
#endif // BACKEND_H
// backend.cpp
#include "backend.h"
#include "imageprovider.h" // 在cpp中包含完整的頭文件
#include <QDebug>
#include <QDir>
#include <opencv2/imgcodecs.hpp>// ... (matToQImage輔助函數)Backend::Backend(ImageProvider *provider, QObject *parent): QObject(parent), m_imageProvider(provider) // 在構造函數中保存指針
{}void Backend::startScan()
{qDebug() << "C++: Loading image with OpenCV...";emit statusMessageChanged("正在從數據集加載圖像...");// 1. 構建數據集圖片的絕對路徑// QDir::currentPath() 獲取的是構建目錄,需要向上兩級到項目根目錄// 我們從MVTec數據集中挑選一張帶瑕疵的圖片作為示例QString imagePath = QDir::currentPath() + "/../../dataset/screw/test/scratch_head/000.png";// 2. 使用OpenCV從文件系統加載圖像// 注意:imread需要一個標準C++字符串cv::Mat imageMat = cv::imread(imagePath.toStdString());if (imageMat.empty()) {qDebug() << "Error: Could not load image from path:" << imagePath;emit statusMessageChanged("錯誤:無法從數據集加載圖像!請檢查路徑。");return;}// 3. 將cv::Mat轉換為QImageQImage imageQ = matToQImage(imageMat);if (imageQ.isNull()){emit statusMessageChanged("錯誤:圖像格式轉換失敗!");return;}// 4. 更新ImageProvider中的圖像m_imageProvider->updateImage(imageQ);// 5. 發射信號,只告訴QML需要刷新的圖像IDemit imageReady("screw_sample");emit statusMessageChanged("圖像顯示成功!");
}

3. 修改QML以使用URL (Main.qml)
這是最后一步,讓QML的Image組件使用我們自定義的image:// URL。

// Main.qml
import QtQuick
// ...Window {// ...Connections {target: backendfunction onStatusMessageChanged(message) {statusLabel.text = message;}function onImageReady(imageId) {// 1. 構建URL: "image://<provider_name>/<image_id>"// 2. 附加一個時間戳來強制刷新,避免QML使用緩存videoDisplay.source = "image://liveImage/" + imageId+ "?" + new Date().getTime();}}ColumnLayout {// ...Frame {id: videoFrame// ...Image {id: videoDisplayanchors.fill: parentfillMode: Image.PreserveAspectFitcache: false // 顯式禁用緩存,增加刷新可靠性}}// ...}
}

4. 運行結果
現在再次運行程序,點擊“開始檢測”按鈕。流程變為:

  1. QML: 調用 backend.startScan()
  2. C++ Backend: 加載cv::Mat -> 轉換為QImage -> 調用 m_imageProvider->updateImage()將新圖像存入Provider -> 發射 imageReady("screw_sample") 信號。
  3. QML Connections: 收到信號,執行 onImageReady("screw_sample")
  4. QML Image: source屬性被設置為 "image://liveImage/screw_sample?..."
  5. QML引擎: 看到image://協議,將請求轉發給名為liveImageImageProvider
  6. C++ ImageProvider: requestImage()被調用,它返回存儲在m_image中的最新圖像。
  7. QML Image: 成功獲取并顯示圖像。

最終的運行效果如下:
在這里插入圖片描述

六、總結與展望

在本篇文章中,我們完成了一個關鍵的里程碑:成功地將強大的OpenCV視覺庫集成到了Qt項目中。我們不僅掌握了在CMake中配置第三方庫的方法,引入了項目后續將持續使用的真實、高質量數據集,還攻克了核心技術難點——cv::MatQImage之間的無縫轉換。

現在,我們的應用程序不再只是一個空殼,它真正擁有了處理圖像的“視力”。這座連接C++后端和QML前端的橋梁,已經成功地運輸了第一批“貨物”——圖像數據。

在下一篇文章【《使用Qt Quick從零構建AI螺絲瑕疵檢測系統》——6. 傳統算法實戰:用OpenCV測量螺絲尺寸】中,我們將利用OpenCV的能力,對圖像進行更深入的分析,實現第一個真正的機器視覺功能。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/90432.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/90432.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/90432.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

智慧駕駛疲勞檢測算法的實時性優化

智慧駕駛疲勞檢測&#xff1a;從技術突破到場景革命全球每年因疲勞駕駛引發的交通事故占比超20%&#xff0c;夜間及長途駕駛場景中這一比例更高。當駕駛員出現疲勞甚至暈倒等危險駕駛行為時&#xff0c;傳統檢測手段因依賴單一傳感器或受環境干擾&#xff0c;存在誤報率高、響應…

USRP X440

產品概述 USRP X440 是 Ettus Research 推出的高性能、多通道、寬帶軟件定義無線電&#xff08;SDR&#xff09;系統。基于 Xilinx Zynq UltraScale RFSoC 架構&#xff0c;它提供高密度、相干性的信號收發能力&#xff0c;幫助您快速構建雷達、電子戰&#xff08;EW&#xff0…

[特殊字符] GitHub 2025年7月月度精選項目 Top5

&#x1f680; GitHub 2025年7月月度精選項目 Top5 本月GitHub有哪些值得關注的優質開源項目&#xff1f;我從數千個新項目中&#xff0c;精選了5個有趣 實用 可演示的倉庫 無論你是開發者、AI愛好者、工具控&#xff0c;還是正在做副業產品&#xff0c;這篇文章都值得收藏&a…

微服務架構下的自動化測試策略調優經驗分享

微服務架構下,自動化測試策略需針對分布式特性、服務自治性和高耦合風險進行針對性調整的關鍵調整方向及實施方法: 一、??測試策略重構:分層與契約驅動?? 1. ??測試金字塔升級為鉆石模型?? ??調整邏輯??:傳統金字塔中UI測試占比過高,而微服務需強化契約測試與…

圖論:并查集

入門 久聞并查集的大名&#xff0c;今天來一探究竟&#xff0c;到底什么是并查集&#xff0c;并查集有什么用&#xff1f; 并查集(Disjoint Set Union, DSU)是一種處理不相交集合的合并及查詢問題的數據結構。 其實并查集的作用主要就有兩個&#xff1a; 1、將兩個元素添加到…

告別靜態文檔!Oracle交互式技術架構圖讓數據庫學習“活“起來

&#x1f5fa;? 當數據庫架構圖學會"互動" 想象一下&#xff0c;你正在學習Oracle數據庫架構&#xff0c;面對密密麻麻的靜態文檔和復雜的組件關系圖&#xff0c;是不是常常感到&#xff1a; 像在迷宮里找路&#xff0c;不知道組件間如何協作&#xff1f;想深入了…

day62-可觀測性建設-全鏈路監控zabbix+grafana

&#x1f31f;監控api接口 &#x1f50d;監控zabbix-api接口 生成API tokens命令行測試 curl -s -X POST -H "Content-Type: application/json-rpc" -d {"jsonrpc": "2.0","method": "host.get","params": {&quo…

通過Deepseek找工作

推送的結果如下,對應的AI提示詞在底部: 計算機方向遠程工作職位匯總 整合全球遠程技術崗位 | 支持全地域遠程辦公 | 涵蓋開發、安全、云計算等方向 覆蓋方向:8+個技術領域 薪資范圍:10K-40K/月 工作模式:100%遠程 遠程技術職位列表 職位名稱 技能要求 經驗要求 薪資…

vscode文件顏色,只顯示自己更改的文件顏色、剛git下來的庫,vscode打開后,顯示所有文件都被修改了

問題&#xff1a;git新的庫&#xff0c;然后我用vscode打開&#xff0c;默認顯示所有的文件都更改了&#xff0c;但是我打開他們修改的對比&#xff0c;沒有顯示任何有被修改的地方&#xff0c;是怎么回事 linux/wsl下這么設置就可以了&#xff1a;git config core.autocrlf in…

基于ENMeval包的MaxEnt模型參數優化總結

MaxEnt模型參數優化1. MaxEnt模型優化&#xff1a;增加RM&#xff0c;降低模型過擬合風險&#xff0c;簡易模型&#xff0c;平滑響應曲線&#xff0c;增強模型可解釋性和轉移性&#xff08;生物入侵&#xff09;2. 默認參數&#xff1a;FCLQHP&#xff0c;RM12.1. 基于優化的 M…

Docker實踐:使用Docker部署blog輕量級博客系統

Docker實踐&#xff1a;使用Docker部署blog輕量級博客系統一、blog系統介紹1.1 blog介紹1.2 個人博客系統介紹1.3 個人博客使用場景二、本地環境介紹2.1 本地環境規劃2.2 本次實踐介紹三、本地環境檢查3.1 檢查Docker服務狀態3.2 檢查Docker版本3.3 檢查docker compose 版本四、…

專題:2025電商增長新勢力洞察報告:區域裂變、平臺壟斷與銀發平權|附260+報告PDF、原數據表匯總下載

原文鏈接&#xff1a;https://tecdat.cn/?p43416 當茂名果農對著鏡頭用方言喊出“荔枝現摘現發”&#xff0c;2小時賣出83萬元&#xff1b;當65歲的上海阿姨通過“子女代付”買到人生第一臺智能冰箱——2025年的電商戰場&#xff0c;正在上演三重革命&#xff1a;新興市場的增…

數字化轉型-AI落地金字塔法則

前言 人工智能必須要跟傳統產業結合&#xff0c;融入傳統產業&#xff0c;才能落地&#xff0c;才能產生巨大的倍增個幾何級效果&#xff01;&#xff01; AI不應該停留在工具層面&#xff0c;AI不僅僅是工具&#xff0c;不僅僅是硬件和軟件&#xff0c;而是軟硬結合。人工智能…

SQL Server 字段類型選型指南:什么數據用什么字段

目錄 一、數值型數據 二、日期與時間數據 三、字符串與文本數據 四、布爾值與狀態碼 五、二進制與文件數據 六、唯一標識符&#xff08;GUID&#xff09; 七、枚舉與代碼表設計 八、存儲優化小結 九、總結 在數據庫設計中&#xff0c;字段類型&#xff08;數據類型&am…

酷暑來襲,科技如何讓城市清涼又潔凈?

烈日下的身影&#xff0c;不該被“炙烤”的擔當又是一年盛夏&#xff0c;城市的血管在高溫下脈動&#xff0c;柏油馬路仿佛要融化&#xff0c;空氣中彌漫著灼熱的氣息。此刻&#xff0c;你是否曾留意過那些身影&#xff1f;在烈日下&#xff0c;他們依舊堅守崗位&#xff0c;用…

傳統框架與減震樓蓋框架地震動力響應分析與有限元模擬

傳統框架與減震樓蓋框架地震動力響應分析與有限元模擬 前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家,覺得好請收藏。點擊跳轉到網站。 摘要 本文針對傳統鋼框架和減震樓蓋鋼框架兩種結構體系,建立了水平地震作用下的動力學模型,推…

Java集合去重

? 方式一&#xff1a;TreeSet Comparator最優雅的一種&#xff0c;適用于對象中某個字段唯一的去重&#xff08;如 partyAId&#xff09;List<PartyACompanyVO> result contractDOS.stream().map(contract -> {PartyACompanyVO vo new PartyACompanyVO();vo.setPa…

Qt字符串處理與正則表達式應用

一、Qt字符串處理基礎 在Qt應用程序開發中&#xff0c;字符串處理是一項常見且重要的任務。Qt提供了強大而靈活的字符串處理功能&#xff0c;能夠滿足各種復雜的文本處理需求。 1.1 QString類概述 QString是Qt中處理字符串的核心類&#xff0c;它基于Unicode編碼&#xff0c…

qt5靜態版本對應的pcre編譯

下載 https://sourceforge.net/projects/pcre/files/pcre/8.45/ 不同版本qt對應不同pcre 編譯 啟動vs2013的開發人員命令&#xff0c;可以找到cl程序 nmake環境設置到系統path中 cd C:\pcre-8.45 mkdir build_static cd build_static cmake .. -G "NMake Makefiles" …

JimuReport 積木報表 v2.1.1 版本發布,免費開源的報表和大屏

項目介紹 積木報表&#xff0c;是一款免費的數據可視化報表&#xff0c;含報表、打印、大屏和儀表盤&#xff0c;像搭建積木一樣完全在線設計&#xff01;功能涵蓋&#xff1a;復雜報表、打印設計、圖表報表、門戶設計、大屏設計等&#xff01; 分兩大模塊&#xff1a;JimuRepo…