简体   繁体   English

QT C++ 如何从QThread返回QImage并在QMainWindow的QLabel上显示?

[英]QT C++ How to return QImage from QThread and display it on QLabel in QMainWindow?

I am trying to process some image using OpenCV Library inside a thread, because the process operation takes some time before it's done.我正在尝试在线程内使用 OpenCV 库处理一些图像,因为处理操作需要一些时间才能完成。

So the problem is QThread always returning a Null QImage to the Slot in QMainWindow.所以问题是 QThread 总是返回一个 Null QImage 到 QMainWindow 中的 Slot。

I get this Exception error:我收到此异常错误:

Exception thrown at 0x00007FFE01962F6D (Qt5Guid.dll) in QtWidgetsApplication1.exe: 0xC0000005: Access violation reading location 0x0000022CAB6EE080.

Error occurs in this file:此文件中出现错误:

qtwidgetsapplication1.cpp this file is for QMainWindow qtwidgetsapplication1.cpp此文件用于 QMainWindow

#include "qtwidgetsapplication1.h"
#include "stdafx.h"


QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this); 
    
    connect(ui.addItem_btn, SIGNAL(clicked()), this, SLOT(addItem_btn_OnClick())); // Add Item to the List
    
    dftThread = new DetectFaceThread(this);

    connect(dftThread, &DetectFaceThread::detectedFace, this, &QtWidgetsApplication1::onDetectedFace);
    connect(dftThread, &DetectFaceThread::finished, dftThread, &QObject::deleteLater);
}

QtWidgetsApplication1::~QtWidgetsApplication1()
{
    
}

void QtWidgetsApplication1::addItem_btn_OnClick()
{
    dftThread->start();
}

void QtWidgetsApplication1::onDetectedFace(const QImage& face)
{
    
    if (face.isNull())
    {
        QMessageBox::warning(this, QString("Detection Error"), QString("Face not detected!"));
        return;
    }
    ui.imgDisplay_label->setPixmap(QPixmap::fromImage(face));
}

Here is my Code:这是我的代码:

DetectFaceThread.h DetectFaceThread.h

#pragma once
#include <qthread.h>
#include <QtWidgets/qmessagebox.h>
#include <qmutex.h>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>


class DetectFaceThread :
    public QThread
{
    Q_OBJECT

public:
    DetectFaceThread(QWidget* parent = nullptr);
    ~DetectFaceThread();
    void run() override;

signals:
    void detectedFace(const QImage &face);
};

DetectFaceThread.cpp DetectFaceThread.cpp

#include "DetectFaceThread.h"

DetectFaceThread::DetectFaceThread(QWidget* parent)
{

}

DetectFaceThread::~DetectFaceThread()
{
    QMessageBox::information(nullptr, QString("Thread Info"), QString("Thread successfully destroyed"));
}

void DetectFaceThread::run()
{
    QMutex mutex;

    mutex.lock();

    std::string img_path = "res/paper.jpg";
    cv::Mat img = cv::imread(img_path);

    if (img.empty())
    {
        QMessageBox::warning(nullptr, QString("Load Error"), QString("Image not found!"));
        return;
    }

    cv::cvtColor(img, img, cv::ColorConversionCodes::COLOR_BGR2RGB);

    float w = 800, h = 1000;

    cv::Point2f src[4] = { {383, 445}, {885, 521}, {89, 1125}, {921, 1270} };
    cv::Point2f dst[4] = { {0.0f, 0.0f}, {w, 0.0f}, {0.0f, h}, {w, h} };

    cv::Mat matrix = getPerspectiveTransform(src, dst);
    cv::Mat img_warp;
    cv::warpPerspective(img, img_warp, matrix, cv::Size(w, h));

    QImage qimage(img_warp.data, img_warp.cols, img_warp.rows, img_warp.step, QImage::Format::Format_RGB888);

    mutex.unlock();

    emit detectedFace(qimage);
}

And in the end the application just terminates, can any one help me please.最后应用程序刚刚终止,请任何人帮助我。

Update: I tried your solution but it throws the same Exception error.更新:我尝试了您的解决方案,但它引发了相同的异常错误。

connect(dftThread, &DetectFaceThread::detectedFace, this, &QtWidgetsApplication1::onDetectedFace, Qt::QueuedConnection);

The main problem is that the thread affinity of a QThread is the thread on which it is currently handling events -- usually the thread on which it was created, not the thread it manages and on which its run member function will execute.主要问题是QThread的线程亲和性是它当前正在处理事件的线程——通常是创建它的线程,而不是它管理的线程,并且它的run成员 function 将在其上执行。 So, with...所以,随着...

connect(dftThread, &DetectFaceThread::detectedFace, this, &QtWidgetsApplication1::onDetectedFace);

both dftThread and this have the same thread affinity meaning you effectively have a direct connection between code running on two different threads without any synchronization -- that's undefined behaviour. dftThreadthis都具有相同的线程亲和性,这意味着您可以有效地在两个不同线程上运行的代码之间建立直接连接,而无需任何同步——这是未定义的行为。 Change the above to..把上面改成..

connect(dftThread, &DetectFaceThread::detectedFace,
        this, &QtWidgetsApplication1::onDetectedFace,
        Qt::QueuedConnection);

Other issues.其他问题。 In DetectFaceThread::run you have...DetectFaceThread::run你有...

if (img.empty()) {
    QMessageBox::warning(nullptr, QString("Load Error"), QString("Image not found!"));
    return;
}

This will create a GUI element (a QMessageBox ) on a non-GUI thread.这将在非 GUI 线程上创建一个 GUI 元素(一个QMessageBox )。 That's not supported.这是不支持的。

Also, the use of a QMutex scoped locally within DetectFaceThread::run doesn't make any sense.此外,在DetectFaceThread::run中使用本地范围的QMutex没有任何意义。 What is it supposed to protect -- it's not visible to any code other than DetectFaceThread::run ?它应该保护什么——除了DetectFaceThread::run之外的任何代码都看不到它?

Edit: One further issue is that the QImage constructor you're using doesn't make a deep copy of the data passed.编辑:另一个问题是您使用的QImage构造函数不会对传递的数据进行深层复制。 Try changing...换个试试...

emit detectedFace(qimage);

to...至...

emit detectedFace(qimage.copy());

That should force a deep copy of the original to be passed to the slot.这应该强制将原件的深层副本传递到插槽。

I think the problem is in creating an object image in the DetectFaceThread thread.我认为问题在于在 DetectFaceThread 线程中创建 object 图像。 When the run () function is executed, all data in run () will be destroyed.当run() function被执行时,run()中的所有数据都会被销毁。 I suggest creating an object image in the main thread and performing image processing in DetectFace.我建议在主线程中创建一个 object 图像并在 DetectFace 中执行图像处理。 And use movetothread, but not the run override method.并使用 movetothread,但不使用运行覆盖方法。

working example工作示例

 ---------------MainWindow.h-------------
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
#include <QThread>
#include <detectface.h>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    QThread thread;
    DetectFace detect;
private:
    Ui::MainWindow *ui;
public slots:
    void showResult();
private slots:
    void on_pushButton_clicked();
};
#endif // MAINWINDOW_H


 ---------------MainWindow.cpp-------------
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    connect(&thread,&QThread::started,&detect,&DetectFace::detectFace);
    connect(&detect,&DetectFace::endDetectFace,&thread, &QThread::quit);
    connect(&thread, &QThread::finished, this, &MainWindow::showResult);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::showResult()
{
    cv::Mat *image = detect.getImage();
    QImage qimage = QImage((uchar*) image->data, image->cols, image->rows, image->step, QImage::Format_RGB888);
    ui->label->setPixmap(QPixmap::fromImage(qimage,Qt::AutoColor));
}


void MainWindow::on_pushButton_clicked()
{
    while(thread.isRunning())
    {
        thread.quit();
    }
    detect.moveToThread(&thread); //transfer object img in thread
    thread.start();

}

---------------DetectFace.h--------------
#ifndef DETECTFACE_H
#define DETECTFACE_H

#include <QObject>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
#include <QMessageBox>
class DetectFace : public QObject
{
    Q_OBJECT
    cv::Mat img;
    cv::Mat img_warp;
    std::string img_path ;
public:

    DetectFace();
    cv::Mat* getImage();
    void detectFace();
signals:
    void endDetectFace();
};

#endif // DETECTFACE_H

---------------DetectFace.cpp--------------
#include "detectface.h"

DetectFace::DetectFace()
{
    img_path = "res/paper.jpg";
}

cv::Mat *DetectFace::getImage()
{
    return &img_warp;
}

void DetectFace::detectFace()
{

     img = cv::imread(img_path);

     if (img.empty())
     {
         QMessageBox::warning(nullptr, QString("Load Error"), QString("Image not found!"));
         return;
     }

     cv::cvtColor(img, img, cv::ColorConversionCodes::COLOR_BGR2RGB);

     float w = 800, h = 1000;

     cv::Point2f src[4] = { {383, 445}, {885, 521}, {89, 1125}, {921, 1270} };
     cv::Point2f dst[4] = { {0.0f, 0.0f}, {w, 0.0f}, {0.0f, h}, {w, h} };

     cv::Mat matrix = cv::getPerspectiveTransform(src, dst);
     cv::warpPerspective(img, img_warp, matrix, cv::Size(w, h));

     emit endDetectFace();

}

-----------------main cpp------------
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

Please read what the QImage ctor tells you: The buffer must remain valid throughout the life of the QImage请阅读QImage ctor告诉您的内容:缓冲区必须在 QImage 的整个生命周期内保持有效

Your data goes out-of-scope before.您的数据之前超出范围。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM