How to Convert an Opencv Cv::Mat to Qimage

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_RGB888. 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 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.

Fast way to convert from cv::Mat to QImage

waitKey(30) will pause your code for about 30 milliseconds.

This line doesn't make much sense in that function, so you should better remove it. Creating a QImage the way you do should not take much more than some microseconds, because no image is copied etc... However cap >> frame could take some time, depending on your capturing device (but you can't do much against that).

If you really need the waitKey you should use waitKey(1) to not waste much time there.

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

Read OpenCV Mat 16bit to QImage 8bit Greyscale

I faced the same type kind of issue.
The following code is the solution I used to fix that case.

   #include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

#include <QImage>

int main(void)
{

//Read the 16 bit per pixel image.
cv::Mat I = cv::imread('16BitsPerPixelImage.tiff',cv::IMREAD_ANYDEPTH|cv::IMREAD_ANYCOLOR);

//Convert from 16 bit per pixel to 8 bit per pixel using a min max normalization and store it a 8 bit per pixel.
cv::normalize(I,I,0.,255.,cv::NORM_MINMAX,CV_8U);

// Then actualy the easiest way to convert it to a QImage is to save a temporary file and open it using QT functions.
// PNG use a compression without loss algorithm.
cv::imwrite("/tmp/convert16to8.png",I);

QImage QI;

QI.load("/tmp/convert16to8.png");

return EXIT_SUCCESS;
}


Related Topics



Leave a reply



Submit