cv::Mat 初識
Opencv種的Mat類,使得Opencv的編程更加的簡單,程序員不用過多的去關注內存管理,並且發現Opencv的編程有點像不需要太多編程技術的Matlab一樣,甚至有些函數名字都是一樣的。所以對Mat的瞭解是很有必要的。
首先我們在處理一塊數據的時候,如果使用Mat類,我們得到的好處是:
- 不需要手動申請一塊內存;
- 在不需要時不用再手動釋放內存;
- 可以通過類的封裝,方便的獲取到數據的相關信息。
顯然的,它利用了類的特性,將內存管理和數據信息封裝在類的內部,用戶只需要對Mat類對象進行數據或面向對象操作即可。
Mat類分爲兩個部分:矩陣頭和矩陣數據。如果我們在操作一副圖像的數據量時,矩陣數據的大小很大(一般約有1M的數據量),那麼拷貝和賦值函數所作的操作如果的深拷貝的話,效率會大大的降低。所以,Opencv的做法是隻複製其矩陣頭信息,而矩陣數據採用引用的方式,即多個Mat對象共享同一個矩陣數據,這裏使用的原理類似c++11中的共享指針。
如下示例:
cv::Mat A = cv::imread("erode.jpg");
cv::Mat B(A);
cv::Mat C = A;
printf("A.data = %p\nB.data = %p\nC.data = %p\n", A.data, B.data, C.data);
輸出結果如下:
A.data = 000001F0A0BF00C0
B.data = 000001F0A0BF00C0
C.data = 000001F0A0BF00C0
如上我們可以看到,三個Mat類對象的矩陣數據的地址是一樣的。那麼釋放內存的原則是怎樣的呢。這個也是內部使用了引用計數的方法,類似共享指針,當引用計數變爲0的時候纔會真正的釋放內存。
cv::Mat 類對象的創建方法
通過學習本人也總結了一些創建Mat類對象常用的幾種方法,在這裏也記錄一下。
1. 使用構造函數
cv::Mat M1(3, 3, CV_8UC4, cv::Scalar(0, 0, 0, 255));
std::cout << "M1 = " << std::endl << M1 << std::endl;
這裏指定矩陣的行和列,並表示爲4通道的矩陣,每個點的顏色值爲(0, 0, 0, 255)。輸出結果如下:
M1 =
[ 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255;
0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255;
0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255]
2. 通過數組初始化矩陣維數
int sz[2] = { 3, 3 };
cv::Mat M2(2, sz, CV_8UC1, cv::Scalar::all(0));
std::cout << "M2 = " << std::endl << M2 << std::endl;
如上,構造函數的第一個參數指定的是矩陣的維數,那麼sz數組表示的是每一維數的數量,即這裏表示的是3行3列。如果第一個參數是3的話,數組的大小也應該是3,表示的就是x,y,z三個維度,每個維度有3個。輸出如下:
M2 =
[ 0, 0, 0;
0, 0, 0;
0, 0, 0]
3. 通過create函數來初始化
cv::Mat M3;
M3.create(4, 4, CV_8UC(1));
std::cout << "M3 = " << std::endl << M3 << std::endl;
這裏是4*4的二維單通道矩陣,矩陣中的數據爲隨機值。如下:
[205, 205, 205, 205;
205, 205, 205, 205;
205, 205, 205, 205;
205, 205, 205, 205]
4. 通過opencv提供的類matlab的函數創建
cv::Mat Me = cv::Mat::eye(4, 4, CV_64F);
std::cout << "Me = " << std::endl << Me << std::endl;
cv::Mat Mo = cv::Mat::ones(4, 4, CV_64F);
std::cout << "Mo = " << std::endl << Mo << std::endl;
cv::Mat Mz = cv::Mat::zeros(4, 4, CV_64F);
std::cout << "Mz = " << std::endl << Mz << std::endl;
eye函數表示的是單位矩陣,ones顧名思義是全是1的矩陣,zeros表示全是0的矩陣。輸出如下:
Me =
[1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 1, 0;
0, 0, 0, 1]
Mo =
[1, 1, 1, 1;
1, 1, 1, 1;
1, 1, 1, 1;
1, 1, 1, 1]
Mz =
[0, 0, 0, 0;
0, 0, 0, 0;
0, 0, 0, 0;
0, 0, 0, 0]
5. 數據自定義矩陣Mat創建
cv::Mat M4 = (cv::Mat_<double>(3, 3) << 0, -1, 0, -1, 0, 0, 0, 0, 1);
std::cout << "M4 = " << std::endl << M4 << std::endl;
我們可以自己定義自己需要的數據量比較小的矩陣,然後通過如上的方式將其封裝到cv::Mat中。如下:
M4 =
[0, -1, 0;
-1, 0, 0;
0, 0, 1]
6. 通過clone函數創建不同的Mat
cv::Mat M5 = M4.row(1).clone();
std::cout << "M5 = " << std::endl << M5 << std::endl;
通過克隆函數clone獲取我們需要的某一行或列的數據,這裏構建出來的矩陣是深拷貝出來的Mat類對象。輸出如下:
M5 =
[-1, 0, 0]
以上,是對cv::Mat的簡單總結。