基于Qt和Opencv實(shí)現(xiàn)二維碼解析
本文詳細(xì)講解了如何利用 Qt 和 OpenCV 實(shí)現(xiàn)一個(gè)可從視頻和圖片中檢測(cè)二維碼的軟件。代碼實(shí)現(xiàn)了視頻解碼、多線程處理和界面更新等功能,是一個(gè)典型的跨線程圖像處理項(xiàng)目。以下分模塊對(duì)代碼進(jìn)行解析。


一、項(xiàng)目的整體結(jié)構(gòu)
項(xiàng)目分為以下幾部分:
主窗口 (MainWindow) :負(fù)責(zé)界面的加載、初始化和用戶交互。
工作線程 (mThread):處理耗時(shí)的圖像處理任務(wù)(如二維碼識(shí)別)。
二維碼檢測(cè)邏輯:使用 OpenCV 進(jìn)行二維碼檢測(cè),支持圖片和視頻兩種數(shù)據(jù)來(lái)源。
多線程通信:通過(guò)信號(hào)與槽機(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;
// 線程信號(hào)與主窗口槽函數(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 被用來(lái)讓用戶選擇視頻或圖像文件,并將這些參數(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í)的二維碼檢測(cè)任務(wù)。其主要功能包括:
根據(jù)功能 ID 分別處理視頻或圖片。
在每幀中調(diào)用 OpenCV 的 QRCodeDetector 進(jìn)行二維碼檢測(cè)。
通過(guò)信號(hào)將處理后的圖像和數(shù)據(jù)傳遞回主線程。
1. 核心線程邏輯
線程的運(yùn)行邏輯集中在 run() 方法中。getFunId() 決定了是處理視頻還是圖片,分別調(diào)用 anayVideo() 或 anayImages()。
void mThread::run()
{
m_isRun = true;
emit RuningState(true); // 通知主線程:線程開(kāi)始運(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() 檢測(cè)二維碼,并通過(guò)信號(hào)將結(jié)果傳回主線程。
void mThread::anayVideo()
{
cv::VideoCapture cap;
if (!cap.open(m_Params.toString().toLocal8Bit().data()) || !cap.isOpened())
{
emit errors(tc("視頻未打開(kāi)"));
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); // 檢測(cè)二維碼
emit imageProcessed(MatToQImage(frame), msg); // 發(fā)射處理信號(hào)
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ā)射信號(hào)
cv::waitKey(1000);
}
}
四、二維碼檢測(cè)實(shí)現(xiàn)
1. 使用 OpenCV 進(jìn)行檢測(cè)
在 delectDecoded() 方法中,利用 OpenCV 的 QRCodeDetector 類進(jìn)行二維碼檢測(cè)和解碼,并將結(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("未檢測(cè)到二維碼!");
}
return 0;
}2. Mat 轉(zhuǎn) QImage
為了在 Qt 界面中顯示 OpenCV 的圖像,MatToQImage() 將 OpenCV 的 cv::Mat 轉(zhuǎn)換為 Qt 的 QImage。
五、多線程與信號(hào)槽
在本項(xiàng)目中,多線程通過(guò)信號(hào)與槽實(shí)現(xiàn)以下功能:
更新主界面狀態(tài):線程的運(yùn)行狀態(tài)(如啟動(dòng)和停止)通過(guò) RuningState 信號(hào)通知主線程。
實(shí)時(shí)更新圖像和檢測(cè)結(jié)果:imageProcessed 信號(hào)傳遞處理后的圖像和二維碼信息,更新界面。
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); // 顯示檢測(cè)到的信息
}以上就是基于Qt和Opencv實(shí)現(xiàn)二維碼解析的詳細(xì)內(nèi)容,更多關(guān)于Qt Opencv二維碼解析的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++實(shí)現(xiàn)簡(jiǎn)易選課系統(tǒng)代碼分享
這篇文章主要介紹了C++實(shí)現(xiàn)簡(jiǎn)易選課系統(tǒng)及實(shí)現(xiàn)代碼的分享,具有一定的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你有所幫助2022-01-01
實(shí)例講解C++設(shè)計(jì)模式編程中State狀態(tài)模式的運(yùn)用場(chǎng)景
這篇文章主要介紹了實(shí)例講解C++設(shè)計(jì)模式編程中State狀態(tài)模式的運(yùn)用場(chǎng)景,文章最后的適用性部分則介紹了一些State模式善于處理的情況,需要的朋友可以參考下2016-03-03
在Visual Studio中配置C++最新版netCDF庫(kù)的方法
本文介紹在Windows電腦的Visual Studio軟件中,配置C++ 語(yǔ)言最新版netCDF庫(kù)的方法,文中通過(guò)圖文結(jié)合的形式介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-03-03
C++模擬實(shí)現(xiàn)時(shí)間輪模式(推薦)
文章介紹了時(shí)間輪(TimerWheel)的原理和實(shí)現(xiàn)方式,這是一種高效的定時(shí)器管理算法,常用于操作系統(tǒng)內(nèi)核,時(shí)間輪通過(guò)類似鐘表的機(jī)制管理定時(shí)任務(wù),每過(guò)一秒,秒針移動(dòng)一格,觸發(fā)相應(yīng)任務(wù),感興趣的朋友跟隨小編一起看看吧2025-11-11
C++實(shí)現(xiàn)LeetCode(113.二叉樹(shù)路徑之和之二)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(113.二叉樹(shù)路徑之和之二),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C++實(shí)現(xiàn)LeetCode(151.翻轉(zhuǎn)字符串中的單詞)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(151.翻轉(zhuǎn)字符串中的單詞),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C語(yǔ)言實(shí)現(xiàn)學(xué)生選課系統(tǒng)完整版
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)學(xué)生選課系統(tǒng)的完整版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02

