cv::Mat的代數運算、變形與分割
關於cv::Mat最基礎的內存結構與元素訪問筆記在以下博文中:
OpenCV3.4.0學習筆記(一)——cv::Mat的內存結構與訪問
可以自由讀寫cv::Mat數組中的任一元素後,以下考慮 cv::Mat 的一些常用操作。
這裏將常用操作分爲三類:
- 代數運算
- 變形
- 分割
代數運算
cv::Mat 的代數運算接近矩陣運算,以下給出常用的幾種代數運算代碼。
cv::Mat a,b,c;
/*矩陣級運算*/
c = a + b; //矩陣加法
c = a - b; //矩陣減法
c = a + 1; //矩陣數加,位置可互換
c = a - 1; //矩陣數減,位置可互換
c = a * 1; //矩陣數乘,位置可互換
c = a * b; //矩陣乘,限二維單通道矩陣
c = a.inv(); //矩陣求逆,限二維單通道矩陣
c = a.t(); //矩陣轉置,限二維單通道矩陣
cv::Scalar trace = cv::trace(a); //矩陣求跡
double det = cv::determinant(a); //矩陣行列式,要求a爲單通道浮點型矩陣
cv::eigen(a,eigenValue,eigenVector); //對稱單通道浮點型矩陣a的特徵值特徵向量特徵向量
//eigenValue與eigenVector均爲cv::Mat型變量,eigenValue存放了特徵值,
//行向量eigenVector[m]對應eigenValue[m],也就是有如下關係成立:
//src*eigenvectors.row(i).t() = eigenvalues.at<srcType>(i)*eigenvectors.row(i).t()
cv::solve(a,b,c); //求解線性方程 A*C = B 中C的最佳值。C與B規模相同,爲多個列向量,A必須爲方陣
/*元素級運算*/
//元素級代數運算
cv::multiply(a,b,c,2); //矩陣元素相乘並放大給定的因子
cv::divide(a,b,c); // c=a./b,逐元素除法
cv::exp(a,c); //逐元素求自然底數爲底的指數
cv::log(a,c); //逐元素求自然底數爲底的對數
cv::pow(a,2,c); //逐元素求指定數的冪次方,注意,對a中非整數對象,則先取絕對值再冪次方
cv::phase(a,b,c); //c = arctan2(b,a);逐元素運算
//不等運算,逐元素比較,返回0\255矩陣
c = a > b;
c = a >= b;
c = a < b;
c = a <= b;
c = a == b;
//矩陣元素位運算
c = a & b;
c = a | b;
c = a ^ b;
c = ~a;
c = a & 1;
//按元素求絕對值
c = cv::abs(a);
//矩陣元素比較並取值
c = cv::max(a,b);
c = cv::max(a,1);
c = cv::min(a,b);
c = cv::min(a,1);
cv::Scalar sum = cv::sum(a); //矩陣元素求和(適用於多通道)
cv::Scalar means = cv::mean(a); //求a全部元素平均值(適用於多通道)
變形
變形是指改變 cv::Mat 的維度以及維度上的數據長度。常用 cv::Mat::reshape() 函數來實現。
reshape的本質是改變 cv::Mat 的 dims,step,size,channals的矩陣頭數據,不改變data中的儲存順序。
因此reshape函數的執行效率很高。同時注意 Mat 的數據總長是:
以下給出幾種常用的reshape函數重載格式:
cv::Mat& cv::Mat::reshape(int channalsCount, std::vector<int> newshape);
cv::Mat& cv::Mat::reshape(int channalsCount, int newdims, int* newsize);
cv::Mat& cv::Mat::reshape(int channalsCount, int newrows = 0);
需要注意的是,reshape()的返回值是更改後的矩陣信息頭。以下面代碼爲例:
cv::Mat a = cv::Mat::zeros(2, 2, CV_32FC1);
cv::Mat b = a.reshape(1,1); //調用第三個重載的reshape
cout << a.size() << endl; //a的size不發生改變,爲 2*2
cout << b.size() << endl; //b的size爲 1*4
cout << (unsigned long long)a.data << endl; //a.data與b.data相等
cout << (unsigned long long)b.data << endl;
分割
常用的矩陣分割包括矩陣指定區域截取、通道分離與合成等。
先給出矩陣指定區域截取的幾個重載操作符的代碼:
cv::Mat operator ()(std::vector<cv::Range> &ranges);
cv::Mat operator ()(cv::Range* ranges);
cv::Mat operator ()(cv::Rect &roi); //限於二維矩陣
cv::Mat operator ()(cv::Range rowRange, cv::Range colRange); //限於二維矩陣
cv::Mat row(int index) //返回矩陣第index行,限於二維矩陣
cv::Mat col(int index) //返回矩陣第index列,限於二維矩陣
需要注意,矩陣分割的返回值是原有矩陣的引用,也即通過返回值可以修改原有矩陣的數據。
通過以下代碼加以說明:
cv::Mat a = cv::Mat::zeros(3, 3, CV_32FC1);
cv::Mat b = a(cv::Range(0,1),cv::Range(0,1)); //調用第4個重載的()
cout << a.at<float>(0, 0) << endl; //輸出0
b.at<float>(0,0) = 1;
cout << a.at<float>(0, 0) << endl; //輸出1
再給出通道分離與融合的幾個重載方法
//通道分離函數
void cv::split(cv::Mat &muiltChannalMat, std::vector<cv::Mat> channalsMaps);
//muiltChannalMat爲多通道Mat,channalsMaps爲單通道Mat向量,多通道與單通道Mat的規模一樣,
//channalsMaps共有muiltChannalMat.channals個元素
//通道整合函數,參數意義同split
void cv::merge(std::vector<cv::Mat> channalsMaps, cv::Mat &muiltChannalMat);