I am trying to create an application where I am trying to integrate opencv and qt.
I managed successfully to convert a cv::Mat to QImage by using the code below:
void MainWindow::loadFile(const QString &fileName)
{
cv::Mat tmpImage = cv::imread(fileName.toAscii().data());
cv::Mat image;
if(!tmpImage.data || tmpImage.empty())
{
QMessageBox::warning(this, tr("Error Occured"), tr("Problem loading file"), QMessageBox::Ok);
return;
}
/* Mat to Qimage */
cv::cvtColor(tmpImage, image, CV_BGR2RGB);
img = QImage((const unsigned char*)(image.data), image.cols, image.rows, QImage::Format_RGB888);
imgLabel->setPixmap(QPixmap::fromImage(img));
imgLabel->resize(imgLabel->pixmap()->size());
saveAsAct->setEnabled(true);
}
However, when I am trying to convert the QImage to cv::Mat by using the following code:
bool MainWindow::saveAs()
{
if(fileName.isEmpty())
{
QMessageBox::warning(this, tr("Error Occured"), tr("Problem loading file"), QMessageBox::Close);
return EXIT_FAILURE;
}else{
outputFileName = QFileDialog::getSaveFileName(this, tr("Save As"), fileName.toAscii().data(), tr("Image Files (*.png *.jpg *.jpeg *.bmp)\n *.png\n *.jpg\n *.jpeg\n *.bmp"));
/* Qimage to Mat */
cv::Mat mat = cv::Mat(img.height(), img.width(), CV_8UC4, (uchar*)img.bits(), img.bytesPerLine());
cv::Mat mat2 = cv::Mat(mat.rows, mat.cols, CV_8UC3 );
int from_to[] = {0,0, 1,1, 2,2};
cv::mixChannels(&mat, 1, &mat2, 1, from_to, 3);
cv::imwrite(outputFileName.toAscii().data(), mat);
}
saveAct->setEnabled(true);
return EXIT_SUCCESS;
}
I have no success and the result is totally disordered image. In the net that I searched I saw that the people are using this way without mentioning any specific problems. Does someone have any idea, about what could be cause the problem? Thanks in advance.
Theoodore
P.S. I am using opencv 2.4 and Qt 4.8, under a Arch Linux system with gnome-3.4
Just find out the "correct" solution of copying(not reference) the QImage to cv::Mat
The answer of Martin Beckett is almost correct
for (int i=0;i<image.height();i++) {
memcpy(mat.ptr(i),image.scanline(i),image.bytesperline());
}
I don't see the full codes, but I guess you may want to use it like this way
cv::Mat mat(image.height(), image.width(), CV_8UC3);
for (int i=0;i<image.height();i++) {
memcpy(mat.ptr(i),image.scanline(i),image.bytesperline());
}
But this code exist a problem, the memory allocated by cv::Mat may not have the same "bytesperline" as the QImage
The solution I found is take the reference of the QImage first, then clone it
return cv::Mat(img.height(), img.width(), format, img.bits(), img.bytesPerLine()).clone();
The solution of Martin Beckett suggested could generate correct result in most of the times, I didn't notice there are a bug until I hit it.
Whatever, I hope this is a "correct" solution. If you find any bug(s), please let everyone know so we could have a change to improve the codes.
I think you might find this useful. http://www.jdxyw.com/?p=1480
It uses IplImage to get data, but you can use cv::Mat and Mat.data to get a pointer to the original matrix. I hope you might find this useful.
opencv images are stepped so that each row begins on a multiple of 32bits, this makes memory access faster. If you are using a 3byte/pixel format then unless you have a width that is 1/3 of a multiple of 4 you will have 'spare' memory at the end of each line
The safe way is to copy the image data a row at a time. The opencv mat.ptr(row) returns a pointer to the start of each row and the QImage .scanline(row) member does the same
See How to output this 24 bit image in Qt
edit: Something like
for (int i=0;i<image.height();i++) {
memcpy(mat.ptr(i),image.scanline(i),image.bytesperline());
}
From this source code
QImage MatToQImage(const 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();
}
else
{
qDebug() << "ERROR: Mat could not be converted to QImage.";
return QImage();
}
} // MatToQImage()
This is what i got from here thanks to jose. It helps me to get over this.
To visualize OpenCV image (cv::Mat) in Qt (QImage), you must follow these steps:
- Invert color sequence:
cv::cvtColor(imageBGR, imageRGB, CV_BGR2RGB);
- Change format OpenCV to Qt:
QImage qImage((uchar*) imageRGB.data, imageRGB.cols, imageRGB.rows, imageRGB.step, QImage::Format_RGB888);
- Use QPainter to render the image.
Please note the use of QImage::Format. Read the Qt on this issue.
if you're still looking for the solution. here's one :
Cv::Mat to QImage :
QImage Mat2QImage(cv::Mat &image )
{
QImage qtImg;
if( !image.empty() && image.depth() == CV_8U ){
if(image.channels() == 1){
qtImg = QImage( (const unsigned char *)(image.data),
image.cols,
image.rows,
QImage::Format_Indexed8 );
}
else{
cvtColor( image, image, CV_BGR2RGB );
qtImg = QImage( (const unsigned char *)(image.data),
image.cols,
image.rows,
QImage::Format_RGB888 );
}
}
return qtImg;
}
For QImage to cv::Mat .
cv::Mat QImage2Mat(QImage &image) {
cv::Mat cvImage;
switch (image.format()){
case QImage::Format_RGB888:{
cvImage = cv::Mat(image.height(),
image.width(),
CV_8UC3,
image.bits(),
image.bytesPerLine());
cv::cvtColor(cvImage, cvImage, CV_RGB2BGR);
return cvImage;
}
case QImage::Format_Indexed8:{
cvImage = cv::Mat(image.height(),
image.width(),
CV_8U,
image.bits(),
image.bytesPerLine());
return cvImage;
}
default:
break;
}
return cvImage;
}
来源:https://stackoverflow.com/questions/11170485/qimage-to-cvmat-convertion-strange-behaviour