Eigen 学习笔记
Eigen是一个C++开源库,支持线性代数,矩阵运算,数值分析,矢量计算等一系列算法,方便使用者实现一些复杂的运算
Eigen 头文件说明如下:
部分函数用法:
MatrixXd : 定义一个二维矩阵,行列未知,初始化时需指定数组的行和列(double型)
Matrix2cf:定义2x2的方阵(复数,float型)
Matrix2f :定义2x2的方阵(float型)
Matrix2i: 定义2x2的方阵(int型)
Matrix2Xf : 定义2行N列的矩阵(float型,N值不确定)
数字代表nn方阵的大小,‘X’代表这个矩阵不是方阵,是一个mn的矩阵,‘d’代表double,‘f’代表float, ‘i’代表整数,‘c’代表complex,即复数;
简单用法:
1、数据初始化与访问
MatrixXf tmp = MatrixXf::Random(3,4); //定义3x4的float型的随机数矩阵
cout<<"tmp:\n"<<tmp<<endl;
Matrix4f result = Matrix4f(); //定义一个4*4的 float 的方阵
result(0,1)=1; //单个元素访问赋值
cout<<"result\n"<<result<<endl;
Matrix2Xd res= Matrix2Xd(2,3); //初始化一个2行n列的数组,第一个参数必须是2,否则报错
cout<<"res\n"<<res<<endl<<" res.szie:"<<res.size()<<" " <<"res.row\\col:"<<res.rows()<<","<<res.cols()<<endl;
//res.size() 返回的是总的元素个数,不是矩阵的shape (获取行列用res.rows(),res.cols())
MatrixXd m = MatrixXd::Constant(7, 7, 0); //初始化一个矩阵7*7 初始值全为0
cout << "EigenMat:" << endl << m << endl;
m(1,1) =1; //直接访问修改
m(2,2) = m(0,0)+m(0,1); //矩阵元素做加法
cout << "EigenModify:" << endl << m << endl;
MatrixXd m1 = MatrixXd::Random(3, 3); //初始化3*3 大小的随机矩阵,范围都在(-1,1)之间
m1 = (m1 + MatrixXd::Constant(3, 3, 2)) * 50; //将m1每个元素加上常数2再乘50,随机数会变为(50,150)之间
2、元素级操作(取绝对值,开方),将Matrix矩阵与常数比较
float th =0.2; //比较阈值
MatrixXf p1= MatrixXf::Constant(3, 4, 0.5); //定义一个3*4的常量数组0.5
cout<<"tmparr: \n"<<tmp.array()<<endl<<"sqrt(temp):\n"<<sqrt(abs(tmp.array()))<<endl;//元素级别操作需要转换到array ,这里的tmp 上面已经定义过
cout<<p1.array().min(tmp.array().abs().sqrt())<<endl; //返回p1和tmp.abs().sqrt()两个数组中对应元素较小的那个值(这里即将所有元素和0.5进行比较,取小的值)
MatrixXf p2 = p1.array().min(tmp.array().abs().sqrt()).matrix(); //将得到的比较结果转换回MatrixXf类型
cout<<"p2 -MatrixXf\n"<<p2<<endl;
auto mapp = tmp.array()>th; //将整个矩阵和某个阈值比较,满足条件,返回1,否者返回0
cout<<"mapp:\n"<<mapp<<endl;
3、tensor 定义及操作,Matrix转换成Tensor
Eigen::Tensor<float,2> pd1(3,4); //定义一个3*4的tensor ,2代表维度
pd1(0,2) = 3;
pd1(1,2) = 4;
auto cmp = pd1>2; //auto自动接收类型 找出pd1中值大于2的元素,其余值赋0 ,tensor可以直接和常数比较,Matrix 不行,需要先转成array
cout<<"pd1: \n"<<pd1<<endl;
Eigen::Tensor<float, 2> cmp2 = cmp.cast<float>(); //有文档说需要实例化变量类型转换一下,否则会编译报错(不过我这里直接输出cmp也是可以的)
cout<<"cmp: \n"<<cmp<<endl; //直接输出比较矩阵
cout<<"cmp2: \n"<<cmp2<<endl; //将auto 转换为 Tensor<float,2> 2维张量输出
Matrix矩阵转换成tensor (TensorMap)
TensorMap<Tensor<float,2>> MatrixXtoTensor(tmp.data(),3,4);//将MatrixXf 转换为3*4的二维tensor
cout<<"MatrixXtoTensor :\n"<<MatrixXtoTensor<<endl;
4、向量初始化与操作,向量转换成tensor 或 Matrix
VectorXd v(3);//初始化一条向量(长度为3)
v<<1,2,3;
cout<<"v:\n"<<v<<endl;
Vector3f v1(5.5,4.3,6.7);//定义三行1列的向量,数据个数要和变量类型对应上
cout<<"v1= "<<v1<<endl;
auto eigenTensorMap =TensorMap<Tensor<float, 2, Eigen::RowMajor>> (v1.data(), 2,2); //将vector v1 转换成2*2的Tensor 行优先,不足的元素,会自动填充
cout<<"eigenTensorMap RowMajor: \n"<<eigenTensorMap<<endl;
auto eigenTensorMap1 =TensorMap<Tensor<float, 2, Eigen::ColMajor>> (v1.data(), 2,2);//将vector v1 转换成2*2的Tensor 列优先 ,不足的元素,会自动填充
cout<<"eigenTensorMap ColMajor: \n"<<eigenTensorMap1<<endl;
auto eigenMap = Eigen::Map<MatrixX<float>> (v1.data(), 3,3); //将vector v1转换为MatrixX类型 3*3的
cout<<"eigenMap :\n"<<eigenMap<<endl;
5、Opencv::Mat 与 Eigen::MatrixX 互相转换 ,Matrix数据块操作
注意:头文件顺序不能写反,否则编译失败
#include <Eigen/Dense>
#include <opencv2/core/eigen.hpp>
cv::Mat ImageMat, OutPutMat;
ImageMat = cv::imread("D:/pic/test.png", 0);
cv::imshow("原图", ImageMat);
cv::waitKey(0);
Mat Min=Mat(round(ImageMat.rows*0.1),round(ImageMat.cols*0.1),CV_16S); //定义缩小后的图片大小 short型16bit
cv::Size dsize = cv::Size(Min.rows,Min.cols); // cv::Size 定义
cv::resize(ImageMat,Min,dsize,0.1,0.1); //resize ImageMat大小变为1/10,行、列分别缩小1/10
imshow("min",Min);
cv::waitKey(0);
Eigen::MatrixXd Matrixs(Min.rows, Min.cols);
cv2eigen(Min, Matrixs); //将缩小后的图像Min 转换为eigen::MatrixXd
cout<<"cv2eigen:"<<endl<<Min<<endl; //如下所示为行列各缩小为1/10 后的图像数据
MatrixXd mmm = MatrixXd::Constant(30,30,0); //定义30*30的0矩阵
mmm(1,1)=1;
mmm(1,2)=1;
mmm(2,1)=1;
mmm(2,2)=1 ;
mmm.block(3,3,10,10) = MatrixXd::Constant(10,10,1); //块操作 以(3,3)为起点(左上角点),10x10块区域内赋值常数1
mmm.block<4,4>(15,15) = MatrixXd::Constant(4,4,1); //和上面一样也是块操作,另一种表达方式,以(15,15)为起点,4x4区域内赋值常数1
cv::Mat cvm = Mat(30, 30, CV_8U); //定义Mat大小,接收数据
eigen2cv(mmm, cvm);
imshow("cvm",cvm);
waitKey(0);
cout << "cvMat:" << endl << cvm << endl;
如下图所示在30*30区域内显示为白色的,就是修改为1的部分。
6、Matrix 行列单独操作, 矩阵边角矩阵获取(左上,右下,前N行,后N列)
MatrixXd tt = MatrixXd::Constant(4,4,1);
tt.row(1)+= tt.row(2);
cout<<"行列操作:\n"<<tt<<endl;
//边角子矩阵提取 左上角:topLeftCorner(p,q) 左下角:bottomLeftCorner(p,q) 右上角:topRightCorner(p,q) 右下角:topRightCorner(p,q)
//前p行:topRows(p) 后p行:bottomRows(p) 前q列:leftCols(q) 后q列:leftCols(q)
cout<<"tt左上(2,2):\n"<<tt.topLeftCorner(2,2)<<"\ntt前两行:\n"<<tt.topRows(2)<<endl;
7、切片操作,向量vector和Matrix矩阵切片都是通过 Map 实现 (Eigen默认采用列主导(column major)的数据存储形式)
RowVectorXf p = RowVectorXf::LinSpaced(20,0,19); //行向量 长度20,范围[0,19]
cout << "Input:" << endl << p << endl;
Map<RowVectorXf,0,InnerStride<2> > v2(p.data(), p.size()/2); //步长为2进行获取 v.data() 长度为v.size/2
//参数0代表数据对其格式,参数InnerStride代表沿着数据存储的方向间隔2步长取值
cout << "Even:" << v2 << endl;
MatrixXf M1 = MatrixXf::Random(3,8); //默认存储方式列优先
//innerStride既表示沿着矩阵的数据存储方向移动一个元素的位置,在内存中需要移动的次数。outerStride的含义就是不沿着数据存储方向移动一个位置,在内存中需要移动的次数
cout << "Column major input:" << endl << M1 << "\n";
cout<<"innerStride="<<M1.innerStride()<<endl;
cout<<"outerstride=" <<M1.outerStride()<<endl;
Map<MatrixXf,0,OuterStride<> > M2(M1.data(), M1.rows(), (M1.cols()+2)/3,OuterStride<>(M1.outerStride() * 3));
//代表 将M1矩阵的元素映射到M2,行不变,列为间隔3列取,在M1上取元素需要在行方向上一次移动步长为原来的3倍(因为是列优先存储的,间隔9个才取下一列)
cout << "间隔3列切片 M2" << endl << M2 << endl;