cv::Mat to QImage and back
Code looks fine with one exception.
Memory management. cv::Mat
doesn't work like QImage
in this mater. Remember that QImage
is using copy on write mechanism and shares memory for each copy.cv::Mat
also shares memory but it doesn't do copy on write (I'm also new with open cv (2 weeks) so I can't explain yet exactly how it works but I've stumbled on some crushes because of that)!
Another thing is that when you are creating QImage
from memory image is using this memory and doesn't take ownership of it.
Final outcome is that on Linux and Qt5 your code is crashes because of problems with memory management. On your screen shot you can see at the top of second window that something strange is going on and you see some memory trash.
So I've corrected your conversion functions it works perfectly:
QImage Mat2QImage(cv::Mat const& src)
{
cv::Mat temp; // make the same cv::Mat
cvtColor(src, temp,CV_BGR2RGB); // cvtColor Makes a copt, that what i need
QImage dest((const uchar *) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);
dest.bits(); // enforce deep copy, see documentation
// of QImage::QImage ( const uchar * data, int width, int height, Format format )
return dest;
}
cv::Mat QImage2Mat(QImage const& src)
{
cv::Mat tmp(src.height(),src.width(),CV_8UC3,(uchar*)src.bits(),src.bytesPerLine());
cv::Mat result; // deep copy just in case (my lack of knowledge with open cv)
cvtColor(tmp, result,CV_BGR2RGB);
return result;
}
So we both have to do a reading about memory management in open-CV :).
OFFTOPIC:
Best way to include openCV in qt projects on Linux is to add to pro file something like:
# add open CV
unix {
CONFIG += link_pkgconfig
PKGCONFIG += opencv
}
You will be free of path problems when moving code to another machine.
Converting cv::mat to QImage
Your problem is the conversion of the QImage to cv::Mat
, when using the flag 0 in cv::imread
implies the reading is grayscale, and you are using the conversion with the format QImage::Format_RGB88
8. I use the following function to make the conversion of cv::Mat
to QImage
:
static QImage MatToQImage(const cv::Mat& mat)
{
// 8-bits unsigned, NO. OF CHANNELS=1
if(mat.type()==CV_8UC1)
{
// Set the color table (used to translate colour indexes to qRgb values)
QVector<QRgb> colorTable;
for (int i=0; i<256; i++)
colorTable.push_back(qRgb(i,i,i));
// Copy input Mat
const uchar *qImageBuffer = (const uchar*)mat.data;
// Create QImage with same dimensions as input Mat
QImage img(qImageBuffer, mat.cols, mat.rows, mat.step, QImage::Format_Indexed8);
img.setColorTable(colorTable);
return img;
}
// 8-bits unsigned, NO. OF CHANNELS=3
if(mat.type()==CV_8UC3)
{
// Copy input Mat
const uchar *qImageBuffer = (const uchar*)mat.data;
// Create QImage with same dimensions as input Mat
QImage img(qImageBuffer, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
return img.rgbSwapped();
}
return QImage();
}
After that I see that you have misconceptions of how QGraphicsView
and QGraphicsScene
work when commenting: put the frame, which contains image 3, to the GUI, with ui->graphicsView_4->setScene(scene);
you are not setting a frame but a scene, and the scene should only be set once and preferably in the constructor.
// constructor
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
So when you want to load the image just use the scene:
cv::Mat image= cv::imread(filename.toStdString(), CV_LOAD_IMAGE_GRAYSCALE);
cv::Mat histEquImg;
equalizeHist(image, histEquImg);
QImage qimage = MatToQImage(histEquImg);
QPixmap pixmap = QPixmap::fromImage(qimage);
scene->addPixmap(pixmap);
ui->graphicsView->fitInView(scene->sceneRect(), Qt::KeepAspectRatio);
The complete example can be found in the following link.
cv::Mat to QImage conversion
Your solution to the problem is not efficient, in particular it is less efficient then the code I posted on the other question you link to.
Your problem is that you have to convert from grayscale to color, or RGBA. As soon as you need this conversation, naturally a copy of the data is needed.
My solution does the conversion between grayscale and color, as well as between cv::Mat and QImage at the same time. That's why it is the most efficient you can get.
In your solution, you first try to convert and then want to build QImage around OpenCV data directly to spare a second copy. But, the data you point to is temporary. As soon as you leave the function, the cv::Mat free's its associated memory and that's why it is not valid anymore also within the QImage. You could manually increase the reference counter of the cv::Mat beforehand, but that opens the door for a memory leak afterwards.
In the end, you attempt a dirty solution to a problem better solved in a clean fashion.
from cvMat to QImage
as @SpamBot mentioned, the data is freed when Mat goes out of scope. try deep copying the data:
QImage mat2qimage(const Mat& mat)
{
Mat rgb;
cvtColor(mat, rgb, CV_BGR2RGB);
return QImage((const unsigned char*)(rgb.data), rgb.cols, rgb.rows, QImage::Format_RGB888).copy();
}
Qt - Transform cv::Mat to QImage in worker thread crashes
It doesn't have a chance of working, since the QImage
wraps transient data that then is deallocated by cvMat
. At the minimum you should emit the copy of the image (literally qImage.copy()
. Ideally, you'd wrap the cvMat
lifetime management in QImage
's deleter, so that the copy is not necessary, and the matrix gets destroyed along with the image that wraps it. Since format conversions are usually needed between cv::Mat
and QImage
, it's probably best to perform the conversion between the source cv::Mat
and another cv::Mat
that wraps the QImage
-owned memory. That solution avoids memory reallocations, since the QImage
can be retained in the class performing the conversion. It is then also comaptible with Qt 4, where QImage
doesn't support deleters.
See this answer for a complete example of an OpenCV video capture Qt-based widget viewer, and this answer for a complete example of multi-format conversion from cv::Mat
to QImage
.
How to convert a cv::Mat to QImage or QPixmap?
Your code seems to be hopelessly convoluted and you do a lot of unnecessary things. There's no reason to have cv_size
a pointer. You should just use an instance of cv::Size
. Your Mat2QImage
returns a QImage
with a dangling pointer to its data.
The code below is a complete, tested example. It maintains the otherwise unnecessary Ui
namespace etc. just to make it similar to your existing code base.
Some publicized Mat2QImage
-style methods are broken as they return a QImage
that uses the mat's data without retaining a reference to it. If the source mat ceases to exist, the image references a dangling pointer, and anything can happen. That was your problem. The version below is correct in this respect.
#include <QApplication>
#include <QBasicTimer>
#include <QImage>
#include <QPixmap>
#include <QGridLayout>
#include <QLabel>
#include <opencv2/opencv.hpp>
namespace Ui { struct UIQT {
QLabel * image;
void setupUi(QWidget * w) {
QGridLayout * layout = new QGridLayout(w);
layout->addWidget((image = new QLabel));
}
}; }
class UIQT : public QWidget {
Q_OBJECT
Ui::UIQT ui;
QBasicTimer m_timer;
cv::Size m_size;
void timerEvent(QTimerEvent *);
public:
UIQT(QWidget * parent = 0);
~UIQT();
Q_SLOT void refreshImage();
};
void matDeleter(void* mat) { delete static_cast<cv::Mat*>(mat); }
static QImage imageFromMat(cv::Mat const& src) {
Q_ASSERT(src.type() == CV_8UC3);
cv::Mat * mat = new cv::Mat(src.cols,src.rows,src.type());
cvtColor(src, *mat, CV_BGR2RGB);
return QImage((uchar*)mat->data, mat->cols, mat->rows, mat->step,
QImage::Format_RGB888, &matDeleter, mat);
}
static cv::Scalar randomScalar() {
static cv::RNG rng(12345);
return cv::Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255));
}
static QPixmap pixmapFromMat(const cv::Mat & src) {
QImage image(imageFromMat(src));
return QPixmap::fromImage(image);
}
UIQT::UIQT(QWidget * parent) :
QWidget(parent),
m_size(100, 100)
{
ui.setupUi(this);
m_timer.start(500, this);
refreshImage();
}
UIQT::~UIQT() {}
void UIQT::timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
refreshImage();
}
void UIQT::refreshImage() {
cv::Mat mat(m_size, CV_8UC3, randomScalar());
ui.image->setPixmap(pixmapFromMat(mat));
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
UIQT w;
w.show();
return app.exec();
}
#include "main.moc"
Related Topics
Colour Output of Program Run Under Bash
Memory Stability of a C++ Application in Linux
How to Know If One Shared Library Depends on Another Shared Library or Not
How to Make a Function Async-Signal-Safe
Libstdc++ Static Linking in Dynamic Library
/Usr/Lib64/Libstdc++.So.6: Version 'Glibcxx_3.4.15' Not Found
How to Get a List of Installed True Type Fonts on Linux Using C or C++
Pthread Condition Variables Not Signalling Even Though Set to Pthread_Process_Shared
Shgetknownfolderpath Equivalent API in Linux
Hello World Python Extension in C++ Using Boost
Detecting If Computer Is Idle Based on Mouse and Keyboard Interactions
External Library Throws Undefined Reference Errors in Qt Creator
Using Qsocketnotifier to Select on a Char Device
How to Compile SQLite with Icu
C++ How to Use Select to See If a Socket Has Closed
What Is the Performance Penalty of C++11 Thread_Local Variables in Gcc 4.8