cv::Mat 转换为 QImage 并反向转换

18

//抱歉我的英语不好。

请告诉我,我做错了什么? 我已经阅读了很多资料并编写了一些代码,但是结果很糟糕。

据我理解,在OpenCV中,CV_8UC3QImage :: Format_RGB888相同,只是BGR和RGB不同。

要以这种格式读取cv :: Mat,可以执行以下操作:

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

所以,要将cv::Mat转换为QImage,我可以这样做:

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

我创建了一个临时mat,因为我想在QImage中拥有数据的副本。

然后,要把它转换回来,我必须执行:

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

我已经插入了cvtColor(res, res, CV_BGR2RGB);来使得OpenCV的Mat对象使用BGR颜色。我不确切知道这个函数cvtColor(res, res, CV_BGR2RGB);内部发生了什么,但我决定如果cvtColor(res, res, CV_BGR2RGB);交换了红色和蓝色的位置,那么它会将颜色位置恢复,因为我没有找到CV_BGR2RGB

因此,我编写了一个短的示例程序。

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

和 .pro 文件

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

我得到了一个糟糕的结果!!! My bad result

有没有人可以帮忙吗?

为了获取cv :: Mat.step和QImage.bytesPerLine()的调试信息,我添加了一些调试信息,并且它们是不同的。

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

这是什么意思,可能存在问题吗?

2个回答

40

除了一个问题,代码看起来很好。
内存管理。在这个问题上,cv::MatQImage不同。请记住,QImage使用写时复制机制,并为每个副本共享内存。 cv::Mat也共享内存,但它不执行写时复制(我也是OpenCV的新手(2周),所以还无法准确解释其工作原理,但由于此问题而遇到了一些崩溃)!
另一件事是,当您从内存图像创建QImage时,它会使用该内存,但不会拥有该内存。
最终结果是,在Linux和Qt5中,由于内存管理问题,您的代码会崩溃。在您的屏幕截图中,您可以在第二个窗口的顶部看到一些奇怪的情况发生,并且可以看到一些内存垃圾。

所以我已经更正了您的转换函数,现在完美运行:

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

所以我们都需要阅读关于openCV中的内存管理的内容 : )。

离题:
在Linux上将openCV包含到qt项目中最好的方法是在pro文件中添加类似以下的内容:

# add open CV
unix {
    CONFIG += link_pkgconfig
    PKGCONFIG += opencv
}

当您将代码移动到另一台机器上时,您将不再遇到路径问题。


3
你能不能只调用 dest.detach() 并返回 dest?另外,为什么 detach() 没有在文档中提到呢?不管怎样,这个解决方案使用了 Qt 的 rgbSwapped() 函数,该函数已经被记录和证明有效。 - takfuruya
很好的链接,在我写这个答案时这个页面还不存在 :). 明确地说,这是更好的解决方案(更通用)。我在那里看到一个错误,对于CV_8UC1情况(内存未分离)。由于它与cvMatToQPixmap一起使用并且这会复制内容,因此它没有被暴露出来。关于这个detach,我没有注意到它没有被记录。所以我会修复这个问题。 - Marek R
我现在就想拥抱你! 对我来说像魔法一样奏效,解释得很好,伙计! - PRIME
在调用函数QImage2Mat时,我们应该传递QImage的引用还是直接传递变量? - arqam

2

非常感谢!真的很有效!但是,为什么内存会被破坏呢? 首先,我有一些内存负载incv::Mat。

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

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

然后我将这个cvMat的副本复制到另一个cv::Mat中。
cv::Mat temp(src.cols,src.rows,src.type());
cvtColor(src, temp,CV_BGR2RGB);

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

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

然后从这些数据中创建QImage。 QImage dest = QImage((uchar*) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);

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

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

那么temp超出范围并删除自身? QImage不会拥有它,因此在temp1和dest中的内存被标记为自由,并且编译器可以放置其他数据?我是正确的吗?


2
不是破坏(内存泄漏),而是损坏(悬空指针)。在我看来,问题出在Mat2QImage函数中的本地temp矩阵上。QImage引用了该矩阵的内存,但它是一个局部变量,因此在从Mat2QImage返回时内存被释放了。 - Marek R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接