查看原文
其他

从零开始一起学习SLAM | 你好,点云

electech6 计算机视觉life 2019-06-22

点击“计算机视觉life”关注,置顶更快接收消息!
本文阅读时间8分钟

本文提纲

先热热身点云是啥你知道点云优缺点吗?点云库PCL:开发者的福音PCL安装指北炒鸡简单的PCL实践留个作业再走

先热热身

小白:hi,师兄,好久不见
师兄:师妹好,上周单应矩阵作业做了吗?
小白:嗯,做了,这个单应矩阵真的挺有意思的。作业之外,我发现了一个新技能。。。
师兄:什么技能?
小白:我发现很多网上流传的图片都可以用上次我学过的单应矩阵实现,你看这张图,我第一次看到还以为是真的

现在知道这不就是我们上节课讲的单应矩阵的变换吗?
果然我在网上找到了原图

现在我也会用OpenCV里的单应函数做这样的图了~

师兄:哈哈,厉害了,会学以致用了。话说回来,今天有啥想问的?
小白:嗯,最近在看点云相关的,师兄要不给我扫扫盲吧?
师兄:这个我也有些了解,就把我知道的给你说说吧
小白:yeah,师兄最棒!

点云是啥

师兄:那我就当你是个小白吧,从点云的定义说起。点云的英文是“point cloud”,点云的意思和它的名字一样,就是一堆点的集合,看起来像是一坨“云”。
小白:一坨。。。

师兄:哦,一坨不太文雅,应该是像一片“云”一样。上面只是一种形象的说法,其实点云在编程实现的时候是一种数据结构,用来表达多维空间中点的集合,这个多维空间一般对我们来说是三维空间。
小白:哦,原来如此,也就是我们所在的 XYZ 空间咯

师兄:对,因此点云在三维相关的领域内用的很多,比如三维扫描,三维重建、三维感知等方面。
小白:那点云是怎么得到的啊?
师兄:一般是通过三维成像传感器得到的,比如双目相机、三维扫描仪、RGB-D相机等,除了可以用这种特殊的硬件得到,也可以用计算机合成。

小白:嗯嗯,师兄说了这么多,还是没有一个直观的印象,这个点云到底长什么样?
师兄:想要直观一点的话,我直接给你看点云图吧,你看下面就是室外一个街道的点云图,远看能看清房子,树木的轮廓啊

小白:嗯嗯,确实能看出轮廓出来,不过不像是我们平时拍的照片,这个点云好像看着不是很清楚哎!

你知道点云优缺点吗?

师兄:确实是这样的,这也是点云的一个缺点吧
小白:那师兄能否顺便说说,这个点云的表达有啥优缺点呢?
师兄:这个问题问的有水平,额,我想想,我先说说优点吧。

第一个优点就是可以表达物体的空间轮廓和具体位置,比如上面这个图,我们能看出来街道的样子,房屋的形状,距离摄像机的距离也是知道的。

第二个优点我觉得就是,点云本身和视角无关,也就是你可以任意旋转,可以从不同角度和方向观察一个点云,而且不同的点云只要在同一个坐标系下就可以直接融合,还是很方便的。

至于缺点嘛,我们先来放大一下点云看看。如下图所示,如果拉近一直放大最后看到的就是一个个的点。也就是空间中成千上万的点组成了一个点的集合,他们构成了上面的街道房屋等。

小白:这个点云放大了啥都看不出来啊
师兄:对,这个是点云的其中一个缺点,那我想想点云还有哪些缺点哈!

第一个缺点就是点云并不是稠密的表达,一般比较稀疏,你放大了看,会看到一个个孤立的点。它的周围是黑的,也就是没有信息。所以在空间很多位置其实没有点云,这部分的信息是缺失的。

第二个缺点嘛,就是点云的分辨率和离相机的距离有关。你离近了看是看不清是个什么东西的,只能拉的很远才能看个大概。

点云库PCL:开发者的福音

小白:嗯,了解了,好像缺点也没啥影响哈。再问个问题,那这些点云怎么显示和保存啊,感觉点云很多的话,好像很复杂的样子。。。

师兄:的确,比如一张 640 x 480 尺寸的深度图就可以转换为拥有三十万个空间点的点云,大的点云可能有百万千万以上,不过,不用担心,有一个开源的点云库,英文名为Point Cloud Library ,简称PCL ,专门用来进行点云的读写,处理等各种操作。

小白:那太好了,这个PCL是谁做的啊?好用吗?
师兄:这个PCL是由一大批世界范围内的名校和科技公司合作开发的,你看下面是他们的名字

小白:哇塞,好多不认识啊!不过我看到了ETH,也就是苏黎世联邦理工大学,还有TUM,也就是德国慕尼黑工业大学,我看论文经常看到他们学校的,他们都是SLAM领域非常顶级的研究机构呢

师兄:嗯,看来你论文看了不少啊,哈哈。我们继续说PCL,PCL真的挺方便,支持跨平台,可以在Windows,Linux,macOS,iOS,Android上部署,可以说非常全了,而且PCL可以分为很多小的库文件,在非常适合于计算资源有限或者内存有限的应用场景,非常方便移动端开发。

小白:那可以说很通用了哈!对了师兄,那这个PCL都有啥功能?
师兄:功能很丰富了,基本PCL的操作都有,比如读写、滤波、特征估计、表面重建、配准、分割等等,你看这个图就是PCL的功能啦

小白:嗯嗯,那是不是平时我们操作点云,调PCL的函数就行了?
师兄:怎么说呢,其实PCL类似于OpenCV那样,有很多功能模块,每个模块里有封装好的函数,你可以调这些函数进行基本处理,但是有些功能PCL里可能没有,这时候需要自己写一些代码实现。
小白:嗯,师兄,总之就是要多编程实践啦

PCL安装指北

师兄:对,那我们开始实践环节吧,带着你做一些PCL的基础编程实现
小白:太好啦!

师兄:首先,我们先来看看点云库,也就是PCL的安装方法,推荐用Linux环节哈,安装很方便
小白:嗯,我之前听师兄说Linux环境配置比较简单,就开始学Linux了,确实,比Windows方便多啦,就是刚开始用命令行不太习惯,好多命令记不住。。。

师兄:没事的,命令行不用刻意去记,忘记了就去查一下,用多了自然知道了
小白:知道啦!
师兄:PCL在Linux下安装有两种方式,一种是安装编译好的库,一种是从源码编译。你看这个是PCL GitHub上的安装包,前面几种是不同环境下的编译好的库了,最后的source code就是源码安装

小白:那这两种方式有什么不一样的?
师兄:第一种方式比较简单,几行命令就可以搞定,比如如果你用的是Ubuntu 16.04版本的Linux系统的话,只需要打开一个终端,输入

sudo apt-get install libpcl-dev pcl-tools

就可以自动安装啦,优点是安装的版本都是稳定版本(当然也比较旧一点),不需要处理依赖关系,安装非常方便,不过这种安装方式也有一些缺点。一个是一般安装的都不是最新的,二是安装的都是别人已经编译好的通用的二进制库,所以不能根据自己的需求进行调整

小白:只要一行代码确实方便啊!那第二种从源码安装的优缺点呢?
师兄:第二种从源码编译安装也没有麻烦多少,它是把所有的源代码都下载了下来,所以会占用较多的存储空间。但好处很多,比如可以下载最新的版本或者指定的版本,可以根据自己的需求在编译时进行选择,可以查看甚至修改某个函数的源代码等等

小白:感觉都挺好,选择恐惧症了,不知道该选哪个了。。。
师兄:推荐用第二种吧,从源码自己编译安装,更方便一些
小白:好,具体如何操作呢?
师兄:首先打开PCL GitHub的网址:https://github.com/PointCloudLibrary/pcl
然后选择你需要的版本号,如下图所示

如果没什么特殊需要可以不选择,我们打开一个终端,输入下面一行代码

git clone https://github.com/PointCloudLibrary/pcl pcl-trunk

然后PCL 源代码就 clone 到你的电脑上了
下一步就是Ubuntu下安装的老一套了,进入目录,新建一个build文件夹存放编译的文件,然后进入build文件夹,cmake一下,然后make,最后install,这一系列流程就是下面这段代码了

cd pcl-trunk && mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
make -j2
sudo make -j2 install

静静的等待编译结束就好啦!

炒鸡简单的PCL实践

小白:嗯嗯,我照着做一下。。。嗯,编译好啦,我们开始用PCL写代码吧
师兄:那我们来写一个读取RGB-D相机的一帧图像并用PCL转化为点云的代码吧

首先,我们需要定义点云的结构,我们这里使用typedef来定义两个常用的类型

typedef pcl::PointXYZRGB PointT;
typedef pcl::PointCloud<PointT> PointCloud;

这里的 pcl::PointXYZRGB 就是我们定义的空间点,它包括空间坐标XYZ以及颜色信息RGB,此外常用的点的 类型还有
pcl::PointXYZRGBA ( 多了一个alpha通道), pcl::PointXYZ (只有空间位置,不包含颜色信息),pcl::PointXYZHSV (空间位置 + HSV颜色空间表示的颜色)等,总之,我们这里输入的 RGB-D 图像就定义为pcl::PointXYZRGB。

小白:师兄,为什么这里要用typedef 啊?我看好多地方都这样写的
师兄:这样是为了把那么长的一个类型用一个简单的别名 PointT 来代替,不至于代码显得特别冗长

小白:这样啊,懂了,那么 pcl::PointCloud就是点云了吧?它里面可以包含许多类型为PointT的点
师兄:对,我们继续。其实RGB-D图像转换为点云的方式也简单,代码如下

for (int m = 0; m < depth.rows; m++)
    for (int n = 0; n < depth.cols; n++)
    {
        // 获取深度图中(m,n)处的值
        ushort d = depth.ptr<ushort>(m)[n];
        // d 可能没有值,若如此,跳过此点
        if (d == 0)
            continue;
        // d 存在值,则向点云增加一个点
        PointT p;
        // 计算这个点的空间坐标
        p.z = double(d) / camera.scale;
        p.x = (n - camera.cx) * p.z / camera.fx;
        p.y = (m - camera.cy) * p.z / camera.fy;

        // 从rgb图像中获取它的颜色
        p.b = rgb.ptr<uchar>(m)[n * 3];
        p.g = rgb.ptr<uchar>(m)[n * 3 + 1];
        p.r = rgb.ptr<uchar>(m)[n * 3 + 2];

        // 把p加入到点云中
        cloud->points.push_back(p);
    }

之所以能这样写,前提是 RGB 和 depth图已经对齐了,也就是 depth 图中某个位置的深度值在 RGB图中同样的位置处就是它对应的颜色。
小白:中间那个计算点的空间坐标好像就是用《从零开始一起学习SLAM | 相机成像模型》的相机成像模型公式的?

师兄:对,所以这里需要相机内参。然后需要注意的就是OpenCV里 RGB 图像里存储颜色顺序其实是BGR,所以从 RGB中获取颜色时顺序也是 BGR而不是RGB,这里容易错。如果没 问题的话,输出结果如下,可以在终端里用pcl_viewer 查看,用鼠标可以缩放和翻转查看,如果你放大了就发现是一个个的点。下图是离的比较远的结果

小白:嗯,这个看起来挺不错的,那下一步呢?
师兄:下一步就是多个点云融合了,这部分我给你变成了作业的形式,你可以自己去编程练习一下,可以利用前面图像生成点云的代码。
小白:额,好。。师兄猝不及防啊。。。

留个作业再走

题目:点云融合实验。已经给定3帧(不连续)RGB-D相机拍摄的 RGB + depth 图像,以及他们之间的变换矩阵(以第一帧为参考帧),请将上述3帧RGB-D图像分别生成点云并融合出最终的点云输出。
练习目的:熟悉PCL的使用,熟悉RGB-D图像到点云的转换过程,掌握简单的点云融合方法。

如果不出意外,最终点云融合结果如下:

本文内容部分参考:PCL官网、半闲居士博客。

师兄提醒:关注“计算机视觉life”微信公众号,菜单栏回复“点云”,就能下载代码框架和数据啦!


欢迎留言讨论,更多学习视频、文档资料、参考答案等扫描下方二维码进入知识星球「从零开始学习SLAM」,菜单栏点击“知识星球”查看星球介绍,快来和其他小伙伴一起学习交流~


相关推荐

从零开始一起学习SLAM | 为什么要学SLAM?

从零开始一起学习SLAM | 学习SLAM到底需要学什么?

从零开始一起学习SLAM | SLAM有什么用?

从零开始一起学习SLAM | C++新特性要不要学?

从零开始一起学习SLAM | 为什么要用齐次坐标?

从零开始一起学习SLAM | 三维空间刚体的旋转

从零开始一起学习SLAM | 为啥需要李群与李代数?

从零开始一起学习SLAM | 相机成像模型

从零开始一起学习SLAM | 不推公式,如何真正理解对极约束?

从零开始一起学习SLAM | 神奇的单应矩阵

零基础小白,如何入门计算机视觉?

欢迎关注公众号:计算机视觉life,一起探索计算机视觉新世界~

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存