0%

Matrix类

Eigen中所有矩阵和向量都是属于Matrix模板类的对象,Vectors是特殊的矩阵(一行或一列)

Matrix前三个模板参数

虽然Matrix一共有六个模板参数,但目前暂时学前三个就够了,剩下三个参数有默认值,现在还用不到。Matrix的三个必需模板参数如下:

1
Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
  • Scalar :矩阵参数的变量类型,如 int ,float ,double

  • RowsAtCompileTimeColsAtCompileTime :矩阵的行数和列数

    • 特殊值 Dynamic :表示矩阵维度未知

      例如:MatrixXd 表示一个double类型的未知行列的矩阵

      1
      typedef Matrix<double, Dynamic, Dynamic> MatrixXd;

      类似地,VectorXi 表示一个int类型的未知维度的向量

      1
      typedef Matrix<int, Dynamic, 1> VectorXi;
  • Eigen中还提供了一些typedefs覆盖一些常用类型

    • MatrixNt :即 Matrix<type, N, N>,例如 Matrix4f 表示4x4的float矩阵
    • VectorNt :即 Matrix<type, N, 1>,例如 Vector4f 表示4维的float列向量
    • RowVectorNt :即 Matrix<type, 1, N>,例如 RowVector3d 表示3维的double行向量
    • 以上 N 可以是2,3,4或X(表示Dynamic),t 可以是 i (int), f (float), d (double), cd (complex\), cd (complex\)等。

向量类型

列向量(最常用),列参数固定为1,例如 Vector3f 表示3维float列向量

1
typedef Matrix<float, 3, 1> Vector3f;

行向量,行参数固定为1,例如 RowVector2i 表示2维int行向量

1
typedef Matrix<int, 1, 2> RowVector2i;

构造函数

默认构造函数

不执行动态内存分配,不初始化矩阵所有系数

1
2
Matrix3f a;
MatrixXf b;
  • a是一个3x3矩阵,有一个未初始化的float[9]系数数组
  • b是一个动态大小的矩阵,当前大小为0x0,系数数组内存空间尚未分配

含维度的构造函数

  1. 矩阵类构造函数第一个参数为行数,第二个参数为列数:
1
MatrixXf a(10,15);
  • a是一个10x15的动态大小矩阵,内存空间已分配,矩阵系数未初始化
  1. 向量类构造函数传入向量维数即可:
1
VectorXf b(30);
  • b是一个维度为30的动态大小向量,内存空间已分配,向量系数未初始化
  1. 在固定大小的矩阵上使用该构造函数是合法的,这是为了给固定大小和动态大小的矩阵提供相同的API接口,虽然这种行为并不会执行什么操作:
1
Matrix3f a(3,3);

固定维度向量(<4)允许直接初始化系数

1
2
3
Vector2d a(5.0, 6.0);
Vector3d b(5.0, 6.0, 7.0);
Vector4d c(5.0, 6.0, 7.0, 8.0);

访问系数

用括号访问/修改系数,矩阵先行后列,向量仅传下标即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <eigen3/Eigen/Eigen>

using namespace std;

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);
cout << "Here is the matrix m:\n"
<< m << endl;
Eigen::Vector2d v(2);
v(0) = 4;
v(1) = v(0) - 1;
cout << "Here is the vector v:\n"
<< v << endl;
cout << "Here is matrix m(1):\n"
<< m(1) << endl;
cout << "Here is matrix m(3):\n"
<< m(3) << endl;
return 0;
}

输出:

1
2
3
4
5
6
7
8
9
10
Here is the matrix m:
3 -1
2.5 1.5
Here is the vector v:
4
3
Here is matrix m(1):
2.5
Here is matrix m(3):
1.5
  • 语法 m(index) 不仅适用于向量,对一般矩阵都适用,表示矩阵m在内存中存储的第index个元素,默认存储顺序为列优先,当然也可以修改为行有限,见 Storage Orders

  • 操作符[]对向量重载,因为C++中[]只能容纳一个参数,所以只能向量使用,且 matrix[i,j] 将会被编译成 matrix[j] 并报错:

    1
    error: static assertion failed: THE_BRACKET_OPERATOR_IS_ONLY_FOR_VECTORS__USE_THE_PARENTHESIS_OPERATOR_INSTEAD

逗号初始化

1
2
3
4
5
m << 1, 2, 3,
4, 5, 6,
7, 8, 9;
cout << "m:\n"
<< m << endl;

输出:

1
2
3
4
m:
1 2 3
4 5 6
7 8 9

更多介绍见 Advanced Initialization

大小调整

  1. 调用 rows() cols() size() 方法获取矩阵行数、列数和系数个数

  2. 动态大小矩阵调用 resize() 方法调整大小。如果矩阵大小不发生变化,该方法不进行任何操作;否则它是破坏性的,矩阵系数可能被改变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <eigen3/Eigen/Eigen>

using namespace std;

int main()
{
Eigen::MatrixXd m(3, 3);
m << 1, 2, 3,
4, 5, 6,
7, 8, 9;
cout << "The matrix m is of size "
<< m.rows() << "x" << m.cols()
<< ", with " << m.size() << " cofficients." << endl;
cout << "m:\n" << m << endl;

m.resize(4, 4);
cout << "After resize(4,4), the matrix m is of size "
<< m.rows() << "x" << m.cols()
<< ", with " << m.size() << " cofficients." << endl;
cout << "m:\n" << m << endl;

return 0;
}

输出:

1
2
3
4
5
6
7
8
9
10
11
The matrix m is of size 3x3, with 9 cofficients.
m:
1 2 3
4 5 6
7 8 9
After resize(4,4), the matrix m is of size 4x4, with 16 cofficients.
m:
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
  1. 如果想保持矩阵原有系数不变,调用 conservativeResize() 方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <eigen3/Eigen/Eigen>

using namespace std;

int main()
{
Eigen::MatrixXd m(3, 3);
m << 1, 2, 3,
4, 5, 6,
7, 8, 9;
cout << "The matrix m is of size "
<< m.rows() << "x" << m.cols()
<< ", with " << m.size() << " cofficients." << endl;
cout << "m:\n"
<< m << endl;

m.conservativeResize(4, 4);
cout << "After conservativeResize(4,4), the matrix m is of size "
<< m.rows() << "x" << m.cols()
<< ", with " << m.size() << " cofficients." << endl;
cout << "m:\n"
<< m << endl;

m.conservativeResize(2, 2);
cout << "After conservativeResize(2,2), the matrix m is of size "
<< m.rows() << "x" << m.cols()
<< ", with " << m.size() << " cofficients." << endl;
cout << "m:\n"
<< m << endl;

return 0;
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
The matrix m is of size 3x3, with 9 cofficients.
m:
1 2 3
4 5 6
7 8 9
After conservativeResize(4,4), the matrix m is of size 4x4, with 16 cofficients.
m:
1 2 3 0
4 5 6 0
7 8 9 0
0 0 0 0
After conservativeResize(2,2), the matrix m is of size 2x2, with 4 cofficients.
m:
1 2
4 5
  1. 为了保证API的统一性,上述改变矩阵大小的方法也可以用于固定大小的矩阵类。resize到不同大小会触发 assertion 错误;resize到原本大小不会报错,也不会执行任何操作。

整体赋值

操作符 = 左边的矩阵会被自动resize到右边矩阵的大小,如果不希望这种自动resize发生,也可以将其禁用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <eigen3/Eigen/Eigen>

using namespace std;

int main()
{
Eigen::MatrixXd m(3, 3);
m << 1, 2, 3,
4, 5, 6,
7, 8, 9;
cout << "The matrix m is of size "
<< m.rows() << "x" << m.cols()
<< ", with " << m.size() << " cofficients." << endl;
cout << "m:\n"
<< m << endl;

Eigen::MatrixXd n(2, 2);
cout << "The matrix n is of size "
<< n.rows() << "x" << n.cols()
<< ", with " << n.size() << " cofficients." << endl;
cout << "n:\n"
<< n << endl;

n = m;
cout << "After assignment, the matrix n is of size "
<< n.rows() << "x" << n.cols()
<< ", with " << n.size() << " cofficients." << endl;
cout << "n:\n"
<< n << endl;

return 0;
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
The matrix m is of size 3x3, with 9 cofficients.
m:
1 2 3
4 5 6
7 8 9
The matrix n is of size 2x2, with 4 cofficients.
n:
0 0
0 0
After assignment, the matrix n is of size 3x3, with 9 cofficients.
n:
1 2 3
4 5 6
7 8 9

如果左侧为固定大小矩阵,编译不会报错。但如果大小与右侧不同,执行时会报错;相同则不会。

固定大小 vs. 动态大小

固定大小:适用于小矩阵(尤其是个数≤16的矩阵)。固定大小避免了动态内存分配并展开循环,有利于提高性能。实际上一个固定大小的Eigen矩阵只是一个简单的数组,即:执行 Matrix4f m 实际上就是在做 float m[16] 这件事,所以几乎不花时间。但需要注意的是,Eigen默认将数组自动分配为局部变量,这个操作在堆栈(stack)上完成,因此矩阵太大可能会导致堆栈溢出,并且矩阵太大(≥32)时,使用固定大小的性能优势就变得可以忽略不计了。

动态大小:适用于大矩阵或不清楚具体维度的情况。与固定大小矩阵不同,它始终分配在堆(heap)上,即 MatrixXf m(rows, cols) 相当与执行 float *m = new float[rows*cols] ;除此之外,MatrixXf 对象还需要将行数、列数存储为成员变量。

Matrix其他模板参数

完整的 Matrix 模板类如下:

1
2
3
4
5
6
Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompilTime,
int MaxColsAtCompileTime = ColsAtCompilTime>
  • Options0 表示按列优先存储;RowMajor 表示按行优先存储。例如,一个3x3行优先矩阵:

    1
    Matrix<float, 3, 3, RowMajor> m;
  • MaxRowsAtCompileTime MaxColsAtCompileTime :不知道矩阵具体大小,但知道维度上限的时候很有用,这么做的最大原因是为了避免动态内存分配。

    1
    Matrix<float, Dynamic, Dynamic, 0, 3, 4> m

    这句话定义的矩阵仅仅使用了一个大小为12的float数组。