可视化

namespace: visualization

可视化是为了方便观察数据从来带来更直观的理解和感受(对于研究者来说),也会让你做的事情看起来更炫酷(对于作者来说)。

OnePiece对于可视化的需求也不多,主要是用来显示点云,三角网格(支持显示颜色,或者phong模型),还支持显示线段(用来描述Correspondence)和三棱锥(用来显示相机位置)。

可视化的功能主要集中在类Visualizer中,是对Pangolin的需要功能的部分封装。主要成员函数如下:

class Visualizer
{
    public:
    //初始化,name为窗口的名称
    void Initialize(const std::string &name = "OnePiece");
    //添加点云
    void AddPointCloud( const geometry::PointCloud &pcd);
    //添加三角网格
    void AddTriangleMesh(const geometry::TriangleMesh &mesh); 
    //显示一帧,主要用于显示会动态变化的物体,例如显示重建过程
    void ShowOnce();
    //持续显示,期间不能改变可视化中的几何对象
    void Show();
    //添加一组Camera(三棱锥),第一个参数是位姿,第二个参数是相机颜色
    void AddCameraSet(const geometry::SE3List & _camera_poses,
        const geometry::Point3List &_camera_colors);
    //添加一组线段,第一个参数是线的两端,第二个参数是线段的颜色
    void AddLineSet(const geometry::Point3List & _points, 
        const std::vector<std::pair<int, int>> &_index, const geometry::Point3List &_line_colors);
};

目前Visualizer添加了设置视图矩阵的方法SetModelViewMatrix,只需要传入相机外参,这个函数会自动转化当前相机位姿在OpenGL坐标系中的视图矩阵。背后的原理会稍微有点绕,下面会解释一下。

首先,想要理解视图矩阵,模型是怎样渲染的:先将世界坐标系,转化成相机坐标系,再通过相机坐标系下进行透射投影到2D图片,就是我们看到的结果。

将世界坐标系下的点转化成相机坐标系下的点,就是视图矩阵(View Matrix),将三维点投影到二维图片上是投影矩阵(Projection Matrix)。通过SetModelViewMatrix,也就是设定视图矩阵,我们可以实现下面跟踪相机位姿的效果:

下面几点会详细介绍如何根据真实的相机位姿求得OpenGL的视图矩阵:

  • 一个相机位姿为\(T = [R|t]\),那么它的坐标系也是用一样方式进行旋转和平移。首先进行旋转,世界坐标系的\(x, y, z\)向量分别为\((1, 0, 0)^T, (0, 1, 0)^T, (0, 0, 1)^T\),旋转后坐标系变为:\(R\cdot[x, y, z] = R\)。 因此,实际上旋转后的坐标系向量就是\(R\)的列向量。为了得到某个点在该坐标系下的坐标,需要将列向量转化为行向量。因此转化后的坐标系为\(R^T\)。旋转后,我们再对坐标系进行平移,相机位置在这个旋转后的坐标系下的坐标为:\(R^Tt\)。如果坐标系平移\(t\)后,则原先点\(p\)的坐标为\(p - t\)。因此,我们可以得到在相机位姿为\([R|t]\)的时候,其视图矩阵为\([R^T|-R^Tt] = T^{-1}\)

  • OpenGL中,规定\(z\)轴是指向相机的,而\(y\)轴是朝上的。而在SLAM中(一般的相机模型),\(z\)轴是从相机指出去的,因此求得相机坐标系后\(z\)要取反。如果在建模时,相机的\(f_y\)是正的,那么意味着成像是上下颠倒的(\(z\)从相机指出去,\(y\)轴朝下,而\(f_y\)为正,意味着世界坐标系y轴也朝下,\(x\)轴朝右,因此\(x\)轴是正常的)。如果是这种情况,\(y\)轴也需要取反。因此,根据相机位姿求得相机坐标系后,需要对这两个轴进行处理,另外一个轴朝右,可以通过叉乘得到(右手坐标系),这样就得到OpenGL中的相机坐标系的方向向量矩阵\(R_g\)

  • 处理后得到坐标系的方向向量,还需要对坐标系进行平移,方法也就是方向向量组成的矩阵乘上观察位置\(t\)再取负:\(t_g = - R_g t\),则最终视图矩阵为\([R_g|t_g]\)