簡體   English   中英

QT C++ 如何從QThread返回QImage並在QMainWindow的QLabel上顯示?

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

我正在嘗試在線程內使用 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);

主要問題是QThread的線程親和性是它當前正在處理事件的線程——通常是創建它的線程,而不是它管理的線程,並且它的run成員 function 將在其上執行。 所以,隨着...

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

這應該強制將原件的深層副本傳遞到插槽。

我認為問題在於在 DetectFaceThread 線程中創建 object 圖像。 當run() function被執行時,run()中的所有數據都會被銷毀。 我建議在主線程中創建一個 object 圖像並在 DetectFace 中執行圖像處理。 並使用 movetothread,但不使用運行覆蓋方法。

工作示例

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

請閱讀QImage ctor告訴您的內容:緩沖區必須在 QImage 的整個生命周期內保持有效

您的數據之前超出范圍。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM