1. 程式人生 > >淺析OpenCV::Mat 型別的畫素儲存機理

淺析OpenCV::Mat 型別的畫素儲存機理

由於工作需要,經常要把一幅灰度圖儲存到一段記憶體裡,每一個位元組代表一個畫素的亮度。然後還要把這段畫素儲存到opencv的Mat變數裡面。下面用程式來測試,Mat裡面的畫素資料是否也指向同一段記憶體。

#include "matprobe.h"
#include <opencv2/opencv.hpp>  
#include <QImage>
#include <QPainter>
#include <QDebug>

using namespace cv; 

#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")


unsigned char * m_pData = new unsigned char[256 * 256];
QImage m_Img;

MatProbe::MatProbe(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);
	for(int k = 0; k<256;k++)
	{
		for(int l = 0; l<256; l++)
		{
			m_pData[l * 256 + k] = k;
		}
	}
}

MatProbe::~MatProbe()
{
	if(m_pData)
		delete [] m_pData;
}

void MatProbe::paintEvent(QPaintEvent *e)
{
	QImage img(m_pData, 256, 256, 256, QImage::Format_Grayscale8);
	QImage m_Img(256, 256,  QImage::Format_Grayscale8);
	QPainter qp;
	qp.begin(&m_Img);
	qp.fillRect(m_Img.rect(), QBrush(img));
	qp.end();

//	QPainter qp;
	qp.begin(this);
	qp.drawImage(rect(), m_Img, m_Img.rect());
	qp.end();
}

void MatProbe::mouseDoubleClickEvent(QMouseEvent *e)
{
	Mat mtx(256, 256, CV_8UC1, m_pData);
	delete [] m_pData;
	m_pData = NULL;
	bool b = imwrite("D:\\mtx.jpg", mtx);
	qDebug()<<b;
}
執行後的對話方塊是一個從黒向白的漸變:

然後雙擊對話方塊,並通過斷點來觀察Mat的畫素儲存地址:


注意兩個紅圈標註的地址,一個是原始資料的地址,一個是Mat的畫素的地址,兩者相同。這說明程式只是把m_pData的地址傳到mtx,並沒有給mtx開闢新記憶體(所謂的“淺拷貝”)。mouseDoubleClickEvent函式執行完畢後,儲存下來的影象《mtx.jpg》也不是一幅漸變的圖,而是混亂的灰度圖。儘管我沒有對Mat的畫素做任何直接的操作,但是我釋放了m_pData,導致mtx的內容發生變化:


假如程式設計師需要建立一個和m_pData不相關的Mat變數,可以利用已有的mtx,做一次 clone()操作,獲取新的Mat型別的變數。clone是“深拷貝”,它的返回值是一個Mat型別,且這個返回變數擁有自己的記憶體,而不是跟前面的Mat變數共享同一段記憶體。

對程式碼做如下修改,儲存的影象就不會出問題:

void MatProbe::mouseDoubleClickEvent(QMouseEvent *e)
{
	Mat mtx(256, 256, CV_8UC1, m_pData);
	/************注意下面語句*******************/
	Mat mtx_clone = mtx.clone();

	delete [] m_pData;
	m_pData = NULL;
//還有下面:
	bool b = imwrite("D:\\mtx.jpg", mtx_clone);
	qDebug()<<b;
}