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 的数据总长是:
∣
∣
d
a
t
a
∣
∣
=
s
i
z
e
o
f
(
d
a
t
a
T
y
p
e
)
∗
∏
m
=
1
d
i
m
s
s
i
z
e
[
m
]
(
b
y
t
e
)
||data||=sizeof(dataType)*\prod_{m=1} ^{dims}size[m]\quad (byte)
∣∣data∣∣=sizeof(dataType)∗m=1∏dimssize[m](byte)
以下给出几种常用的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);