1.eigen库介绍
eigen库是C++中的线性代数库,实现了线性代数以及矩阵分析中所有计算方法。既然已经要学eigen库了,想必,线性代数的基本知识肯定是有的。如果你是做三维重建的,那么这个库必学。
另外,eigen库内部实现的操作基本都是高度优化的,计算顺序可能并不像我们平时计算那样从左到右,一般情况下,我们是不需要去管这个的。
eigen官方库使用官方教程链接: link
2.matrix对象创建
只有一个模板类Matrix,其他的所有数据类型3阶矩阵、2维向量等等都是由这个类模板定义的。如typedef Matrix<int,2,1> Vector2i;typedef Matrix<float,3,3> Matrix3f;
//固定大小的矩阵
Eigen::Matrix<int, 2, 2> mat1;
Eigen::Matrix2i mat2;
cout << "mat1:"<<endl<<mat1 << endl;
cout << "mat2:" << endl<<mat2 << endl;
//mat不支持构造时初始化,vec是可以构造时初始化
mat2 << 1, 2,
3, 4;
cout << "输入值后,mat2:" << endl << mat2 << endl;
Eigen::Vector2f vec2f(99.9, 88.8);
cout << "输入值后,vec2f:" << endl << vec2f << endl;
//如果支持C++11特性,则mat和vec都可以用列表初始化
Eigen::MatrixXi dymat{{1, 2, 3}, { 4,5,6 }};
cout << "dymat:" << endl << dymat << endl;
运行结果:
矩阵元素的访问
#include <iostream>
#include <Eigen/Dense>
int main()
{
Eigen::MatrixXd m(2,2);
m(0,0) = 3;
m(1,0) = 2.5;
m(0,1) = -1;
m(1,1) = m(1,0) + m(0,1);
std::cout << "Here is the matrix m:\n" << m << std::endl;
Eigen::VectorXd v(2);
v(0) = 4;
v(1) = v(0) - 1;
std::cout << "Here is the vector v:\n" << v << std::endl;
}
动态矩阵–即矩阵维度是不确定的
用Eigen::MatrixXi来定义(其实是Eigen::Matrix<int,dynamic,dynamic>)。动态矩阵时可以改变大小的resize函数。
//如果支持C++11特性,则mat和vec都可以用列表初始化
Eigen::MatrixXi dymat{{1, 2, 3}, { 4,5,6 }};
cout << "dymat:" << endl << dymat << endl;
cout << "行数:"<<dymat.rows() << endl;
dymat.resize(3, 2);
cout << "dymat:" << endl << dymat << endl;
cout << "行数:" << dymat.rows() << endl;
注意:Eigen中矩阵的元素存储默认是列主序,即dymat在内存中是 1,4,2,5,3,6
可能有人会问,什么时候用动态矩阵。什么时候用固定矩阵?
这个没有必然的答案,只有更合适。比如,在你知道矩阵大小,且维度小于16的情况下,使用固定矩阵对于性能计算会更好。反之则使用动态矩阵。另外,动态矩阵是在堆上申请的,固定矩阵是在栈上申请的。
3.矩阵四则运算
eigen库通过运算符重载来实现类似于普通常数的四则运算,当然也有一些函数方法。注意: Eigen库不支持数据类型的自动提升,所以执行四则运算的矩阵必须是相同数据类型,可以使用cast()成员函数进行数据类型强转。
加减
Eigen::Matrix2d a;
a << 1, 2,
3, 4;
Eigen::MatrixXd b(2, 2);
b << 2, 3,
1, 4;
std::cout << "a + b =\n" << a + b << std::endl;
std::cout << "a - b =\n" << a - b << std::endl;
std::cout << "Doing a += b;" << std::endl;
a += b;
std::cout << "Now a =\n" << a << std::endl;
Eigen::Vector3d v(1, 2, 3);
Eigen::Vector3d w(1, 0, 0);
std::cout << "-v + w - v =\n" << -v + w - v << std::endl;
标量乘除
Eigen::Matrix2d a{{1, 2}, { 3, 4 }};
cout << a << endl;
cout << "************" << endl;
auto a1 = a * 2;
auto a2 = a / 2;
cout << a1 << endl;
cout << "************" << endl;
cout << a2 << endl;
矩阵乘除
Eigen::Matrix2d a{{1, 2}, { 3, 4 }};
Eigen::Matrix2d b = a.transpose();
cout << "a:" << endl << a << endl;
cout << "b:" << endl << b << endl;
cout << "a*b:" << endl << a * b << endl;
Eigen::Vector2d c(1, 1);
cout << "a * b * c" << endl << a * b * c << endl;
内积和外积–dot、cross
Eigen::Vector3d a(1, 1,1);
Eigen::Vector3d b(3, 4,5);
cout << "内积:" << a.dot(b) << endl;
cout << "内积另一种计算:" << a.transpose()*b << endl;
cout << "叉乘:" <<endl<< a.cross(b) << endl;
4. Array类实现逐元素的操作。类似于numpy中的广播机制。
只要记住是逐元素操作就行,其它的所有操作都跟Matrix差不多。
Eigen::Array<int, 2, 3> a{{-1, -2, -1}, { 6,6,6 }};
Eigen::Array<int, 2, 3> b{{2, 2, 2}, { 2,3,2 }};
cout << "a:" << endl << a << endl;
cout << "b:" << endl << b << endl;
cout << "a+1:" << endl << a + 1 << endl;
cout << "a+b:" << endl << a + b << endl;
cout << "a*b:" << endl << a * b << endl;
cout << "a的绝对值:" << a.abs() << endl;
cout << "b的平方根:" << b.sqrt() << endl;
cout << "返回a和b中对应元素位置最大的元素组成的array:" << endl << a.max(b) << endl;
5.矩阵块操作–block、()重载
Eigen::MatrixXf m(4, 4);
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
//block方法返回值做右值
auto t = m.block<2, 2>(1, 1);
cout << t << endl;
//左值
m.block(1, 1, 2, 2) = Eigen::Array22f::Constant(0.5);
cout << "m:\n" << m << endl;
注意: 块操作结果是可以作为左值的,即block函数返回的是引用。
取块的其他操作:
Eigen::MatrixXf m(4, 4);
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
//block方法返回值做右值
auto t = m.block<2, 2>(1, 1);
cout << t << endl;
//左值
m.block(1, 1, 2, 2) = Eigen::Array22f::Constant(0.5);
cout << "m:\n" << m << endl << endl;
//取第2行
cout << "第2行:" << m.row(1) << endl;
//左上角2*3,并+1
m.topLeftCorner(2, 3) = m.topLeftCorner(2, 3).array() + 1;
cout << endl << "左上角+1后:\n" << m << endl;
6.切片-seq,seqN,lastN,all,其它数组
切片和块操作差不多,都支持左值和右值计算。不同的是,切片更灵活,切片得到的矩阵布局跟切片时输入的行列顺序一样。
Eigen::MatrixXf m(4, 4);
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
//matrix的切片
//用seq()生成数组
cout << m(Eigen::seq(1, 2),Eigen::seq(0,1) ) << endl;
cout << "********" << endl;
//用seqN()生成间隔数组
cout << m(Eigen::seqN(0, 2, 2), Eigen::last) << endl;
cout << "********" << endl;
//用一般的可遍历容器生成数组
int col[2] = { 0,1 };
cout << m(vector<int>{1, 2, 3}, col) << endl;
cout << "********" << endl;
//用lastN生成后面的数组
cout << m(Eigen::lastN(2), Eigen::all) << endl;
cout << "********" << endl;
cout <<"m:\n"<< m << endl;
cout << "********" << endl;
//逆序切片
cout << m(Eigen::seq(Eigen::last, 1, -1), vector<int>{1, 2}) << endl;
//任意行列切片
cout << "********" << endl;
Eigen::Array3i ainx{1, 3, 2};
cout << m(vector<int>{2, 1, 2}, ainx) << endl;
7.初始化的其它方法–用matrix初始化
//用matrix来初始化,实现堆叠
MatrixXi mat1(2, 2);
mat1 << 1, 2, 3, 4;
MatrixXi mat2(2, 3);
mat2 << 1, 1, 1, 2, 2, 2;
cout << "mat1:\n" << mat1 << endl;
cout << "mat2:\n" << mat2 << endl;
MatrixXi mat3(2, 5);
mat3 << mat1, mat2;
cout << "mat3:\n" << mat3 << endl;
MatrixXi mat4(5, 5);
mat4.row(0) << mat3.row(1);
mat4.row(1).head(4) << 4, 4, 4, 4;
mat4.row(1).tail(1) << 100;
mat4.block(2, 0, 3, 5) << mat3, mat3.row(0);
cout << "mat4:\n" << mat4 << endl;
8.常见的一些基本矩阵幅值
零矩阵、单位阵、常矩阵、随机矩阵
//零矩阵
MatrixXi mat1 = MatrixXi::Zero(2, 3);
cout << "mat1:\n" << mat1 << endl;
Matrix3i mat2 = Matrix3i::Zero();
cout << "mat2:\n" << mat2 << endl;
//常数矩阵
MatrixXf mat3 = MatrixXf::Constant(2, 3, 6.6);
cout << "mat3:\n" << mat3 << endl;
//随机矩阵
RowVectorXf vec1 = RowVector3f::Random();//-1~1随机浮点数
cout << "vec1:\n" << vec1 << endl;
//单位阵
MatrixXi mat4 = Matrix3i::Identity();
cout << "mat4:\n" << mat4 << endl;
分块的常见矩阵赋值or原地赋值操作
const int size = 6;
MatrixXd mat1(size, size);
mat1.topLeftCorner(size / 2, size / 2) = MatrixXd::Zero(size / 2, size / 2);
mat1.topRightCorner(size / 2, size / 2) = MatrixXd::Identity(size / 2, size / 2);
mat1.bottomLeftCorner(size / 2, size / 2) = MatrixXd::Identity(size / 2, size / 2);
mat1.bottomRightCorner(size / 2, size / 2) = MatrixXd::Zero(size / 2, size / 2);
std::cout << mat1 << std::endl << std::endl;
MatrixXd mat2(size, size);
mat2.topLeftCorner(size / 2, size / 2).setZero();
mat2.topRightCorner(size / 2, size / 2).setIdentity();
mat2.bottomLeftCorner(size / 2, size / 2).setIdentity();
mat2.bottomRightCorner(size / 2, size / 2).setZero();
std::cout << mat2 << std::endl << std::endl;
MatrixXd mat3(size, size);
mat3 << MatrixXd::Zero(size / 2, size / 2), MatrixXd::Identity(size / 2, size / 2),
MatrixXd::Identity(size / 2, size / 2), MatrixXd::Zero(size / 2, size / 2);
std::cout << mat3 << std::endl;