视觉SLAM十四讲学习笔记-第五讲-图像和实践

 专栏系列文章如下:

 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客

视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客

视觉SLAM十四讲学习笔记-第二讲-开发环境搭建_goldqiu的博客-CSDN博客

视觉SLAM十四讲学习笔记-第三讲-旋转矩阵和Eigen库_goldqiu的博客-CSDN博客

视觉SLAM十四讲学习笔记-第三讲-旋转向量、欧拉角、四元数_goldqiu的博客-CSDN博客

视觉SLAM十四讲学习笔记-第三讲-相似、仿射、射影变换和eigen程序、可视化演示_goldqiu的博客-CSDN博客

视觉SLAM十四讲学习笔记-第四讲-李群与李代数基础和定义、指数和对数映射_goldqiu的博客-CSDN博客

视觉SLAM十四讲学习笔记-第四讲-李代数求导与扰动模型_goldqiu的博客-CSDN博客

视觉SLAM十四讲学习笔记-第四讲-Sophus实践、相似变换群与李代数_goldqiu的博客-CSDN博客

视觉SLAM十四讲学习笔记-第五讲-相机模型_goldqiu的博客-CSDN博客

5.2 图像

相机把三维世界中的信息转换成了一张由像素组成的照片,存储在计算机中,作为后续处理的数据来源。在数学中,图像可以用一个矩阵来描述;而在计算机中,它们占据一段连续的磁盘或内存空间,可以用二维数组来表示。

最简单的图像——灰度图:每个像素位置 (x,y) 对应一个灰度值 I,一张宽度为 w、高度为 h 的图像,数学上可以记为一个函数:

其中 (x,y) 是像素的坐标。然而,计算机并不能表达实数空间,所以需要对下标和图像读数在某个范围内进行量化(类似于模拟到数字的概念)。在常见的灰度图中,用 0~255 的整数(一个 unsigned char或1 个字节)来表达图像的灰度读数。那么,一张宽度为 640 像素、高度为 480 像素分辨率的灰度图就可以表示为:

unsigned char image[480][640] //二维数组表达图像

在程序中,图像以二维数组形式存储。它的第一个下标是指数组的行,而第二个下标则是列。在图像中,数组的行数对应图像的高度,而列数对应图像的宽度。

当访问某一个像素时,需要指明它所处的坐标。像素坐标系原点位于图像的左上角,X 轴向右,Y 轴向下(也就是u,v* 坐标)。如果还有第三个轴—Z 轴,根据右手法则,Z 轴向前。这种定义方式是与相机坐标系一致的。图像的宽度或列数,对应着 X 轴;而图像的行数或高度,则对应着它的 Y 轴。

根据这种定义方式,访问一个位于 x,y 处的像素,那么在程序中应该是:

unsigned char pixel = image[y][x];  //访问图像像素

它对应着灰度值 I(x,y) 的读数。

视觉SLAM十四讲学习笔记-第五讲-图像和实践_第1张图片

在 RGB-D 相机的深度图中,记录了各个像素与相机之间的距离。这个距离通常是以毫米为单位,而 RGB-D 相机的量程通常在十几米左右,超过了 255。这时会采用 16 位整数(unsigned short)来记录深度图的信息,也就是位于 0~65535 的值。换算成米的话,最大可以表示 65 米,足够 RGB-D 相机使用。

彩色图像的表示则需要通道(channel)的概念。在计算机中,用红色、绿色和蓝色这三种颜色的组合来表达任意一种色彩。于是对于每一个像素,就要记录其 R、G、B 三个数值,每一个数值就称为一个通道。最常见的彩色图像有三个通道,每个通道都由 8 位整数表示。在这种规定下,一个像素占据 24 位空间。通道的数量、顺序都是可以自由定义的。在 OpenCV 的彩色图像中,通道的默认顺序是 B、G、R。也就是说,当得到一个 24 位的像素时,前 8 位表示蓝色数值,中间 8 位为绿色,最后 8 位为红色。如果还想表达图像的透明度,就使用 R、G、B、A 四个通道。

5.3 实践:计算机中的图像

5.3.1 OpenCV 的基础使用方法

安装 OpenCV,网站:

Home - OpenCV​opencv.org/正在上传…重新上传取消​

OpenCV提供了大量的开源图像算法,是计算机视觉中使用极广的图像处理算法库。

在Ubuntu下,有两种安装方式:

  1. 从源代码安装,指从OpenCV网站下载所有的OpenCV源代码,并在机器上编译安装,以便使用。好处是可以选择的版本比较丰富,而且能看到源代码,不过需要编译。还可以调整一些编译选项,匹配编程环境(例如,需不需要GPU加速等),还可以使用一些额外的功能。 源代码安装OpenCV 目前维护了两个主要版本,分为 OpenCV2.4系列和 OpenCV3系列。
  2. 只安装库文件,指通过Ubuntu来安装由Ubuntu社区人员已经编译好的库文件,这样无须编译。

源代码安装,安装依赖项:

sudo apt−get install build−essential libgtk2.0−dev libvtk5−dev libjpeg−dev libtiff4−dev libjasper−dev libopenexr−dev libtbb−dev

OpenCV 的依赖项很多,缺少某些编译项会影响它的部分功能,但可能不会用上。OpenCV 会在 cmake 阶段检查依赖项是否会安装,并调整自己的功能。如果电脑上有GPU并且安装了相关依赖项,OpenCV也会把GPU加速打开。

安装:

cmake ..
make -j8
sudo make install

安装后,OpenCV 默认存储在/usr/local 目录下

操作 OpenCV 图像

slambook/ch5/imageBasics/imageBasics.cpp

在该例程中操作有:图像读取、显示、像素遍历、复制、赋值等。编译该程序时,需要在CMakeLists.txt中添加 OpenCV的头文件,然后把程序链接到库文件上,还使用了C++11标准(如 nullptr 和 chrono)。

编译运行:

视觉SLAM十四讲学习笔记-第五讲-图像和实践_第2张图片

报错:

CMakeFiles/joinMap.dir/joinMap.cpp.o:在函数‘fmt::v7::detail::compile_parse_context::char_type const* fmt::v7::detail::parse_format_specs >, fmt::v7::detail::compile_parse_context >(fmt::v7::detail::compile_parse_context&)’中:
joinMap.cpp:(.text._ZN3fmt2v76detail18parse_format_specsIN5Eigen9TransposeINS3_6MatrixIdLi4ELi1ELi0ELi4ELi1EEEEENS1_21compile_parse_contextIcNS1_13error_handlerEEEEEPKNT0_9char_typeERSB_[_ZN3fmt2v76detail18parse_format_specsIN5Eigen9TransposeINS3_6MatrixIdLi4ELi1ELi0ELi4ELi1EEEEENS1_21compile_parse_contextIcNS1_13error_handlerEEEEEPKNT0_9char_typeERSB_]+0x247):对‘fmt::v7::detail::error_handler::on_error(char const*)’未定义的引用

需要将之前安装的fmt库链接joinMap.cpp,rgbd文件夹中的cmakelists如下:

find_package(Sophus REQUIRED)
include_directories(${Sophus_INCLUDE_DIRS})
find_package(Pangolin REQUIRED)
find_package(FMT REQUIRED)
add_executable(joinMap joinMap.cpp)
target_link_libraries(joinMap fmt::fmt ${OpenCV_LIBS} ${Pangolin_LIBRARIES})

在图像中,鼠标点击图像中的每个点都能在左下角得到UV坐标值和RGB三通道值。

函数解析如下:

  1. cv::imread:函数读取图像,并把图像和基本信息显示出来。
  2. OpenCV 提供了迭代器,可以通过迭代器遍历图像的像素。cv::Mat::data 提供了指向图像数据开头的指针,可以直接通过该指针自行计算偏移量,然后得到像素的实际内存位置。
  3. 复制图像中直接赋值是浅拷贝,并不会拷贝数据,而clone方法是深拷贝,会拷贝数据,这在图像存取中会经常用到。
  4. 在编程过程中碰到图像的旋转、插值等操作,自行查阅函数对应的文档,以了解它们的原理与使用方式。

注:1. cv::Mat 亦是矩阵类,除了表示图像之外,我们也可以用它来存储位姿等矩阵数据,但一般还是使用eigen,更快一些。

  1. cmake默认编译的是debug模式,如果使用release模式会快很多。

5.3.2 图像去畸变

OpenCV 提供了去畸变函数 cv::Undistort(),这个例程从公式出发计算了畸变前后的图像坐标(代码中有内参数据)。

slambook/ch5/imageBasics/undistortImage.cpp

运行如下:

视觉SLAM十四讲学习笔记-第五讲-图像和实践_第3张图片

可以看到去畸变前后图像差别还是蛮大的。

5.4 实践:3D 视觉

5.4.1 双目视觉

在stereo文件夹中,有左右目的图像和对应代码。其中代码计算图像对应的视差图,然后再计算各像素在相机坐标系下的坐标,它们共同构成点云。

slambook/ch5/stereoVision/stereoVision.cpp

运行如下:(比较大的图片是视差图)

视觉SLAM十四讲学习笔记-第五讲-图像和实践_第4张图片

例程中调用了OpenCV实现的SGBM算法(Semi-global Batch Matching)[26] H. Hirschmuller, “Stereo processing by semiglobal matching and mutual information,” IEEE Transactions on pattern analysis and machine intelligence, vol. 30, no. 2, pp. 328–341, 2008.

计算左右图像的视差,然后通过双目相机的几何模型把它变换到相机的3D空间中。SGBM 使用了来自网络的经典参数配置,主要调整了最大和最小视差。视差数据结合相机的内参、基线,即能确定各点在三维空间中的位置。感兴趣可以阅读相关的参考文献[27, 28]。

[27] D. Scharstein and R. Szeliski, “A taxonomy and evaluation of dense two-frame stereo correspondence algorithms,” International journal of computer vision, vol. 47, no. 1-3, pp. 7–42, 2002.

[28] S. M. Seitz, B. Curless, J. Diebel, D. Scharstein, and R. Szeliski, “A comparison and evaluation of multi-view stereo reconstruction algorithms,” in null, pp. 519–528, IEEE, 2006.

5.4.2 RGB-D 视觉

RGB-D相机能通过物理方法获得像素深度信息。如果已知相机的内外参,可以计算任何一个像素在世界坐标系下的位置,从而建立一张点云地图。

位于 slambook/ch5/rgbd 文件夹中有5对图像。在 color/下有 1.png 到 5.png 共 5 张 RGB 图,而在 depth/下有 5 张对应的深度图。同时,pose.txt 文件给出了5张图像的相机外参位姿。位姿记录的形式为平移向量加旋转四元数: [x, y, z, qx, qy, qz, qw], 其中 qw 是四元数的实部。

这一段程序,完成了两件事:(1). 根据内参计算一对 RGB-D图像对应的点云;(2). 根据各张图的相机位姿(也就是外参),把点云加起来,组成地图。

slambook/ch5/rgbd/jointMap.cpp

运行程序如下:

视觉SLAM十四讲学习笔记-第五讲-图像和实践_第5张图片

习题

  1. 寻找一部相机,标定它的内参。可能会用到标定板, 或者棋盘格。
  2. 相机内参的物理意义。如果一部相机的分辨率变为原来的两倍而其他地方不变,它的内参如何变化?
  3. 搜索特殊相机(鱼眼或全景相机)的标定方法。它们与普通的针孔模型有何不同?
  4. 调研全局快门相机(global shutter)和卷帘快门相机(rolling shutter)的异同。它们在SLAM中有何优缺点?
  5. RGB-D 相机是如何标定的?以 Kinect 为例,需要标定哪些参数?(参照https://github.com/code-iai/iai_kinect2)
  6. 除了示例程序演示的遍历图像的方式,还能举出哪些遍历图像的方法?
  7. 阅读 OpenCV 官方教程,学习它的基本用法。

习题有代码学习和工程应用的知识,后面实际开发中会很有帮助。

你可能感兴趣的:(算法,书籍学习笔记,计算机视觉,opencv,图像处理,slam,人工智能)