天天看點

opencv深入學習(3)-- Mat格式的幾個參數以及幾種元素存取方法的讨論

#include "stdafx.h"
#include <core.hpp>
#include <highgui.hpp>
#include <iostream>
#include <fstream>

using namespace cv;
using namespace std;

#pragma comment(lib,"opencv_core220d.lib")
#pragma comment(lib,"opencv_highgui220d.lib")


int _tmain(int argc, _TCHAR* argv[])
{
	ofstream outFile;
	outFile.open("pixel_32f.txt");

	char name[100] = "d://picture//lena.jpg";
	namedWindow("show", CV_WINDOW_AUTOSIZE);

	{
		Mat pnm = imread(name, -1);
		if (pnm.empty())
		{
			cout<<"read error"<<endl;
			return -1;
		}
		Mat temp;
		pnm.convertTo(temp, CV_32FC3);
		vector<Mat> vec;
		split(temp, vec);

		cout<<"pnm.channels = "<<pnm.channels()<<endl;
		cout<<"pnm.depth = "<<pnm.depth()<<endl;
		cout<<"pnm.dims = "<<pnm.dims<<endl;
		cout<<"pnm.elemsize = "<<pnm.elemSize()<<endl;
		cout<<"pnm.elemsize1 = "<<pnm.elemSize1()<<endl;
		for (int k=0; k<pnm.dims;++k)
		{
			cout<<"pnm.step = "<<pnm.step[k]<<endl;
		}
		cout<<"pnm.step1 = "<<pnm.step1()<<endl;
		cout<<"pnm.type = "<<pnm.type()<<endl;
		cout<<"pnm.total = "<<pnm.total()<<endl;
		cout<<"pnm.rows*cols = "<<pnm.cols*pnm.rows<<endl;
		cout<<"pnm.rows = "<<pnm.rows<<endl;
		cout<<"pnm.cols = "<<pnm.cols<<endl;
		cout<<endl;

		cout<<"temp.channels = "<<temp.channels()<<endl;
		cout<<"temp.depth = "<<temp.depth()<<endl;
		cout<<"temp.dims = "<<temp.dims<<endl;
		cout<<"temp.elemsize = "<<temp.elemSize()<<endl;
		cout<<"temp.elemsize1 = "<<temp.elemSize1()<<endl;
		for (int m=0; m<temp.dims; ++m)
		{
			cout<<"temp.step = "<<temp.step[m]<<endl;
		}
		cout<<"temp.step1 = "<<temp.step1()<<endl;
		cout<<"temp.type = "<<temp.type()<<endl;
		cout<<"temp.total = "<<temp.total()<<endl;
		cout<<"temp.rows = "<<temp.rows<<endl;
		cout<<"temp.cols = "<<temp.cols<<endl;
		cout<<endl;

		int i,j;
======section1======//
		int64 beg = cvGetTickCount();

		vector<Mat> spl;		
		split(temp, spl);
		for (i=0; i<temp.rows; ++i)
		{
			float *pt = spl[0].ptr<float>(i);
			for (j=0; j<temp.cols; ++j)
			{
				float mm = pt[j];
//				if(i<6)
//					outFile<<mm<<",";
				mm = mm/(float)20.6;

			}
//			outFile<<endl;
		}
		merge(spl, temp);

		int64 second = cvGetTickCount();

		cout<<(second-beg)/cvGetTickFrequency()<<endl;
		outFile<<(second-beg)/cvGetTickFrequency()<<endl;
//=========section2=========
		int64 secbeg = cvGetTickCount();

		for (i=0; i<temp.rows; ++i)
		{
			for (j=0; j<temp.cols; ++j)
			{
				float *mm = &(temp.ptr<float>(i)[3*j]);
//				if(i<6)
//					outFile<<*mm<<",";
				*mm = *mm/(float)20.6;
			}
//			outFile<<endl;
		}
		
		int64 third = cvGetTickCount();
		cout<<(third-secbeg)/cvGetTickFrequency()<<endl;
		outFile<<(third-secbeg)/cvGetTickFrequency()<<endl;
//============section3==========//		
		int64 thridbeg = cvGetTickCount();
		int col=temp.cols, row = temp.rows;
		if (temp.isContinuous())
		{
			col*=row;
			row =1;
		}
		for (i=0; i<row; ++i)
		{
			const float *pt = temp.ptr<float>(i);
			for (j=0; j<col;++j)
			{
				float mm=pt[3*j];
//				outFile<<mm<<",";
				mm = mm/(float)20.6;
			}
//			outFile<<endl;
		}
		int64 four = cvGetTickCount();
		cout<<(four-thridbeg)/cvGetTickFrequency()<<endl;
		outFile<<(four-thridbeg)/cvGetTickFrequency()<<endl;
///===========section4===============/
		int64 fourbeg = cvGetTickCount();
		int step0=temp.step[0],step1=temp.step[1];
		for (i=0; i<temp.rows; ++i)
		{
			for (j=0; j<temp.cols; ++j)
			{
				float *pix = (float *)(temp.data+i*step0+j*step1);
//				if(i<6)
//					outFile<<*pix<<",";
				*pix = *pix/(float)20.6;
			}
//			outFile<<endl;
		}
		int64 fifth = cvGetTickCount();
		cout<<(fifth-fourbeg)/cvGetTickFrequency()<<endl;
		outFile<<(fifth-fourbeg)/cvGetTickFrequency()<<endl;
	//============section5==========//		
		int64 fifthbeg = cvGetTickCount();
		int step00=temp.step[0],step01=temp.step[1];
		int col2=temp.cols, row2 = temp.rows;
		if (temp.isContinuous())
		{
			col2*=row2;
			row2 =1;
		}
		for (i=0; i<row2; ++i)
		{
			for (j=0; j<col2;++j)
			{
				float *mm= (float *)(temp.data+i*step00+j*step01);
				//outFile<<mm<<",";
				*mm = *mm/(float)20.6;
			}
			//	outFile<<endl;
		}
		int64 sixth = cvGetTickCount();
		cout<<(sixth-fifthbeg)/cvGetTickFrequency()<<endl;
		outFile<<(sixth-fifthbeg)/cvGetTickFrequency()<<endl;
		
		outFile.close();
		imshow("show", pnm);
		waitKey(0);
	}
	return 0;
}
           

下面是某次的運作結果以及對于Mat的幾個參數的分析。

//

pnm.channels = 3 ------------------通道數。

pnm.depth = 0 ------------------矩陣元素的基礎元素類型,CV_8U(uchar), CV_32F(float)...等.

pnm.dims = 2 ------------------矩陣的維數,圖像一般為2維的;

pnm.elemsize = 3 ------------------矩陣每個元素的大小(所占位元組數)=channel×sizeof(elemsize1);

pnm.elemsize1 = 1 ------------------矩陣每個元素的基礎元素大小=sizeof(uchar, float......等);

pnm.step = 1536 [0]---------------矩陣每行所占的位元組數,包括用于位元組對齊的位元組,如果不用補齊位元組則=cols*elemsize;

pnm.step = 3 [1]---------------矩陣中每個元素所占位元組數,對于2維矩陣=elemsize。

pnm.step1 = 1536 ------------------return normalized step; =step[0]/elemsize1,即每行的步長,it can be useful for fast access to arbitrary matrix element;

pnm.type = 16 ------------------傳回flags中表示每個元素類型,即CV_8UC3,CV_32FC1等表示的整數

pnm.total = 262144 ------------------=rows*cols

pnm.rows*cols = 262144

pnm.rows = 512

pnm.cols = 512

temp.channels = 3

temp.depth = 5

temp.dims = 2

temp.elemsize = 12

temp.elemsize1 = 4

temp.step = 6144

temp.step = 12

temp.step1 = 1536

temp.type = 21

temp.total = 262144

temp.rows = 512

temp.cols = 512

37098.3

34995

4189.36

6048.81

5776.15

//

最後的幾個運作結果每次是不一樣的,但是基本可以肯定的是前兩種是差不多的,第三種最少,第四和第五不相上下,但是第三種的缺點是丢失了原始的行列資訊,對于不計較元素位置的處理來說是首選,正如opencv2.2的手冊中所說,在存儲結構中沒有gap,計算不是太複雜時,可以提升10%-20%的效率。而對于多通道下的需要處理行列資訊的計算來說,個人偏好第四種,也就是opencv1.x的方式,直接使用原始指針操作,習慣上用着友善。第一二種方法是C++API的新的擷取方式,當然還有使用疊代器方式的存取等,詳細的見手冊或者cheet_sheet中提供的方法。

當然上面隻是對于元素類型是CV_32FC3的進行的測試,對于基礎的CV_8UC1/3沒有測試。

------後記-------

對于第四種費了很長時間才搞明白,開始的時候沒弄清楚,取出的資料不是不對就是直接出現通路異常。對于新的Mat結構所有的有效像素資料都是存儲在uchar* data中,是以取用時需要自己顯式的轉換類型,開始時沒有意識到在記憶體中存儲的是uchar類型,直接使用了*(temp.data+i*step0+j*step1)來擷取資料,結果總是取到錯誤的資料,後來意識到存儲的是float資料,需要轉換,就直接在前面加了(float)結果還是不行,最後仔細分析之後終于發現問題了====*(temp.data+i*step0+j*step1)是按照uchar将資料取出,轉換為uchar型的資料,如果在前面加上float轉換,隻是将uchar提升為了float而不是取出的是float,原來如此,将其改為(float *)(temp.data+i*step0+j*step1),也就是按照float的形式取出,也就是一次取出4個位元組作為一個資料,這樣就可以了。