基于Qt和Opencv實(shí)現(xiàn)二維碼解析
本文詳細(xì)講解了如何利用 Qt 和 OpenCV 實(shí)現(xiàn)一個(gè)可從視頻和圖片中檢測二維碼的軟件。代碼實(shí)現(xiàn)了視頻解碼、多線程處理和界面更新等功能,是一個(gè)典型的跨線程圖像處理項(xiàng)目。以下分模塊對代碼進(jìn)行解析。
一、項(xiàng)目的整體結(jié)構(gòu)
項(xiàng)目分為以下幾部分:
主窗口 (MainWindow) :負(fù)責(zé)界面的加載、初始化和用戶交互。
工作線程 (mThread):處理耗時(shí)的圖像處理任務(wù)(如二維碼識別)。
二維碼檢測邏輯:使用 OpenCV 進(jìn)行二維碼檢測,支持圖片和視頻兩種數(shù)據(jù)來源。
多線程通信:通過信號與槽機(jī)制,在主線程和工作線程之間傳遞狀態(tài)與數(shù)據(jù)。
二、主窗口功能解析
1. 初始化界面和變量
MainWindow 類的構(gòu)造函數(shù)調(diào)用了 initializeUI() 和 initializeVariable(),分別完成了界面的樣式加載和核心變量的初始化。
void MainWindow::initializeVariable() { m_tip = nullptr; m_lamp[0] = QImage(":/Img/e.png"); m_lamp[1] = QImage(":/Img/i.png"); m_lamp[2] = QImage(":/Img/w.png"); mthread = new mThread(); // 創(chuàng)建工作線程 m_Threadrun = false; // 線程信號與主窗口槽函數(shù)的連接 connect(mthread, SIGNAL(RuningState(bool)), this, SLOT(onRespondThreadRuningState(bool))); connect(mthread, SIGNAL(errors(QString)), this, SLOT(onRespondThreaderrors(QString))); connect(mthread, SIGNAL(infors(QString)), this, SLOT(onRespondThreadinfors(QString))); connect(mthread, SIGNAL(warings(QString)), this, SLOT(onRespondThreadwarings(QString))); connect(mthread, &mThread::imageProcessed, this, &MainWindow::processImage); }
2. 啟動(dòng)和停止線程
用戶點(diǎn)擊按鈕后,調(diào)用 on_btn_Start_Stop_clicked,判斷當(dāng)前線程狀態(tài)以啟動(dòng)或停止工作線程。
void MainWindow::on_btn_Start_Stop_clicked() { m_Threadrun ? mthread->stop() : mthread->start(); // 根據(jù)當(dāng)前狀態(tài)啟動(dòng)或停止線程 }
3. 文件選擇
QFileDialog 被用來讓用戶選擇視頻或圖像文件,并將這些參數(shù)傳遞到線程處理。
void MainWindow::on_btn_Loadfile_clicked() { QString fileName = QFileDialog::getOpenFileName(nullptr, tc("選擇視頻文件"), "", tc("視頻文件(*.mp4)")); mthread->setFunId(0); // 設(shè)置功能 ID:0 表示處理視頻 if (!fileName.isEmpty()) mthread->setThreadParams(fileName); // 傳遞參數(shù)到線程 } void MainWindow::on_btn_Loadimages_clicked() { QStringList fileNames = QFileDialog::getOpenFileNames(nullptr, tc("選擇圖像文件"), "", tc("圖片文件(*.jpg *.bmp *.png)")); mthread->setFunId(1); // 設(shè)置功能 ID:1 表示處理圖片 if (!fileNames.isEmpty()) mthread->setThreadParams(fileNames); }
三、工作線程實(shí)現(xiàn)
mThread 類繼承自 QThread,用于處理耗時(shí)的二維碼檢測任務(wù)。其主要功能包括:
根據(jù)功能 ID 分別處理視頻或圖片。
在每幀中調(diào)用 OpenCV 的 QRCodeDetector 進(jìn)行二維碼檢測。
通過信號將處理后的圖像和數(shù)據(jù)傳遞回主線程。
1. 核心線程邏輯
線程的運(yùn)行邏輯集中在 run() 方法中。getFunId() 決定了是處理視頻還是圖片,分別調(diào)用 anayVideo() 或 anayImages()。
void mThread::run() { m_isRun = true; emit RuningState(true); // 通知主線程:線程開始運(yùn)行 emit infors(tc("線程啟動(dòng)")); switch (getFunId()) { case 0: anayVideo(); // 處理視頻 break; case 1: anayImages(); // 處理圖片 break; default: break; } emit RuningState(false); // 通知主線程:線程結(jié)束運(yùn)行 emit infors(tc("線程退出")); }
2. 視頻處理
在 anayVideo() 中,使用 OpenCV 的 VideoCapture 解碼視頻逐幀處理。每一幀調(diào)用 delectDecoded() 檢測二維碼,并通過信號將結(jié)果傳回主線程。
void mThread::anayVideo() { cv::VideoCapture cap; if (!cap.open(m_Params.toString().toLocal8Bit().data()) || !cap.isOpened()) { emit errors(tc("視頻未打開")); m_isRun = false; } else { cv::Mat frame; int frameCount = cap.get(cv::CAP_PROP_FRAME_COUNT); while ((frameCount--) > 0 && m_isRun) // 幀循環(huán) { cap >> frame; // 讀取一幀 if (frame.empty()) break; QString msg; delectDecoded(frame, msg); // 檢測二維碼 emit imageProcessed(MatToQImage(frame), msg); // 發(fā)射處理信號 cv::waitKey(50); } cap.release(); } }
3. 圖片處理
圖片處理邏輯與視頻類似,只是直接從文件路徑中讀取。
void mThread::anayImages() { QStringList files = m_Params.toStringList(); for (auto file : files) { cv::Mat frame = cv::imread(file.toStdString().c_str()); if (frame.empty() && !m_isRun) break; QString msg; delectDecoded(frame, msg); emit imageProcessed(MatToQImage(frame), msg); // 發(fā)射信號 cv::waitKey(1000); } }
四、二維碼檢測實(shí)現(xiàn)
1. 使用 OpenCV 進(jìn)行檢測
在 delectDecoded() 方法中,利用 OpenCV 的 QRCodeDetector 類進(jìn)行二維碼檢測和解碼,并將結(jié)果繪制到圖像中。
int mThread::delectDecoded(cv::Mat &image, QString &code) { cv::Mat bbox, rectifiedImage; std::string data = qrDecoder.detectAndDecode(image, bbox, rectifiedImage); ??????? if (data.length() > 0) { code = QString::fromStdString(data); // 將結(jié)果返回 std::vector<cv::Point> points; for (int i = 0; i < bbox.cols; i++) { points.push_back(cv::Point(static_cast<int>(bbox.at<cv::Point2f>(0, i).x), static_cast<int>(bbox.at<cv::Point2f>(0, i).y))); } for (size_t i = 0; i < points.size(); i++) { cv::line(image, points[i], points[(i + 1) % points.size()], cv::Scalar(0, 255, 0), 3); // 繪制綠色邊框 } int minY = points[0].y; for (const auto &point : points) { minY = std::min(minY, point.y); } cv::putText(image, data, cv::Point(points[0].x, minY - 10), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 255, 0), 2); // 顯示二維碼信息 } else { code = tc("未檢測到二維碼!"); } return 0; }
2. Mat 轉(zhuǎn) QImage
為了在 Qt 界面中顯示 OpenCV 的圖像,MatToQImage() 將 OpenCV 的 cv::Mat 轉(zhuǎn)換為 Qt 的 QImage。
五、多線程與信號槽
在本項(xiàng)目中,多線程通過信號與槽實(shí)現(xiàn)以下功能:
更新主界面狀態(tài):線程的運(yùn)行狀態(tài)(如啟動(dòng)和停止)通過 RuningState 信號通知主線程。
實(shí)時(shí)更新圖像和檢測結(jié)果:imageProcessed 信號傳遞處理后的圖像和二維碼信息,更新界面。
connect(mthread, &mThread::imageProcessed, this, &MainWindow::processImage); ???????void MainWindow::processImage(const QImage &image, const QString &msg) { ui->lab_disp->setPixmap(QPixmap::fromImage(image).scaled(image.width() / 2, image.height() / 2)); // 顯示縮放后的圖像 ui->lab_disData->setText(msg); // 顯示檢測到的信息 }
以上就是基于Qt和Opencv實(shí)現(xiàn)二維碼解析的詳細(xì)內(nèi)容,更多關(guān)于Qt Opencv二維碼解析的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言實(shí)現(xiàn)職工工資管理系統(tǒng)的示例代碼
這篇文章主要為大家詳細(xì)介紹了C語言如何實(shí)現(xiàn)職工工資管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08利用C++實(shí)現(xiàn)簡易的.ini配置文件解析器
這篇文章主要為大家詳細(xì)介紹了如何基于C++編寫一個(gè)簡易的.ini配置文件解析器,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解一下2023-03-03C語言實(shí)現(xiàn)的統(tǒng)計(jì)素?cái)?shù)并求和代碼分享
這篇文章主要介紹了C語言實(shí)現(xiàn)的統(tǒng)計(jì)素?cái)?shù)并求和代碼分享,來自PAT平臺(浙江大學(xué)計(jì)算機(jī)程序設(shè)計(jì)能力考試系統(tǒng))的一個(gè)題目,需要的朋友可以參考下2014-08-08C/C++ Zlib庫封裝MyZip壓縮類的詳細(xì)過程
在軟件開發(fā)中,文件的壓縮和解壓縮是一項(xiàng)常見的任務(wù),而ZIP是一種被廣泛應(yīng)用的壓縮格式,本文將聚焦于一個(gè)簡化的C++實(shí)現(xiàn),通過分析代碼,我們將深入了解其設(shè)計(jì)和實(shí)現(xiàn)細(xì)節(jié),感興趣的朋友一起看看吧2023-11-11