Mat详解-OpenCV
轉自:http://blog.skyoung.org/2014/03/26/OpenCV(III)-How-to-use-Mat/
Mat類是OpenCV最基本的一個數據類型,它可以表示一個多維的多通道的數組。Mat常用來存儲圖像,包括單通道二維數組——灰度圖,多通道二維數組——彩色圖。當然也可以用來存儲點云,直方圖等等,對于高維的數組可以考慮存儲在SparseMat中。對于一個Mat對象M,其數據布局是由M.step[]決定的,數據存放在M.data里面,假設M有d維,則數據的尋址方式為:
addr(Mi0,...,id?1)=M.data+i0?M.step[0]+...+id?1?M.step[d?1]addr(Mi0,...,id?1)=M.data+i0?M.step[0]+...+id?1?M.step[d?1]
例如ImgImg是一個二維三通道矩陣,則,
addr(Imgi0,i1)=M.data+i0?M.step[0]+i1?M.step[1]addr(Imgi0,i1)=M.data+i0?M.step[0]+i1?M.step[1]
這里需要說明的是各個維度的步長滿足如下關系:M.step[i] >= M.step[i+1]*M.size[i+1],也就是二維數組的數據的存放是一行一行的,三維數組數據存放是一面一面的。
下面給出OpenCV中Mat類的一個粗略定義如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class CV_EXPORTS Mat { public:// ... a lot of methods ....../*! includes several bit-fields:- the magic signature- continuity flag- depth- number of channels*/int flags;//! the array dimensionality, >= 2int dims;//! the number of rows and columns or (-1, -1) when the array has more than 2 dimensionsint rows, cols;//! pointer to the datauchar* data;//! pointer to the reference counter;// when array points to user-allocated data, the pointer is NULLint* refcount;// other members... }; |
構造Mat的方法
構造Mat的方式有很多種,下面把常用的方法一一列出:
使用構造函數Mat(nrows, ncols, type[, fillValue]),例如,
| 1 2 | // 構建3×2的4通道8位矩陣,每個元素初始值為(1,2,3,4) Mat M(3,2,CV_8UC4,Scalar(1,2,3,4)); |
使用M.create(nrows,ncols,type),例如,
| 1 2 | //構建100×100的10通道8位矩陣 M.create(100,100,CV_8UC(10)) |
構建多維的矩陣,
| 1 2 3 | //構建一個100×100×100的8位三維矩陣 int sz[] = {100,100,100} Mat Cube(3, sz, CV_32F, Scalar::all(0)) |
使用復制構造函數或者賦值操作符
| 1 2 | Mat A(B); Mat C = B; |
單獨對矩陣的某一行某一列進行操作
| 1 2 3 4 5 6 | //第4行加上第6行的3倍賦值給第4行 M.row(3) = M.row(3) + M.row(5)*3;// 把第8列拷貝到第2列,通過 M.col(1) = M.col(7)是不起作用的,應該: Mat M1 = M.col(1); M.col(7).copyTo(M1); |
構建矩陣的ROI區域,單獨操作ROI區域的值
| 1 2 3 | Mat img(Size(320,240),CV_8UC3); Mat roi(img, Rect(10,10,100,100)); roi = Scalar(0,255,0); |
確定矩陣在原矩陣中的相對位置,使用locateROI,
| 1 2 3 4 5 6 | Mat A = Mat::eye(10, 10, CV_32S); Mat B = A(Range::all(), Range(1, 3)); Mat C = B(Range(5, 9), Range::all()); Size size; Point ofs; //得出ofs為(1,5),size為(10,10),為什么是(10,10)?目前沒搞清楚 C.locateROI(size, ofs); |
對于外部數據輸入,進行初始化
| 1 2 3 4 5 6 7 8 9 10 | //外部輸入一個一維數組 void process_video_frame(const unsigned char* pixels, int width, int height, int step) { Mat img(height, width, CV_8UC3, pixels, step); GaussianBlur(img, img, Size(7,7), 1.5, 1.5); }//用二維數組初始化矩陣 double m[2][3] = { {1, 2, 3}, {4, 5, 6} }; Mat M = Mat(2, 3, CV_64F, m); |
| 1 2 3 4 5 | IplImage* img = cvLoadImage("lena.jpg", 1); Mat mtx(img); // IplImage* -> Mat IplImage* img1 = mtx; //Mat -> IplImage* CvMat oldmat = mtx; // Mat -> CvMat Mat mtx1(oldmat); //CvMat -> Mat |
類似Matlab方式和<<賦值
| 1 2 3 4 5 6 7 | //類似Matlab中的單位矩陣等 M = Mat::ones(10, 10, CV_64F); M = Mat::eye(10, 10, CV_64F); M = Mat::zeros(10, 10, CV_64F);//使用`Mat_`和`<<`配合 Mat M = (Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1); |
獲取Mat元素的方法
構造好矩陣后,剩下一個很重要的事情就是如何快速準確的獲取矩陣Mat中的元素,下面列出幾種常用的獲取Mat中的元素方法:
使用M.at(i,j)
| 1 | M.at<double>(i,j) |
對于二維矩陣,可以采取逐行獲取的方式:
| 1 2 3 4 5 6 7 | double sum=0; for(int i = 0; i < M.rows; i++) {const double* Mi = M.ptr<double>(i);for(int j = 0; j < M.cols; j++)sum += std::max(Mi[j], 0.); } |
對于不在乎矩陣的形狀,只是簡單的遍歷矩陣的元素的,可以采用更快速的方法,首先檢查元素排列是否連續,如果是,可以看成一個一維數組訪問。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | double sum=0; int cols = M.cols, rows = M.rows; if(M.isContinuous()) {cols *= rows;rows = 1; } for(int i = 0; i < rows; i++) {const double* Mi = M.ptr<double>(i);for(int j = 0; j < cols; j++)sum += std::max(Mi[j], 0.); } |
仿照STL中,使用迭代器訪問:
| 1 2 3 4 | double sum=0;MatConstIterator_<double> it = M.begin<double>(), it_end = M.end<double>();for(; it != it_end; ++it)sum += std::max(*it, 0.); |
這個矩陣的迭代器可以傳給STL的算法,例如std::sort()。
Mat提供的常用成員函數
賦值操作符’=’
除了普通的矩陣賦值外,如果一個Scalar賦值給一個Mat,則表示把Mat的所有元素賦值為這個Scalar值。
矩陣的取行列及對角線操作
A.row(i),A.col(j)這些操作返回矩陣A的第i行和第j列。A.rowRange(m,n)和A.colRange(m,n)分別取的是A的第m行到第n行(包括m行,不包括n行)和A的第m列到第n列(包括m列,不包括n列)。這里需要注意一個問題,對于
| 1 | A.row(i) = A.row(j) |
這一操作,并不能把第j行復制到第i行,因為A.row()返回的只是矩陣的頭,以上操作僅僅相當于兩個指針的操作,所指內存其實是沒有發生變化的。如果想把第j行復制到第i行,可以
| 1 | A.row(j).copyTo(A(i)) |
當右邊的矩陣發生操作后,是可以賦值的,比如
| 1 2 | A.row(i) = A.row(j)*a A.row(i) = A.row(j) + Scalar(0,0,0); |
A.diag(i)取的是矩陣的對角線,這里i=0代表最中間的對角線,i=1是偏右上一行的對角線,i=-1是左下一行的對角線,例如:
| 1 2 3 | Mat A = (Mat_<float>(3,3)<< 1,9,3,7,5,0,7,3,9); |
A.diag(0)取得是{1,5,9},A.diag(1)取得是{9,0},A.diag(-1)取得是{7,3}。
復制函數
| 1 2 3 | A.clone()//返回A的拷貝。 A.copyTo(B)//執行把A拷貝到B矩陣中。 A.copyTo(B,mask)//進拷貝mask對應的部分 |
轉換矩陣元素的數據類型
| 1 2 | A.converTo(B,tpye,scale)//把A的類型轉換為type并且按照scale縮放A到B矩陣中 assignTo(A,type)//更改A的元素數據類型 |
設定矩陣的值
| 1 | A.setTo(s)//把A中所有的值賦值為s |
更改矩陣的通道數和行數
A.reshape()改變通道數,A.resize()改變行數。其中A.reshape()這個操作不改變roscolschannels的個數,僅僅相當于重構這些元素,例如:
| 1 2 3 | vector<Point> vec;//vec是N個Point Mat pointMat = Mat(vec); //pointMat是一個三通道的N×1的矩陣 pointMat.reshape(1)//pointMat變為一個單通道N×3的矩陣 |
更改矩陣的行數如下
| 1 | A.resize(sz) //A變為sz行 |
locateROI和adjustROI
這兩個函數主要是對submatrix的操作,即通過A.row(),A(Range(i,j),Range::all())等操作獲得的submatrix在原始矩陣中位置。例如:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Mat A = (Mat_<float>(3,3)<< 1,9,3,7,5,0,7,3,9); Mat B = A(Range(0,2),Range(1,3));//B變為 {9,3,// 5,0} Size sz; Point p1; B.locateROI(sz, p1); cout<<sz<<" "<<p1<<endl; //sz是原矩陣的大小3×3,p1是B在A中位置(1,0) B.adjustROI(0,1,0,0); //四個參數分別是上下左右平移的像素數,這里是把B向下平移1行,//最后得出B為{9,3,// 5,0,// 3,9} cout<<B<<endl; |
Mat的各項屬性
| 1 2 3 4 5 6 7 8 9 10 11 12 | A.total() //元素的個數 A.elemSize() //元素的大小,如果是8UC3的話,返回3*sizeof(uchar) A.elemSize1() //如果是8UC3的話,返回sizeof(uchar) A.type() //元素的數據類型 A.depth()//元素的位數 A.channels()//矩陣的通道數 A.step1() //矩陣的每一行元素的個數,A.step/A.elemSize1 A.size() //矩陣的尺寸 //注意以下是成員變量不是成員函數 A.step //矩陣的一行的字節數 A.rows //矩陣的行數,即高 A.cols //矩陣的列數,即寬 |
總結
Mat提供了關于矩陣的一些基本操作,這對圖像的操作打下了堅實的基礎,更多復雜的算法都是基于這些操作實現的
總結
以上是生活随笔為你收集整理的Mat详解-OpenCV的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: char *与char []
- 下一篇: 蒜发芽了还能吃吗