【基于Qt和OpenCV的多线程图像识别应用】

这篇具有很好参考价值的文章主要介绍了【基于Qt和OpenCV的多线程图像识别应用】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

这是一个简单的小项目,使用Qt和OpenCV构建的多线程图像识别应用程序,旨在识别图像中的人脸并将结果保存到不同的文件夹中。这个项目结合了图像处理、多线程编程和用户界面设计。 用户可以通过界面选择要识别的文件夹和保存结果的文件夹。然后,启动识别进程。图像识别线程并行处理选定文件夹中的图像,检测图像中的人脸并将其保存到一个文件夹,同时将不包含人脸的图像保存到另一个文件夹。进度和结果将实时显示在用户界面上。

多线程编程

为什么需要多线程

1、并行处理:在处理大量图像时,使用单线程可能会导致应用程序变得非常慢,因为它必须依次处理每个图像(这里我没有去实现,感兴趣的小伙伴可以自己尝试一下)。多线程允许应用程序同时处理多个图像,从而提高了处理速度。

2、防止阻塞:如果在主线程中执行耗时的操作,比如图像识别,会导致用户界面在操作执行期间被冻结,用户无法与应用程序互动。多线程可以将这些耗时操作移到后台线程,以避免界面阻塞。

3、利用多核处理器:现代计算机通常具有多核处理器,多线程可以充分利用这些多核来加速任务的执行。

Qt如何实现多线程

在项目中,多线程编程主要使用了Qt的 QThread 类来实现。以下是在项目中使用多线程的关键步骤
1、继承QThread类: 首先,创建一个自定义的线程类,继承自 QThread 类。这个类将负责执行多线程任务。在项目中,这个自定义线程类是 ImageRecognitionThread。

2、重写run函数: 在自定义线程类中,重写 run 函数。run 函数定义了线程的执行体,也就是线程启动后会执行的代码。在本项目中,run 函数包含了图像识别的逻辑。

3、创建线程对象: 在应用程序中,创建自定义线程类的对象,例如 ImageRecognitionThread 的对象。然后,通过调用 start 函数来启动线程。

4、信号和槽机制: 使用Qt的信号和槽机制来实现线程间的通信。在项目中,使用信号来更新识别进度和结果,以便主线程可以实时显示这些信息。

5、线程安全性: 要确保多个线程安全地访问共享资源,例如文件系统或图像数据,通常需要使用互斥锁(Mutex)等机制来防止竞争条件和数据损坏。

线程间通信

在线程间进行通信是多线程编程中的关键概念,特别是在项目中,其中一个线程负责图像识别任务,另一个线程用于用户界面更新。在这个项目中,使用了Qt的信号和槽机制来实现线程间的通信,以便更新识别进度和结果。具体步骤如下:
1、信号和槽的定义
首先定义了信号和槽函数,分别用于更新进度和结果:

signals:
    void updateProgress(int progress);
    void updateResult(const QString& result);

private slots:
    void onProgressUpdate(int progress);
    void onResultUpdate(const QString& result);

updateProgress 信号用于更新识别进度,它接受一个整数参数,表示识别进度的百分比。
updateResult 信号用于更新识别结果,它接受一个字符串参数,表示识别的结果信息。
onProgressUpdate 槽函数用于接收进度更新信号,并在主线程中更新用户界面的进度条。
onResultUpdate 槽函数用于接收结果更新信号,并在主线程中更新用户界面的结果文本。

2、信号的发射
在 ImageRecognitionThread 类的 run 函数中,根据图像识别的进度和结果,使用以下方式发射信号:

// 发射进度更新信号
emit updateProgress(progress);

// 发射结果更新信号
emit updateResult("图像 " + imageFile + " 中检测到人脸并已保存。");

通过 emit 关键字,可以发射定义的信号,并传递相应的参数。

3、槽函数的连接
在主线程中,当创建 ImageRecognitionThread 的对象时,需要建立信号和槽的连接,以便接收来自线程的信号并执行槽函数。这通常在主窗口类的构造函数中完成。例如:

// 创建ImageRecognitionThread对象
imageThread = new ImageRecognitionThread(this);

// 连接信号和槽
connect(imageThread, &ImageRecognitionThread::updateProgress, this, &MainWindow::onProgressUpdate);
connect(imageThread, &ImageRecognitionThread::updateResult, this, &MainWindow::onResultUpdate);

这些连接操作确保当 ImageRecognitionThread 中的信号被发射时,相关的槽函数会在主线程中执行。

4、槽函数的执行

槽函数会在主线程中执行,因此可以直接更新用户界面的进度条和结果文本。例如:

void MainWindow::onProgressUpdate(int progress)
{
    ui->progressBar->setValue(progress);
}

void MainWindow::onResultUpdate(const QString& result)
{
    ui->resultTextEdit->append(result);
}

在这里,onProgressUpdate 槽函数更新了主窗口中的进度条,而 onResultUpdate 槽函数更新了结果文本框。
通过信号和槽机制,项目中的不同线程能够安全地进行通信,而不会导致竞争条件或数据损坏。这种机制允许图像识别线程实时更新识别进度和结果,同时保持了用户界面的响应性,提供了更好的用户体验。

图像识别

图像识别的流程是这个项目的核心部分,它包括了加载图像、使用OpenCV的人脸检测器识别人脸、以及根据结果保存图像等步骤。以下是详细描述的图像识别流程:

1、加载图像

首先,从用户选择的识别文件夹中加载图像。这个步骤包括以下操作:
获取用户选择的识别文件夹路径。
遍历该文件夹中的所有图像文件。
逐个加载图像文件。在项目中,可以使用OpenCV库的 cv::imread 函数来加载图像。

// 从文件夹中加载图像
cv::Mat image = cv::imread(imageFile.toStdString());

2、 人脸识别

一旦图像加载完成,接下来的任务是识别图像中的人脸。这个项目使用OpenCV提供的人脸检测器来完成这个任务,通常使用Haar级联分类器或深度学习模型。在本项目中,我们使用了OpenCV内置的Haar级联分类器。

创建一个 cv::CascadeClassifier 对象并加载Haar级联分类器的XML文件。

cv::CascadeClassifier faceCascade;
faceCascade.load("haarcascade_frontalface_default.xml");

使用加载的分类器检测图像中的人脸。这将返回一个矩形列表,每个矩形表示一个检测到的人脸的位置。

std::vector<cv::Rect> faces;
faceCascade.detectMultiScale(image, faces, scaleFactor, minNeighbors, flags, minSize, maxSize);

根据检测到的人脸位置,可以在图像上绘制矩形框,以标记人脸的位置。

for (const cv::Rect& faceRect : faces) {
    cv::rectangle(image, faceRect, cv::Scalar(0, 255, 0), 2); // 在图像上绘制矩形框
}

3、 结果保存
最后,根据识别的结果,将图像保存到相应的文件夹。在本项目中,根据是否检测到人脸,有两个不同的保存路径:一个用于保存包含人脸的图像,另一个用于保存不包含人脸的图像。

如果检测到了人脸,将图像保存到包含人脸的文件夹中。

if (!faces.empty()) {
    QString savePathWithFace = saveFolderPath + "/with_face/" + QFileInfo(imageFile).fileName();
    cv::imwrite(savePathWithFace.toStdString(), image);
}

如果没有检测到人脸,将图像保存到不包含人脸的文件夹中。

else {
    QString savePathWithoutFace = saveFolderPath + "/without_face/" + QFileInfo(imageFile).fileName();
    cv::imwrite(savePathWithoutFace.toStdString(), image);
}

以上就是图像识别的主要流程。通过这个流程,项目能够加载、识别和保存图像,根据识别结果将图像分别保存到两个不同的文件夹中,以实现人脸识别功能。这个流程结合了OpenCV的图像处理能力,为图像识别提供了一个基本框架。

项目代码

项目结构

项目分为两个主要部分:
1、用户界面:使用Qt框架创建,包括选择识别文件夹、选择保存结果文件夹、启动和停止识别等功能。
2、图像识别线程:使用Qt的QThread类创建,负责加载图像、识别人脸、保存结果,并通过信号和槽机制与用户界面通信。

各部分代码

1、imagerecognitionthread.h

#ifndef IMAGERECOGNITIONTHREAD_H
#define IMAGERECOGNITIONTHREAD_H

#include <QThread>
#include <QString>

class ImageRecognitionThread : public QThread
{
    Q_OBJECT

public:
    explicit ImageRecognitionThread(QObject* parent = nullptr);
    void setFolderPath(const QString& folderPath);
    void setSaveFolderPath(const QString& saveFolderPath); 

protected:
    void run() override;

signals:
    void updateProgress(int progress);
    void updateResult(const QString& result);

private:
    QString folderPath;
    QString saveFolderPath; 
};

#endif 

2、imagerecognitionthread.cpp

#include "imagerecognitionthread.h"
#include <opencv2/opencv.hpp>
#include <QDir>
ImageRecognitionThread::ImageRecognitionThread(QObject* parent)
    : QThread(parent), folderPath(""), saveFolderPath("")
{
  
}

void ImageRecognitionThread::setFolderPath(const QString& folderPath)
{
    this->folderPath = folderPath;
}

void ImageRecognitionThread::setSaveFolderPath(const QString& saveFolderPath)
{
    this->saveFolderPath = saveFolderPath;
}

void ImageRecognitionThread::run()
{
    QString faceCascadePath = "D:\\DownLoad\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_default.xml";

    cv::CascadeClassifier faceCascade;
    if (!faceCascade.load(faceCascadePath.toStdString()))
    {
        emit updateResult("无法加载人脸检测器");
        return;
    }

    QDir imageDir(folderPath);
    QStringList imageFilters;
    imageFilters << "*.jpg" << "*.png";
    QStringList imageFiles = imageDir.entryList(imageFilters, QDir::Files);

    int totalImages = imageFiles.size();
    int processedImages = 0;

    QString faceSaveFolderPath = saveFolderPath + "/faces"; // 用于保存包含人脸的图像的文件夹
    QString noFaceSaveFolderPath = saveFolderPath + "/no_faces"; // 用于保存不包含人脸的图像的文件夹

    // 创建保存结果的文件夹
    QDir().mkpath(faceSaveFolderPath);
    QDir().mkpath(noFaceSaveFolderPath);

    for (const QString& imageFile : imageFiles)
    {
        processedImages++;
        int progress = (processedImages * 100) / totalImages;
        emit updateProgress(progress);

        QString imagePath = folderPath + "/" + imageFile;
        cv::Mat image = cv::imread(imagePath.toStdString());

        if (!image.empty())
        {
            std::vector<cv::Rect> faces;
            faceCascade.detectMultiScale(image, faces, 1.1, 4, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30));

            if (!faces.empty())
            {
                QString targetPath = faceSaveFolderPath + "/" + imageFile;
                cv::imwrite(targetPath.toStdString(), image);
                emit updateResult("图像 " + imageFile + " 中检测到人脸并已保存到人脸文件夹。");
            }
            else
            {
                QString targetPath = noFaceSaveFolderPath + "/" + imageFile;
                cv::imwrite(targetPath.toStdString(), image);
                emit updateResult("图像 " + imageFile + " 中未检测到人脸并已保存到非人脸文件夹。");
            }
        }
    }

    emit updateResult("识别完成,结果保存在相应文件夹中");
}

3、mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
#include <QProgressBar>
#include <QListWidget>
#include "imagerecognitionthread.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget* parent = nullptr);

private slots:
    void startRecognition();
    void stopRecognition();
    void updateProgress(int progress);
    void updateResult(const QString& result);
    void selectRecognitionFolder();
    void selectSaveFolder();

private:
    void setupUi();
    void connectSignalsAndSlots();

    QLineEdit* folderPathLineEdit;
    QLineEdit* saveFolderPathLineEdit; 
    QPushButton* startButton;
    QPushButton* stopButton;
    QPushButton* selectRecognitionFolderButton; 
    QPushButton* selectSaveFolderButton; 
    QLabel* progressLabel;
    QProgressBar* progressBar;
    QLabel* resultsLabel;
    QListWidget* resultsList;

    ImageRecognitionThread* recognitionThread;
};

#endif // MAINWINDOW_H

4、mainwindow.cpp

#include "mainwindow.h"
#include "imagerecognitionthread.h"
#include <QVBoxLayout>
#include <QFileDialog>
#include <QDebug>

MainWindow::MainWindow(QWidget* parent)
    : QMainWindow(parent), recognitionThread(nullptr)
{
    setupUi();
    connectSignalsAndSlots();
}

void MainWindow::startRecognition()
{
    // 获取文件夹路径
    QString folderPath = folderPathLineEdit->text();
    QString saveFolderPath = saveFolderPathLineEdit->text(); // 获取保存结果的文件夹路径

    // 创建并启动识别线程
    recognitionThread = new ImageRecognitionThread(this);
    recognitionThread->setFolderPath(folderPath);
    recognitionThread->setSaveFolderPath(saveFolderPath); // 设置保存结果的文件夹路径
    connect(recognitionThread, &ImageRecognitionThread::updateProgress, this, &MainWindow::updateProgress);
    connect(recognitionThread, &ImageRecognitionThread::updateResult, this, &MainWindow::updateResult);
    recognitionThread->start();
}

void MainWindow::stopRecognition()
{
    // 如果识别线程正在运行,终止它
    if (recognitionThread && recognitionThread->isRunning())
    {
        recognitionThread->terminate();
        recognitionThread->wait();
    }
}

void MainWindow::updateProgress(int progress)
{
    progressBar->setValue(progress);
}

void MainWindow::updateResult(const QString& result)
{
    resultsList->addItem(result);
}

void MainWindow::setupUi()
{
    // 创建和布局UI组件
    folderPathLineEdit = new QLineEdit(this);
    saveFolderPathLineEdit = new QLineEdit(this); // 用于保存结果的文件夹路径
    startButton = new QPushButton("开始识别", this);
    stopButton = new QPushButton("停止识别", this);
    selectRecognitionFolderButton = new QPushButton("选择识别文件夹", this); // 选择识别文件夹按钮
    selectSaveFolderButton = new QPushButton("选择保存文件夹", this); // 选择保存文件夹按钮
    progressLabel = new QLabel("进度:", this);
    progressBar = new QProgressBar(this);
    resultsLabel = new QLabel("结果:", this);
    resultsList = new QListWidget(this);

    QVBoxLayout* layout = new QVBoxLayout();
    layout->addWidget(folderPathLineEdit);
    layout->addWidget(selectRecognitionFolderButton); // 添加选择识别文件夹按钮
    layout->addWidget(saveFolderPathLineEdit); // 添加用于保存结果的文件夹路径输入框
    layout->addWidget(selectSaveFolderButton); // 添加选择保存文件夹按钮
    layout->addWidget(startButton);
    layout->addWidget(stopButton);
    layout->addWidget(progressLabel);
    layout->addWidget(progressBar);
    layout->addWidget(resultsLabel);
    layout->addWidget(resultsList);

    QWidget* centralWidget = new QWidget(this);
    centralWidget->setLayout(layout);
    setCentralWidget(centralWidget);
}

void MainWindow::connectSignalsAndSlots()
{
    connect(startButton, &QPushButton::clicked, this, &MainWindow::startRecognition);
    connect(stopButton, &QPushButton::clicked, this, &MainWindow::stopRecognition);
    connect(selectRecognitionFolderButton, &QPushButton::clicked, this, &MainWindow::selectRecognitionFolder); // 连接选择识别文件夹按钮的槽函数
    connect(selectSaveFolderButton, &QPushButton::clicked, this, &MainWindow::selectSaveFolder); // 连接选择保存文件夹按钮的槽函数
}

void MainWindow::selectRecognitionFolder()
{
    QString folderPath = QFileDialog::getExistingDirectory(this, "选择识别文件夹", "", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    folderPathLineEdit->setText(folderPath);
}

void MainWindow::selectSaveFolder()
{
    QString saveFolderPath = QFileDialog::getExistingDirectory(this, "选择保存结果的文件夹", "", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    saveFolderPathLineEdit->setText(saveFolderPath);
}

项目演示

qt图像识别,qt,opencv,开发语言,计算机视觉qt图像识别,qt,opencv,开发语言,计算机视觉qt图像识别,qt,opencv,开发语言,计算机视觉

小结

特别提醒:在使用OpenCv的时候一定要配置好环境哦,这也是一个相对比较麻烦的事情,可以看看其他博主的教程!
点赞加关注,从此不迷路!!文章来源地址https://www.toymoban.com/news/detail-754212.html

到了这里,关于【基于Qt和OpenCV的多线程图像识别应用】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • PyQt应用程序中的多线程:使用Qt还是Python线程?

    多线程模块能够更加高效得完成任务,但是在PyQt 应用程序中实现多线程可以使用 Qt 的线程模块(QThread)或者 Python 的 threading 模块。两者各有优劣,具体选择取决于项目需求和个人偏好。下面我们将以案例来说明两种模块具体得优缺点。 1、问题背景 在 PyQt 应用程序中,编

    2024年02月22日
    浏览(52)
  • 基于Qt的多线程TCP即时通讯软件的设计与实现

    本文将从涉及到主要技术开始,讲解使用Qt来实现一个支持多客户端链接的 多线程TCP服务器 及其 客户端 的设计与实现的解决方案。 注:本文使用的开发环境为Qt5.15.2, 使用MSVC2019_64编译器, C++11及以上 接下来我将会详细讲解客户端和服务端的设计与实现的关键细节。完整的源

    2024年01月16日
    浏览(47)
  • Linux毕业设计:基于OpenCV和QT库实现的人脸识别考勤/门禁系统(arm嵌入式ubuntu)

            本文介绍:Linux上以opencv和qt库实现的人脸识别系统,可应用于考勤、门禁等场景,具有人脸录入、删除、人脸检测、识别、用户管理等完整功能。可运行于ARM嵌入式linux、ubuntu即纯软件、ARM+PC组合等多种方式,应用场景多样且易于移植各个平台。 毕业设计题目汇

    2024年02月04日
    浏览(60)
  • 简易版人脸识别qt opencv

    1、配置文件.pro 2、头文件 3、源文件 main.app widget.cpp 4、ui界面

    2024年02月09日
    浏览(38)
  • QT+opencv【opencv学习篇】OpenCV 读取、显示和保存图像

    目录   一、OpenCV 读取图像 OpenCV 读取函数 参数: 二、OpenCV 显示图像 imshow函数 imshow函数功能 imshow函数原型 三、OpenCV 保存图像 四、结果和代码   OpenCV 允许我们对图像执行多种操作,但要做到这一点,需要读取一个图像文件作为输入,然后我们可以对其执行各种操作。Ope

    2024年02月16日
    浏览(46)
  • QT连接OpenCV库实现人脸识别

    图像容器: Mat类 读取图像:   命名展示图像的窗口: 展示图像: 示例: 视频流类: VideoCapture 打开视频: 若想要打开摄像头只需在构造时,调用构造函数参数传递0即可 读取视频流中图像:  图像翻转: 休眠阻塞函数: 示例: 灰度处理: 均衡化处理: 示例: opencv级联

    2024年02月09日
    浏览(42)
  • Qt-OpenCV学习笔记--人脸识别

    本人从事机械设计12年,业余时间自学编程。 2022年4月6日,开始学习C#, 2022年9月7日,开始学习c++和Qt, 2022年10月28日,开始学习OpenCV, 今天终于搞定了传说中的 人脸识别  ,在此,做个记录。 人脸检测,是基于Haar特征的cascade分类器, 人脸识别,是基于LDA理论的Fisherface算

    2024年02月09日
    浏览(39)
  • 基于Python+OpenCV智能答题卡识别系统——深度学习和图像识别算法应用(含Python全部工程源码)+训练与测试数据集

    本项目基于Python和OpenCV图像处理库,在Windows平台下开发了一个答题卡识别系统。系统运用精巧的计算机视觉算法,实现了批量识别答题卡并将信息导出至Excel表格的功能。这一解决方案使得答题卡的判卷过程变得轻便、高效且准确。 首先,我们以Python语言作为开发基础,结合

    2024年02月10日
    浏览(55)
  • OpenCV+Qt实现图像处理操作工具

    Qt界面实现 雪花屏 高斯模糊 中值滤波 毛玻璃 灰度化 XY方向模糊 双边模糊 腐蚀 [图像处理操作] 要求左边原图,右边效果图 结果展示如下:[图像处理实现有点多,就不一个一个地展示了,各别展示如下] 雪花屏 本文福利, 莬 费领取Qt开发学习资料包、技术视频,内容包括(

    2024年02月06日
    浏览(44)
  • OpenCV+ Qt Designer 开发人脸识别考勤系统

    本系统是一个基于OpenCV和 Qt Designer 的人脸识别考勤系统,主要功能是自动识别摄像头中的人脸,并把人脸对应的姓名和打卡时间存储到数据库中,方便管理人员进行考勤管理。本系统使用 face_recognition 库进行人脸识别,使用 PyQt5 开发界面,然后把界面与代码进行整合。 系统

    2024年02月06日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包