专栏系列文章如下:
视觉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十四讲学习笔记-第四讲-Sophus实践、相似变换群与李代数_goldqiu的博客-CSDN博客
相机把三维世界中的信息转换成了一张由像素组成的照片,存储在计算机中,作为后续处理的数据来源。在数学中,图像可以用一个矩阵来描述;而在计算机中,它们占据一段连续的磁盘或内存空间,可以用二维数组来表示。
最简单的图像——灰度图:每个像素位置 (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) 的读数。
在 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 四个通道。
安装 OpenCV,网站:
Home - OpenCVopencv.org/正在上传…重新上传取消
OpenCV提供了大量的开源图像算法,是计算机视觉中使用极广的图像处理算法库。
在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)。
编译运行:
报错:
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::Mat 亦是矩阵类,除了表示图像之外,我们也可以用它来存储位姿等矩阵数据,但一般还是使用eigen,更快一些。
OpenCV 提供了去畸变函数 cv::Undistort(),这个例程从公式出发计算了畸变前后的图像坐标(代码中有内参数据)。
slambook/ch5/imageBasics/undistortImage.cpp
运行如下:
可以看到去畸变前后图像差别还是蛮大的。
在stereo文件夹中,有左右目的图像和对应代码。其中代码计算图像对应的视差图,然后再计算各像素在相机坐标系下的坐标,它们共同构成点云。
slambook/ch5/stereoVision/stereoVision.cpp
运行如下:(比较大的图片是视差图)
例程中调用了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.
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
运行程序如下:
习题有代码学习和工程应用的知识,后面实际开发中会很有帮助。