相机将三维世界中的坐标点(单位为米)映射到二维图像平面(单位为像素)的过程能够用一个几何模型进行描述。这个模型有很多种,其中最简单的称为针孔模型。针孔模型是很常用,而且有效的模型,它描述了一束光线通过针孔之后,在针孔背面投影成像的关系。在本书中我们用一个简单的针孔相机模型来对这种映射关系进行建模。同时,由于相机镜头上的透镜的存在,会使得光线投影到成像平面的过程中会产生畸变。因此,我们使用针孔和畸变两个模型来描述整个投影过程。
该式中,我们把中间的量组成的矩阵称为相机的内参数矩阵(Camera Intrinsics)K.
同一直线上的投影点仍是同一个
投影顺序:世界——相机——归一化平面——像素:(激光数据的观测模型更加简单)
针孔相机模型描述了单个相机的成像模型。然而,仅根据一个像素,我们是无法确定这个空间点的具体位置的。这是因为,从相机光心到归一化平面连线上的所有点,都可以投影至该像素上。只有当P 的深度确定时(比如通过双目或RGB-D 相机),我们才能确切地知道它的空间位置。
测量像素距离(或深度)的方式有很多种,像人眼就可以根据左右眼看到的景物差异(或称视差)来判断物体与我们的距离。双目相机的原理亦是如此。通过同步采集左右相机的图像,计算图像间视差,来估计每一个像素的深度。下面我们简单讲讲双目相机的成像原理(图5-6 )。
RGB-D相机:物理手段测量深度
注意:先别安装4,后面会讲原因。
本章需要安装OpenCV,书上推荐的是OpenCV3系列。但是第 8 讲的直接法实现,需要使用 OpenCV 4 支持的 cv::parallel_for_ 函数。如果读者使用较旧的版本,需要对代码做一些改动。具体的改动方法请参照对应版本的 OpenCV 文档,或者参考 https://github.com/gaoxiang12/slambook2/issues/32
所以我就直接安装的OpenCV4版本,这里安装的是最新的4.1.2版本,关于OPenCV的安装。
安装完成后在终端输入:pkg-config --modversion opencv4
可查看版本:
若不是OpenCV4系列,则直接输入pkg-config --modversion opencv
查看即可。
关于c++11标准的报错就不提了,和之前的几章一样,在CMakeLists.txt文件中加入相应的代码即可。
这要opencv2的,路径不对:
可以看到opencv2在opencv4中,故将头文件改为:
#include
#include
依然出现错误:
fatal error: opencv2/core.hpp: 没有那个文件或目录
仔细查看之后,并不是这个错误,高度怀疑是OpenCV的版本问题,所以又卸载了4安装了3,关于3的安装。
依然有报错:
这一看就是没有找到相应的OpenCV文件,所以在CMakeLists.txt
文件夹下添加:
find_package( OpenCV REQUIRED )
编译成功:
执行程序:
说是找不到文件,其实是要在cmake-build-debug
下运行的,进入cmake-build-debug
,输入命令:./imageBasics ../ubuntu.png
,可以得到结果:
**补充:**其实在IDE中也可以run起来,只不过要确保把参数传给main!在clion选择:
得到:
填入正确的路径,比如我的是:/home/wh/shenlan/slambook2/ch5/imageBasics/ubuntu.png
,再run一下即可:
结果为:
/home/wh/shenlan/slambook2/ch5/imageBasics/cmake-build-debug/imageBasics /home/wh/shenlan/slambook2/ch5/imageBasics/ubuntu.png
图像宽为1200,高为674,通道数为3
遍历图像用时:3.23e-07 秒。
Process finished with exit code 0
设置好相应的参数后编译成功,执行时出现错误:
error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'imshow'
一开始以为是路径错误,但是排查好久并没有错。然后又想到是不是参数配置还有错,果然,是Working directory没有选对!应该修改为:
再次运行,就没有问题了,可以看到去畸变的前后对比:
编译后依然是缺少C++11的支持,按照老样子加上即可。
突然发现,在ch5的文件夹里有一个总的CMakeLists.txt文件,直接先按照哪个编译即可,啊啊啊啊啊啊!看来我还是太蠢了啊!
编译执行后依然是这样:
返回到rgbd
目录下,输入命令:cmake-build-debug/joinMap
,可以得到:
当然,和上一个实践一样,传入参数,一样是可以在clion中直接run的。配置如下:
需要注意的是,Working directory
的目录一定要是包含pose.txt
的目录,这里的也就是rgbd目录。运行结果如下:
/home/wh/shenlan/slambook2/ch5/rgbd/cmake-build-debug/joinMap /home/wh/shenlan/slambook2/ch5/rgbd/color /home/wh/shenlan/slambook2/ch5/rgbd/depth
转换图像中: 1
转换图像中: 2
转换图像中: 3
转换图像中: 4
转换图像中: 5
点云共有1081843个点.
Process finished with exit code 15
可以看出与之前的结果也是一样的。
这次的实验部分真是令我印象深刻!各种报错、调试,不断的去实践,去修改,然后验证自己的想法,老实说,还是非常有收获的!学到了很多,也对各个工程的运作加深了理解,前路漫漫,继续加油!
ros官方原汁原味的参考文档
参考这篇博客
另一篇参考
等有时间了我再来亲自试一试!
相机内参的物理意义:一点从相机坐标系到像素坐标系上的坐标变换。
相机投影公式:
u的单位为像素,其物理意义为世界坐标系下的点在像素平面的投影。所以当分辨率增加一倍,u,v也就变为原来的两倍。为了保证恒等,则等式右边也得乘以二,然而三维世界坐标并未变化,所以焦距的值和主点的坐标值也会增加一倍。亦即fx、fy变为原来的两倍。
鱼眼相机的标定:鱼眼相机模型和普通针孔相机模型类似,但是鱼眼相机的畸变模型和普通相机不一样,最主要的是在径向畸变上多一个高阶系数,区别体现在畸变系数,内参矩阵是相同的。
全景相机的标定:图像变形非常大,全景相机的是一个像素球面。其标定参数因相机而定,但是大部分的相机标定还是分为去畸变和像素面投影两步。
参考这篇博客:全局相机是一次成像,所有像素停止曝光,容易出噪点。相比较而言卷帘快门是逐行成像,大多数CMOS传感器采用这一快门。但是对于高速移动的物体来说,卷帘快门容易出现扭曲现象。Global shutter 曝光时间更短,但会增加读出噪声;
对于相机厂家,Rolling shutter可以达到更高的帧速,但当曝光不当或物体移动较快时,会出现部分曝光(partial exposure)、斜坡图形(skew)、晃动(wobble) 等现象。这种Rolling shutter方式拍摄出现的现象,就定义为果冻效应。
曝光时间短的应用(如<500μs)适合Global shutter,曝光时间长(如大于500μs)时,选择rolling shutter可以有更低的噪声和帧速。
在slam中,低速情况下两者区分不大,但是全局相机目前是一种趋势适用于各种场合。
具体参考这篇博客以及官方教程
遍历图像像素的14种方法
主要有这几种:
1.一个一个点读取的方法:image.at<>(i,j);
2.指针遍历:Mat图像在内存中连续排布可以用 image.Ptr<>()指针遍历;(用的最多)
3.迭代器遍历:利用cv里的iterator进行遍历。
OpenCV官网
基本用法教程