I am trying to process some image using OpenCV Library inside a thread, because the process operation takes some time before it's done.
So the problem is QThread always returning a Null QImage to the Slot in QMainWindow.
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
#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
#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);
}
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. 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. Change the above to..
connect(dftThread, &DetectFaceThread::detectedFace,
this, &QtWidgetsApplication1::onDetectedFace,
Qt::QueuedConnection);
Other issues. In DetectFaceThread::run
you have...
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. That's not supported.
Also, the use of a QMutex
scoped locally within DetectFaceThread::run
doesn't make any sense. What is it supposed to protect -- it's not visible to any code other than DetectFaceThread::run
?
Edit: One further issue is that the QImage
constructor you're using doesn't make a deep copy of the data passed. 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. When the run () function is executed, all data in run () will be destroyed. I suggest creating an object image in the main thread and performing image processing in DetectFace. And use movetothread, but not the run override method.
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
Your data goes out-of-scope before.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.