把QImage转换为cv::Mat

来源:互联网 发布:手机触摸屏测试软件 编辑:程序博客网 时间:2024/06/05 09:30

本文受了http://blog.csdn.net/dancing_night/article/details/51545524的启发。

QImage里面有一个指针bits(),用来指向存储像素的内存首地址。而cv::Mat也有一个成员data,同样指向存储像素的内存首地址。那么,假如我想把QImage转化为cv::Mat,是否只要调用memcpy就可以了吗?

不是的。QImage的像素存储方式与Mat既有相似之处,也有不同之处。相同之处是,像素的保存方式都是用一位数组,一行一行的储存(行主序)。不同之处是,QImage储存的每一行像素,其字节数目必须是4的整数倍。来看下面的例子,假如一个 Grayscale8格式(每一个像素用一个字节表示)的QImage由2行7列组成,各个像素的取值从1到14递增.由于QImage的每一行必须用4n个字节表示,所以每一行必须空一个字节(红色圈出的位置),保证每一行是8个字节:


为了让程序员清楚知道每一行有多少有效字节,qimage给出了bytesPerLine() 函数。在本例子中,bytesPerLine() 返回7.

而cv::Mat则不同,它存储的字节是连续的,中间不空。因此,直接用memcpy来传递数据是有问题的,会造成字节错位。

下面给出一个函数,完成从QImage向cv::Mat的转化,在转化过程中,不会造成字节错位。

头文件:

#pragma once#include <opencv2/opencv.hpp>  #include <QImage>#include <QPainter>#include <QDebug>#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_core249d.lib")#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_imgproc249d.lib")#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_highgui249d.lib")#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_ml249d.lib")#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_video249d.lib")#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_features2d249d.lib")#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_calib3d249d.lib")#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_objdetect249d.lib")#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_contrib249d.lib")#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_legacy249d.lib")#pragma comment(lib, "E:\\cv\\opencv\\build\\x86\\vc11\\lib\\opencv_flann249d.lib")cv::Mat QImageToMat(QImage image);
cpp文件:

#include "cvUtility.h"cv::Mat QImageToMat(QImage image){cv::Mat mat;switch (image.format()){case QImage::Format_ARGB32:case QImage::Format_RGB32:case QImage::Format_ARGB32_Premultiplied:mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());break;case QImage::Format_RGB888:mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());cv::cvtColor(mat, mat, CV_BGR2RGB);break;case QImage::Format_Grayscale8:mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());break;}return mat;}

在转化过程中,cv::Mat的构造函数被告知QImage的每一行的有效数据占bytesPerLine()个字节,所以openCV库内部会做出相应的安排,避免错位。
下面的代码调用QImageToMat函数。我特意把QImage的行数设为250,不能被4整除。通过是否预定义宏 GRAY来决定QImage是灰度图还是彩图。亲测发现,不管是灰度图还是彩图,QImage向Mat的转化都是成功的。

#include "mat_qimage.h"#include "cvUtility.h"#define GRAYconst int iWidth = 250, iHeight = 255;unsigned char * m_pData = new unsigned char[iWidth * iHeight];QImage m_Img;Mat_QImage::Mat_QImage(QWidget *parent): QMainWindow(parent){ui.setupUi(this);#if defined(GRAY)for(int k = 0; k<iWidth;k++){for(int l = 0; l<iHeight; l++){m_pData[l * iWidth + k] = k;}}QImage img(m_pData, iWidth, iHeight, iWidth, QImage::Format_Grayscale8);qDebug()<<img.bytesPerLine();m_Img = QImage(iWidth, iHeight, QImage::Format_Grayscale8);#elseQImage img;img.load(QString("E:\\merge.bmp"));m_Img = QImage(iWidth, iHeight, img.format());#endifQPainter qp;qp.begin(&m_Img);qp.drawImage(m_Img.rect(), img, img.rect());qp.end();}Mat_QImage::~Mat_QImage(){if(m_pData)delete [] m_pData;}void Mat_QImage::paintEvent(QPaintEvent *e){QPainter qp;qp.begin(this);qp.drawImage(rect(), m_Img, m_Img.rect());qp.end();}void Mat_QImage::mouseDoubleClickEvent(QMouseEvent *e){cv::Mat mtx = QImageToMat(m_Img);bool b = cv::imwrite("E:\\mtx.jpg", mtx);qDebug()<<b;}




0 0