cv::Mat to QImage and back
Asked Answered
T

2

18

//Sorry for my english.

Tell me please, what I am doing wrong? I have read a lot about this. And write some code, but I have a terrible result.

As I understand in Opencv CV_8UC3 is the same as QImage::Format_RGB888 , except BRG and RGB accordingly.

to read cv::Mat in this format I can do:

cv::Mat mat1 = cv::imread("bugero.jpg",3); 

So, to convert cv::Mat to QImage I can do:

QImage Mat2QImage(cv::Mat const& src)
{
     cv::Mat temp(src.cols,src.rows,src.type());
     cvtColor(src, temp,CV_BGR2RGB);
     QImage dest= QImage((uchar*) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);
     return dest;
}

I made temp mat becouse I want to have copy of data in QImage.

Then. To convert it back I Have to do:

cv::Mat QImage2Mat(QImage const& src)
{
     QImage temp = src.copy();
     cv::Mat res(temp.height(),temp.width(),CV_8UC3,(uchar*)temp.bits(),temp.bytesPerLine());
     cvtColor(res, res,CV_BGR2RGB); 
     return res;
}

I have inserted cvtColor(res, res,CV_BGR2RGB); to make cv Mat with BGR colors. I do not exactly know what in inside this function cvtColor(res, res,CV_BGR2RGB);, But I decided that if cvtColor(res, res,CV_BGR2RGB); change places R and B, that will chage places of this colors back, because I did not found CV_BGR2RGB.

So, I wrote short sample program

#include <QApplication>
#include <QtGui>
#include <cv.h>
#include "opencv2/highgui/highgui.hpp"

QImage Mat2QImage(cv::Mat const& src)
{
     cv::Mat temp(src.cols,src.rows,src.type()); // make the same cv::Mat
     cvtColor(src, temp,CV_BGR2RGB); // cvtColor Makes a copt, that what i need
     QImage dest= QImage((uchar*) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);
     return dest;
}

cv::Mat QImage2Mat(QImage const& src)
{
     QImage temp = src.copy(); 
     cv::Mat res(temp.height(),temp.width(),CV_8UC3,(uchar*)temp.bits(),temp.bytesPerLine());
     cvtColor(res, res,CV_BGR2RGB); // make convert colort to BGR ! 
     return res; 
}


int main(int argc, char *argv[])
{
     QApplication a(argc, argv);
     QWidget W1;
     QWidget W2;
     QLabel imlab1(&W1);
     QLabel imlab2(&W2);
     W1.setWindowTitle("Convert cv::Mat to QImage First time"); 
     W2.setWindowTitle("Convert cv::Mat to QImage Second time");    




     cv::Mat mat1 = cv::imread("bugero.jpg",3);

     QImage qim1  = Mat2QImage(mat1);

     cv::Mat mat2 = QImage2Mat(qim1);

     QImage qim2 = Mat2QImage(mat2); 

     cv::Mat mat3 = QImage2Mat(qim2);



     cv::imshow("First Mat",mat1);
     imlab1.setPixmap(QPixmap::fromImage(qim1)); 
     W1.setFixedSize(qim1.size()); 
     cv::imshow("Convert QImage to cv::Mat firstly",mat2);
     imlab2.setPixmap(QPixmap::fromImage(qim2));
     W2.setFixedSize(qim2.size()); 
     cv::imshow("Convert QImage to cv::Mat secondly",mat2);
     W1.show();
     W2.show();

     return a.exec();
}

and .pro file

INCLUDEPATH += /usr/local/include/opencv /usr/local/include/opencv2
LIBS += -lopencv_core -lopencv_imgproc\
                                       -lopencv_highgui
QT       += gui
QT       += core
SOURCES += \
    QcvMat.cpp \

And I have got a BAD result!!! My bad result

Is there some? People,I need help!

I added some debug info to get cv::Mat.step and QImage.bytesPerLine() and it is different.

alex@lenovo /media/Files/Programming/Cpp/tests/QImagecvMat $ ./QcvMat 
cv step  942 
QImage  bytesPerLine  944 
cv step  942 
QImage  bytesPerLine  944 

What does it means and may be problem in it?

Tachygraphy answered 15/6, 2013 at 20:51 Comment(0)
L
40

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.

Lateritious answered 16/6, 2013 at 21:41 Comment(4)
Can't you just do dest.detach() and return dest? Also, detach() is not in the documentation. Why? Either way, this solution uses Qt's rgbSwapped() which works and is documented.Emanative
Nice link, when I was writing this answer this page didn't exist yet :). Definitively it is better solution (more universal). I see one error there for case CV_8UC1 (memory not detached). It was not exposed since it was used in chain with cvMatToQPixmap and this copies content. About this detach I didn't noticed that it is not documented. So I will fix this.Lateritious
I can just hug you right now! Worked like a magic for me, nicely explained fella!Contribute
So while calling the function say, QImage2Mat should we pass the reference of Qimage or we can just pass the variable directly?Deputize
T
2

Thanks a Lot! It is realy works! But. Why does memory whas spoiled? First. I have some memory load incv::Mat

cv::Mat mat1 = cv::imread("bugero.jpg",3);

mat1 - [=====================================]

then I put a copy of this cvMat to other cv:Mat

cv::Mat temp(src.cols,src.rows,src.type());
cvtColor(src, temp,CV_BGR2RGB);

mat1  -  [=========================================]

temp  -  [=========================================]

then make QImage from this data QImage dest= QImage((uchar*) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);

mat1  -   [============================================]

temp  - > [============================================]
         /
dest --/

And then temp goes out of scope and delete it self? QImage does not take ownership of it so memory in temp1 and dest marked as free, and there compiler can put other data? Is I am right?

Tachygraphy answered 17/6, 2013 at 8:43 Comment(1)
not spoiled (mem leak) but corrupted (dangling pointers). IMO problem was local temp matrix in Mat2QImage. QImage was referring to memory of that matrix and it is local variable so memory was freed on return form Mat2QImage.Lateritious

© 2022 - 2024 — McMap. All rights reserved.