1. 程式人生 > >OpenCV中Mat的基本用法

OpenCV中Mat的基本用法

一、矩陣基礎操作:
Mat image(240, 320, CV8UC3);
第一個引數是rows,該矩陣的行數;第二個引數是cols,該矩陣的列數;第三個引數是該矩陣元素的型別。這句話表示建立一個大小為240×320的矩陣,裡面的元素為8位unsigned型,通道數(channel)有3個。

image.create(480, 640, CV8UC3);
分配(或重新分配)image矩陣,把大小設為480×640,型別設為CV8UC3。

Mat A33(3, 3, CV_32F, Scalar(5));
定義並初始化一個3×3的32bit浮點數矩陣,每個元素都設為5。

Mat B33(3, 3, CV_32F);
B33 = Scalar(5);

和上面的作用一樣。

Mat C33 = Mat::ones(3, 3, CV32F)*5.;
ones函式很像MATLAB裡的語句吧。這句的意思是先定義一個3×3的32bit浮點數矩陣,元素全為1,所有元素再乘以5.0。

Mat D33 = Mat::zeros(3, 3, CV32F) + 5.;
和上面類似,先定義個3×3的32bit浮點數矩陣,元素全為0,再將所有元素加上5.0;

double a = CV_PI/3;
Mat A22 = (Mat_<float>(2, 2) << cos(a), -sin(a), sin(a), cos(a));
CV_PI就是少年派的那個派。第二句這個寫法很牛x的樣子,我也沒見過,不過看樣子是建立一個2×2的float矩陣,把後面四個三角函式值分別賦給4個元素。


float B22data[] = {cos(a), -sin(a), sin(a), cos(a)};
Mat B22 = Mat(2, 2, CV32F, B22data).clone();
第一句建立一個普通陣列B22data,第二句建立一個2×2的32bit浮點數矩陣,並使用用B22data數組裡的值初始化,然後克隆一下賦給B22矩陣。
為什麼這裡還要克隆一下,不是多此一舉嗎?不是的,用一個數組去初始化一個矩陣的話,你會發現這個矩陣引用了陣列的記憶體地址。不克隆的話,上面例程的後果是B22data陣列、Mat(2,2...)這個臨時變數矩陣、B22矩陣這三把勺子都插在同一個碗裡。

randu(image, Scalar(0), Scalar(256));

把image弄成一個符合正太分佈的隨機數矩陣,rand表示random,u表示uniform;第二個引數是隨機數下限,方括號哦;第三個引數是隨機數上限,圓括號。

randn(image, Scalar(128), Scalar(10));
高斯分佈的隨機數矩陣;第二個引數是均值,第三個引數是標差。

Mat image_alias = image;
沒有拷貝里面的資料。

float* Idata=new float[480*640*3];
Mat I(480, 640, CV32FC3, Idata);
第一句定義一個480×640×3×sizeof(float)位元組這麼大的陣列;第二句建立一個矩陣I,引用的是Idata的地址。

vector<Point> iptvec(10);
Mat iP(iptvec);
還能和STL一起用啊,以前真不知道。第一句話建立了一個有10個Point的向量,注意Point有兩個int型元素。第二句建立了一個矩陣,元素型別根據就自動設為CV_32SC2,表示32bit signed int,2個channel。

IplImage* oldC0 = cvCreateImage(cvSize(320,240),16,1);
Mat newC = cvarrToMat(oldC0);
IplImage oldC1 = newC;
CvMat oldC2 = newC;
這是為了把經典的OpenCV影象導成矩陣,第一句建立一個320×240的影象;第二句話把IplImage轉成Mat;第三句話把Mat轉成IplImage;第四句把Mat轉成CvMat。

Mat newC2 = cvarrToMat(oldC0).clone();
轉換後克隆一下賦值。

vector<Point2f> ptvec = Mat<Point2f>(iP);
把Mat又轉成vector。

A33.at<float>(i,j) = A33.at<float>(j,i)+1;
操作A33矩陣在row為i,col為j處的元素。需要顯式指定A33裡的元素型別,本例是float。

Mat dyImage(image.size(), image.type());
for(int y = 1; y < image.rows-1; y++)
{
  Vec3b* prevRow = image.ptr<Vec3b>(y-1);
  Vec3b* nextRow = image.ptr<Vec3b>(y+1);
  for(int x = 0; y < image.cols; x++)
    for(int c = 0; c < 3; c++)
      dyImage.at<Vec3b>(y,x)[c] = saturate_cast<uchar>(nextRow[x][c] - prevRow[x][c]);
}
第一句話建立一個和image大小、型別都一樣的矩陣。
Vec3b是一個預定義的型別,三個無符號字元組成的向量:
typedef Vec<uchar, 3> Vec3b;
saturate_cast是一個強制型別轉換,把圓括號裡的東西轉換為尖括號裡的型別。
for迴圈裡的語句是把矩陣dyImage裡的元素都賦值為後一行的值減去前一行的值,注意這個矩陣裡的值是個具有三個元素的向量,理解為陣列就行了,所以用方括號訪問。

Mat<Vec3b>::iterator it = image.begin<Vec3b>(), itEnd = image.end<Vec3b>();
for(; it != itEnd; ++it)
  (*it)[1] ^= 255;
用過STL裡的迭代器嗎?用過的話就不解釋了,沒用過的還是先看看STL吧。

對image每個值(Vec3b向量)的第一個元素和0xFF做異或。

自從版本2.0,OpenCV採用了新的資料結構,用Mat類結構取代了之前用extended C寫的cvMat和lplImage,更加好用啦,最大的好處就是更加方便的進行記憶體管理,對寫更大的程式是很好的訊息。

需要注意的幾點:

1. Mat的拷貝只是複製了Mat的資訊頭,資料的指標也指向了被拷貝的資料地址,而沒有真正新建一塊記憶體來存放新的矩陣內容。這樣帶來的一個問題就是對其中一個Mat的資料操作就會對其他指向同一塊資料的Mat產生災難性的影響。

2.建立多維陣列的格式是這樣的
  1. int sz[3] = {2, 2, 2};  
  2. Mat L(3, sz, CV_8UC(1), Scalar::all(0));  

3.傳統的lplImage格式也可直接轉換為Mat格式
  1. IplImage* img = cvLoadImage("greatwave.png", 1);  
  2. Mat mtx(img); // convert IplImage* -> Mat

如果想將新版本的Mat格式轉換為老版本,則需要如下呼叫:

  1. Mat I;  
  2. IplImage* pI = &I.operator IplImage();  
  3. CvMat* mI = &I.operator CvMat();  

不過更安全的呼叫格式為:
  1. Ptr<IplImage> piI = &I.operator IplImage();  

4.Mat結構更加友好,很多操作更接近matlab的風格 5.也有Point2f,Point3f,vector等資料結構可以使用

6.RNG類可以產生隨機數

7.實現顏色通道的分離使用函式split