眾所周知,Mat類型相比IPLImage有諸多優點,網上相關解釋較多,此處不再贅述。本文總結了三種最常用的Mat類型數據訪問方式,給出了標准寫法,希望對大家有幫助。
-
Mat 類型數據的訪問
這個問題網上有很多資源,但是不太統一,實際使用時會感到混亂。在本博客的代碼都是在VS2010 + opencv2.4.10運行后通過的,盡量確保代碼的簡潔性和正確性。CSDN的魏大神列舉了13種訪問方式[1],確實很好很強大。但是實際中我們只要掌握三種方式就夠了,關鍵是對這三種方式要熟練掌握。
-
方式1: at<type>(i,j)訪問
這種方式在Debug模式下的訪問速度是最慢的,但是在Release模式下的訪問速度也是相當快的,和其他兩種方式相近。楊大神將幾種常用訪問方式比喻成普通青年、文藝青年、暴力青年[2],十分恰當。方式1就是所謂的普通青年了。先上代碼吧!
1 int ROWS = 100; // height
2 int COLS = 200; // width
3 Mat img1(ROWS , COLS , CV_32FC1);
4
5 for (int i=0; i<ROWS ; i++) 6 { 7 for (int j=0; j<COLS ; j++) 8 { 9 img1.at<float>(i,j) = 3.2f; 10 } 11 }
普通青年的代碼看似十分Naive,但是他能夠絕對確保安全,不會發生指針越位問題,稍后會給出一個例子。我們在實際中會碰到各種類型的圖像,dupuleng 筒子貢獻了對應表[3],方便普通青年使用at操作。
Mat_<uchar>---------CV_8U
Mat<char>-----------CV_8S
Nat_<short>---------CV_16S
Mat_<ushort>--------CV_16U
Mat_<int>-----------CV_32S
Mat_<float>----------CV_32F
Mat_<double>--------CV_64F
再給出一個多通道圖像的訪問方式:
1 int ROWS = 100; // height 2 int COLS = 200; // width 3 Mat img1(ROWS , COLS , CV_8UC3); 4 5 for (int i=0; i<ROWS ; i++) 6 { 7 for (int j=0; j<COLS ; j++) 8 { 9 img1.at<vec3b>(i,j)[0]= 3.2f; // B 通道 10 img1.at<vec3b>(i,j)[1]= 3.2f; // G 通道 11 img1.at<vec3b>(i,j)[2]= 3.2f; // R 通道 12 } 13 }
-
方式2: ptr<type>(i) [j] 方式
這種訪問方式就是所謂的文藝青年了,dupuleng 筒子認為這是加強版,我覺得這是十分高效的方法,所以就定義它為標准的ptr訪問方式。
1 int ROWS = 100; // height 2 int COLS = 200; // width 3 Mat img1(ROWS , COLS , CV_32FC1); 4 5 for (int i=0; i<ROWS ; i++) 6 { 7 float* pData1=img5.ptr<float>(i); 8 for (int j=0; j<COLS ; j++) 9 { 10 pData1[j] = 3.2f; 11 } 12 }
方才說到普通青年有時候會比兩外兩種青年更靠譜,為了生成H通道和S通道像素值的二維直方圖,各划分為20個bin,文藝青年的寫法如下:
1 Mat channel_H(ROWS,COLS,CV_32FC1,Scalar(0.5)); 2 Mat channel_S(ROWS,COLS,CV_32FC1,Scalar(0.5)); 3 int hs_map[20][20] = {0}; 4 for (int i = 0; i < ROWS; i++) 5 { 6 float* Hdata = channel_H.ptr<float>(i); 7 float* Sdata = channel_S.ptr<float>(i); 8 for (int j = 0; j < COLS; j++) 9 { 10 int x = (int)floor(Hdata[j]* 20); //floor是向下取整 11 int y = (int)floor(Sdata[j]* 20); 12 if (x >= 20) 13 x = 19; 14 if (y >= 20) 15 y = 19; 16 hs_map[x][y]++; 17 } 18 }
經過多次調試,仍然發生指針越界,才領悟到文藝青年是不靠譜的,注釋掉第16行就不會越界,但是注釋掉了這一行我用這段代碼干嘛呢?大神們可以給出我出錯的原因,我搞不定,直接變身為普通青年就解決了。
-
方式3:img.data + step[0]*i + step[1]*j 方式
這就是所謂的暴力青年了,對於上面那個例子,暴力青年的寫法如下:
1 Mat channel_H(ROWS,COLS,CV_32FC1,Scalar(0.5)); 2 Mat channel_S(ROWS,COLS,CV_32FC1,Scalar(0.5)); 3 int hs_map[20][20] = {0}; 4 for (int i = 0; i < ROWS; i++) 5 { 6 float* Hdata = (float*)(channel_H.data + channel_H.step[0] * i); //可以觀察到channel_S.dims不斷增加,不知何故 7 float* Sdata = (float*)(channel_S.data + channel_S.step[0] * i); 8 for (int j = 0; j < COLS; j++) 9 { 10 int x = (int)floor(Hdata[channel_H.step[1]*j]* 20); 11 int y = (int)floor(Hdata[channel_S.step[1]*j]* 20); 12 if (x >= 20) 13 x = 19; 14 if (y >= 20) 15 y = 19; 16 hs_map[x][y]++; 17 } 18 }
當然暴力青年的做法在此處也是不可行的,我再次擁抱普通青年了。補充一下,由於是float類型的數據,故step[1] = 4, step[0] = 4* COLS;
[1] http://blog.csdn.net/xiaowei_cqu/article/details/19839019
[2] http://blog.csdn.net/yang_xian521/article/details/7161335
[3] http://www.cnblogs.com/dupuleng/articles/4072736.html