0%

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数组。

安装
1
sudo apt-get install libeigen3-dev
版本查看
1
tac /usr/include/eigen3/Eigen/src/Core/util/Macros.h

tac: 文本从后往前输出 —> 版本为3.3.4

output

使用

CMakeLists.txt中添加

1
2
find_package(Eigen3 REQUIRED)
include_directories(${EIGEN3_INCLUDE_DIR})

头文件中包含

1
#include <Eigen/Eigen> 

如果出现找不到FineEigen.cmake错误,在系统中找到FindEigen.cmake并复制到项目文件夹下,修改CMakeLists.txt为:

1
2
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${EIGEN3_INCLUDE_DIRS})

Zheng T, Zhang G, Han L, et al. Building Fusion: Semantic-aware Structural Building-scale 3D Reconstruction [J]. IEEE Transactions on Pattern Analysis and Machine Intelligence, 2020

清华-伯克利深圳学院,清华大学

针对什么问题
  1. 如何实现在线建筑规模协同三维重建,为室内场景提供详细的几何、语义和结构化信息

  2. 如何解决场景中相似房间导致的回环检测出错

采用什么方法

提出了一个中心化架构的语义感知结构化建筑规模的三维重建系统Building Fusion:

  1. 采用分层场景结构实现全局连续的在线重建

    • 协同进行密集重建,每个agent的重建都采用基于关键帧的重建方法(组合时间连续帧)

    • 采用子图策略组合空间连续帧,该方法提高了位姿图优化和深度融合的效率

  2. 采用[8]提出的三维语义-实例分割网络实现在线语义和实例预测,用来检测房间

  3. 提出的房间规模的语义感知结构化回环检测方法 (LCD) 实现了对不同agent间匹配关系的寻找,从而形成全局连续的地图

    • 输入实例分割标签,预测实例级embedding,该网络对物体形状信息进行编码

    • 基于第一步求出的实例形状embedding计算当前房间和数据库中所有房间的相似度

    • 采用图匹配方法和ICP进行几何验证,检查这些房间是否能很好地对齐,并剔除误判。该方法受[45]所提出的基于物体的图匹配方法的启发,并将其扩展到大规模场景

达到什么效果
  1. 和基于实例分割的其他方法比Recall@N(语义标签直方图和PointNet[23])

    Recall@N comparisons

    • PointNet泛化能力差,无法生成好的形状embedding
    • Semantic Label Histogram在Recall@N上表现较好,但不能区分来自相同种类的不同物体
    • 本文提出的方法成功将相似几何外形的物体进行分类
  2. 和不用实例分割的方法相比(PointNetVLAD和FPFH)

  3. 和二维回环检测相比(BoBW)

    Table1

    • BoBW表现不好的可能原因:1)BoBW所用数据集太大了,从而降低了命中率 2) BoBW不擅长从新的位姿进行定位(not good at relocalizing from novel poses)
  4. 建筑规模重建(和Golodetz[4]相比,后者不能得到理想的结果)

  5. 消融实验表明:

    • Room-level LCD:通过房间级的几何与语义信息进行对齐,不再受二维相似图像影响
    • Submap-based reconstruction:大大降低了系统处理所需的时间和内存
    • Submap reintegration:实现无缝重建,重建结果更加平滑
存在什么不足
  1. 几何重建方面
    • 视觉跟踪器(visual tracker)导致的漂移误差(drift error),缺乏足够视觉特征时会跟丢(tracking loss) <= 增加惯性传感器提高跟踪准确率和鲁棒性
    • 对齐房间的时候ICP无法稳定生成最佳结果 <= 用学习的三维特征代替[60]
    • 重建结果可能会因为扫描密度不够而不完整 <= 使用数据驱动的方法[61], [62]
  2. 对场景的语义和结构化理解方面
    • 在训练集中未出现的物体/不完整重建的物体,分割结果较差 <= 根据场景微调模型或采用更好的数据增强方法或采用当前更好的实例/语义分割方法
    • 当前只建模了房间,未来可以增加其他结构类型
    • 当前只考虑了静态场景,还没有考虑动态场景

[4] S. Golodetz, T. Cavallari, N. A. Lord, V. A. Prisacariu, D. W. Murray, and P. H. Torr, “Collaborative large-scale dense 3d reconstruction with online inter-agent pose optimisation,” IEEE transactions on visualization and computer graphics, vol. 24, no. 11, pp. 2895–2905, 2018.

  • 对比文献,建筑规模重建

[8] L. Han, T. Zheng, L. Xu, and L. Fang, “Occuseg: Occupancy-aware 3d instance segmentation,” arXiv preprint arXiv:2003.06537, 2020.

  • 参考工作,实现房间检测的三维语义-实例分割网络

[23] C. R. Qi, H. Su, K. Mo, and L. J. Guibas, “Pointnet: Deep learning on point sets for 3d classification and segmentation,” in Proceedings of the IEEE conference on computer vision and pattern recognition, 2017, pp. 652–660.

  • 对比文献,PointNet,基于实例分割,泛化能力差

[45] R. Finman, L. Paull, and J. J. Leonard, “Toward object-based place recognition in dense rgb-d maps,” in ICRA Workshop Visual

  • 参考工作,基于物体的图匹配方法,本文将其拓展到大规模场景

[53] A. Dai, A. X. Chang, M. Savva, M. Halber, T. Funkhouser, and M. Nießner, “Scannet: Richly-annotated 3d reconstructions of indoor scenes,” in Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, 2017, pp. 5828–5839.

  • RGBD视频数据集,包括了在707个不同地点的1513段扫描、重建的三维模型和三维语义/实例分割GT标签,广泛应用于三维语义和实例分割

[60] C. Choy, J. Park, and V. Koltun, “Fully convolutional geometric features,” in ICCV, 2019.

  • 未来工作参考,基于学习的三维特征

[61] A. Dai, D. Ritchie, M. Bokeloh, S. Reed, J. Sturm, and M. Nießner, “Scancomplete: Large-scale scene completion and semantic segmentation for 3d scans,” in Proc. Computer Vision and Pattern Recognition (CVPR), IEEE, 2018.

  • 未来工作参考,数据驱动

[62] A. Dai, C. Diller, and M. Nießner, “Sg-nn: Sparse generative neural networks for self-supervised scene completion of rgb-d scans,” in Proc. Computer Vision and Pattern Recognition (CVPR), IEEE, 2020.

  • 未来工作参考,数据驱动