【问题标题】:QT C++ How to return QImage from QThread and display it on QLabel in QMainWindow?QT C++如何从QThread返回QImage并将其显示在QMainWindow的QLabel上?
【发布时间】:2021-04-25 15:32:53
【问题描述】:

我正在尝试在线程内使用 OpenCV 库处理一些图像,因为处理操作需要一些时间才能完成。

所以问题是 QThread 总是返回一个 Null QImage 到 QMainWindow 中的 Slot。

我得到这个异常错误:

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

此文件出现错误:

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));
}

这是我的代码:

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

#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);
}

最后应用程序就终止了,谁能帮帮我。

更新:我尝试了您的解决方案,但它引发了相同的异常错误。

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

【问题讨论】:

    标签: c++ visual-studio qt


    【解决方案1】:

    我认为问题在于在 DetectFaceThread 线程中创建对象图像。当run()函数执行时,run()中的所有数据都会被销毁。 我建议在主线程中创建对象图像并在 DetectFace 中执行图像处理。并使用 movetothread,但不使用 run override 方法。

    工作示例

     ---------------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();
    }
    

    【讨论】:

    • 我其实是QT的新手,你能给我一个很好的例子吗,因为我问题中的代码来自QThread文档
    • 您的示例中的 test 是什么
    • 这种方法的主要思想是在主线程中创建对象,然后传递另一个流进行处理,最后得到对象,这就是结果。
    • 谢谢,这实际上解决了我的问题
    【解决方案2】:

    主要问题是QThread 的线程亲和性是它当前正在处理事件的线程——通常是创建它的线程,不是它管理的线程和其run 成员函数将在其上执行。所以,随着...

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

    dftThreadthis 都具有相同的线程亲和性,这意味着您可以有效地在两个不同线程上运行的代码之间建立直接连接,而无需任何同步——这是未定义的行为。把上面的改成..

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

    其他问题。在DetectFaceThread::run 你有...

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

    这将在非 GUI 线程上创建一个 GUI 元素(QMessageBox)。不支持。

    此外,在 DetectFaceThread::run 内使用局部作用域的 QMutex 没有任何意义。它应该保护什么 - 除了DetectFaceThread::run 之外的任何代码都看不到它?

    编辑: 另一个问题是您使用的QImage 构造函数不会对传递的数据进行深层复制。尝试改变...

    emit detectedFace(qimage);
    

    到...

    emit detectedFace(qimage.copy());
    

    这应该强制将原始的深层副本传递到插槽。

    【讨论】:

    • 我尝试了你的建议,但同样的错误,请参阅我的更新。
    • 尝试将信号/槽参数类型从const QImage &amp;更改为QImage
    【解决方案3】:

    请阅读QImage ctor 告诉您的内容:缓冲区必须在 QImage 的整个生命周期内保持有效

    您的数据之前超出范围。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-16
      • 2021-12-25
      • 2018-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多