Opencv Mat矩阵操作
1.生成矩阵:
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.0;
ones函数很像MATLAB里的语句。这句的意思是先定义一个3×3的32bit浮点数矩阵,元素全为1,所有元素再乘以5.0。
Mat D33 =Mat::zeros(3, 3, CV32F) + 5.0;
和上面类似,先定义个3×3的32bit浮点数矩阵,元素全为0,再将所有元素加上5.0;
Mat D33 = Mat::eye(4,4,CV_64F);//对角矩阵
double a = CV_PI/3;
Mat A22 = (Mat_(2, 2) << cos(a), -sin(a), sin(a), cos(a));
CV_PI就是圆周率。是创建一个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));
高斯分布的随机数矩阵;第二个参数是均值,第三个参数是标差。
矩阵输出:
cout<< "M = " << endl << " " << M<< endl << endl;
2. 赋值运算
Mat A,C; //仅创建了头部
Mat B(A); //复制构造函数
C=A; //复制运算
**注:**赋值运算符和复制构造函数 (构造函数)只复制头,没有数据。
src.copyTo(dst); //把src矩阵中的数据拷贝到dst
src.convertTo(dst, type, scale, shift);
缩放并转换到另外一种数据类型:dst:目的矩阵 type:需要的输出矩阵类型,或者更明确的,是输出矩阵的深度,如果是负值(常用-1)则输出矩阵和输入矩阵类型相同 scale和shift:缩放参数,也可以写为alpha和beta这个命令也等价于下面的转换公式:
m.row(i), m.col(i);
创建一个矩阵头,指向m矩阵的第i行/列,新的矩阵头所代表的矩阵和m矩阵的第i行/列共享数据。
m.rowRange(Range(i1,i2));
m.colRange(Range(j1,j2));
创建一个矩阵头,指向m矩阵的第i1到i2行或者第j1到j2列
m.diag(i);
创建一个矩阵头,指向m矩阵的对角线,生成的是一个单列矩阵,O(1)复杂度,不拷贝数据。i=0时表示主对角线,i>0表示下半边的对角线,i<0表示上半边的对角线。
m(Range(i1,i2),Range(j1,j2)); //Range(i1,i2)包含i1不包含i2 行列编号从0开始
从矩阵m中的第i1行到第i2行以及第j1列到第j2列所划定的范围提取一个小矩阵。
m(Range::all(),Range(j1,j2)); //Range::all()表示所有行 [j1,j2]列
m.repeat(ny,nx);
把m矩阵贴马赛克,获取一个大矩阵,在y方向上重复ny次,在x方向上重复nx次。
flip(src,dst,dir);
翻转矩阵,dir是翻转方向,0表示沿x轴翻转,1表示沿y轴翻转,-1表示沿x轴和y轴都进行翻转。
3、算术运算
1.加法
I = I1 + I2; //等同add(I1,I2,I);
add(I1,I2,dst,mask,dtype);
scaleAdd(I1,scale,I2,dst); //dst=scale*I1+I2;
2.减法
absdiff(I1,I2,I); //I=|I1-I2|;
A-B;A-s;s-A;-A;
subtract(I1,I2,dst);
3.乘法
I = I.mul(I); //点乘,I.mul(I,3);-->I=3*I.^2
Mat C=A.mul(5/B); //==divide(A,B,C,5);
A * B; //矩阵相乘
A.dot(B); //A和B点乘,然后求所有元素和,
pow(src,double p,dst); //如果p是整数dst(I)=src(I)^p;其他|src(I)|^p
Mat::cross(Mat); //三维向量(或矩阵)的叉乘,A.cross(B)
4.除法
divide(I1,I2,dst,scale,int dtype=-1); //dst=saturate_cast(I1*scale/I2);
A/B; alpha/A; //都是点除
4、其他操作
1.矩阵元素访问
A.rows //矩阵行数
A.cols //矩阵列数
A.row(i) //取第i行 行编号从0开始
A.col(j) //取第i列 列编号从0开始
A.at<type>(i,j) //访问矩阵第(i,j) 注意矩阵A的类型,否则异常或取值出错 如:A为CV_32CF1时,type为int型时,所取值为异常值,不是矩阵值。
2.矩阵变形:
A.reshape(int cn, int rows);
参数cn:新的通道数;如果cn值为0表示变换前后通道数不变
参数rows:新的行数;如果rows值为0表示变换后矩阵的行数不变
该函数会为当前矩阵创建一个新的矩阵头(指针),新的矩阵拥有不同的尺寸或者不同的通道数,其优点在于运算复杂度为O(1),不用复制矩阵数据.正是因为不用复制数据,所以在转变过程中要保证原数据矩阵在数据上的连续性(这里的连续性是相对于原矩阵来说)。
注:A要为单独矩阵,不能直接为矩阵的一部分。
3.转置运算:
A.t()
4.矩阵求逆:
A.inv(type)
Type: CV_LU - 最佳主元选取的高斯消除法
CV_SVD - 奇异值分解法 (SVD)
CV_SVD_SYM - 对正定对称矩阵的 SVD 方法
5.SVD分解:
SVD::compute(A,D,U,V,flag=0);
Flag可以为空即:SVD::compute(A,D,U,V),D为列矩阵,元素为A的特征值,A=U*diag(D)*V ,diag(D)是把D变成对角阵,与matlab相比,U与matlab值相同,正负号不同,V为matlab中V的转置,正负号不同。
6.最大最小值:
不需的值可以设成0,如: minMaxLoc(A, 0, 0, 0, &max_loc); 求A中最大值的位置,max_loc定义为:Point max_loc max_loc.x为横坐标,对应图像的列,max_loc.y为纵坐标,对应图像的行。
7.矩阵合并
hconcat(A,B,dst1) 水平方向合并
vconcat(A,B,dst2) 垂直方向合并
A=[1 ,2; B=[5,6; dst1=[ 1 2 5 6;
3 ,4 ] 7,8] 3 4 7 8 ]
dst2=[1 2;
3 4;
5 6;
7 8]
8.矩阵底部添加、删除元素
A.push_back(B); //B添加在A的底部,B可以是常数
A.pop_back(m); //从A的底部删除m行
注:添加时A,B的类型和行数必须相同,删除时,m不能大于A的行数,否则异常,另外,添加和删除后的矩阵,不能用于算术运算,否则异常。
9.矩阵行列求和
Reduce( inputArry src, outputArry, int dim, int rtype,int dtype=-1);
dim :
矩阵被简化后的维数索引.0意味着矩阵被处理成一行,1意味着矩阵被处理成为一列,-1时维数将根据输出向量的大小自动选择.
rtype :
简化操作的方式,可以有以下几种取值:
CV_REDUCE_SUM-输出是矩阵的所有行/列的和.
CV_REDUCE_AVG-输出是矩阵的所有行/列的平均向量.
CV_REDUCE_MAX-输出是矩阵的所有行/列的最大值.
CV_REDUCE_MIN-输出是矩阵的所有行/列的最小值.