6

双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python

 2 years ago
source link: https://blog.csdn.net/guyuealian/article/details/121301896
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python


目录

双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python

1.项目结构

2. Environment

3.双目相机标定和校准

(0) 双目摄像头

(1) 采集标定板的左右视图

(2) 单目相机标定和校准

(3) 双目相机标定和校准

4.视差图和深度图

(1) 立体校正

(2) 立体匹配与视差图计算

(3) Demo

5.双目测距

6.3D点云显示

7.Demo代码

8.参考资料


博客将实现Python版本的双目三维重建系统,项目代码实现包含:`双目标定`,`立体校正(含消除畸变)`,`立体匹配`,`视差计算`和`深度距离计算/3D坐标计算` 的知识点。限于篇幅,本博客不会过多赘述算法原理,而是手把手教你,如果搭建一套属于自己的双目三维重建的系统。项目代码包含:

  • 支持双USB连接线的双目摄像头
  • 支持单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示)
  • 支持单目相机标定:mono_camera_calibration.py ,无需Matlab标定
  • 支持双目相机标定:stereo_camera_calibration.py,无需Matlab标定
  • 支持使用WLS滤波器对视差图进行滤波
  • 支持双目测距,误差在1cm内(鼠标点击图像即可获得其深度距离)
  • 支持Open3D和PCL点云显示

尊重原创,转载请注明出处】:https://panjinquan.blog.csdn.net/article/details/121301896

先放一张动图,这是最终重建的效果:

完整的项目代码】:https://download.csdn.net/download/guyuealian/42517006

三维重建中,除了双目相机,还有TOF和结构光3D 相机

  1. 飞行时间(Time of flight,TOF),代表公司微软Kinect2,PMD,SoftKinect, 联想 Phab,在手机中一般用于3D建模、AR应用,AR测距(华为TOF镜头)

  2. 双目视觉(Stereo Camera),代表公司 Leap Motion, ZED, 大疆;

  3. 结构光(Structured-light),代表公司有奥比中光,苹果iPhone X(Prime Sense),微软 Kinect1,英特尔RealSense, Mantis Vision 等,在手机(iPhone,华为)中3D结构光主要用于人脸解锁、支付、美颜等场景。

关于3D相机技术(飞行时间+双目+结构光)的区别,可以参考我的一篇博客《3D相机技术调研(飞行时间TOF+双目+结构光)》:

3D相机技术调研(飞行时间TOF+双目+结构光)_pan_jinquan的博客-CSDN博客1.深度估计3D相机方案目前市面上常有的 3D 相机方案就就是这3种:飞行时间(Time of flight,TOF),代表公司微软Kinect2,PMD,SoftKinect, 联想 Phab,在手机中一般用于3D建模、AR应用,AR测距(华为TOF镜头)双目视觉(Stereo Camera),代表公司 Leap Motion, ZED, 大疆;结构光(Structured-light),代表公司有奥比中光,苹果iPhone X(Prime Sense),微软Kin..favicon32.icohttps://panjinquan.blog.csdn.net/article/details/119649838果你对结构光三维重建感兴趣,可参考鄙人的博客《结构光三维重建-3D Scanning Software实现三维重建

结构光三维重建-3D Scanning Software实现三维重建_pan_jinquan的博客-CSDN博客结构光相机标定-3D Scanning Software使用1. 说明2.Requirements(1)下载相关文件(2)3D Scanning Software源码编译3. Data capture(1)运行程序:scan3d-capture(2)采集校准图片:Capture calibration images(3) 进行校准:Calibration4.扫描模型:Model Scanninfavicon32.icohttps://panjinquan.blog.csdn.net/article/details/121113787


1.项目结构


2. Environment

  • 依赖包,可参考requirements.txt
  • python-pcl (安装python-pcl需要一丢丢耐心,实在不行,就用open3d吧)
  • open3d-python=0.7.0.0
  • opencv-python
  • opencv-contrib-python

3.双目相机标定和校准

(0) 双目摄像头

下面这款双目摄像头(RGB+RGB)是在某宝购买的(几百元,链接就不发了,免得说我打广告),作为本项目的双目相机,其基线是固定的6cm,是单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示),基本满足我们测试需求。一般基线越长,可测量的距离越远,网友也可以根据自己需要购买。

一点注意事项

  1. 双目相机三维重建也可以使用RGB+IR(红外)的摄像头,甚至IR+IR的也是可以,本人亲测,RGB+IR的相机,其效果也是杠杠的。
  2. 基线不太建议太小,作为测试,一般baseline在3~9cm就可以满足需求,有些无人车的双目基线更是恐怖到1~2米长
  3. 从双目三维重建原理中可知,左右摄像头的成像平面尽可能在一个平面内,成像平面不在同一个平面的,尽管可以立体矫正,其效果也差很多。
  4. 一分钱,一分货,相机的质量好坏,直接决定了你的成像效果

(1) 采集标定板的左右视图

  • 采集数据前,请调节相机焦距,尽可能保证视图中标定板清洗可见
  • 采集棋盘格图像时,标定板一般占视图1/2到1/3左右
  • 一般采集15~30张左右

参数说明: 

  1. 参数width指的是棋盘格宽方向黑白格子相交点个数
  2. 参数height指的是棋盘格长方向黑白格子相交点个数
  3. 参数left_video是左路相机ID,一般就是相机连接主板的USB接口号
  4. 参数right_video是右路相机ID,一般就是相机连接主板的USB接口号
  5. PS:如果你的双目相机是单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示),则设置left_video=相机ID,而right_video=-1,
  6. 参数detect建议设置True,这样可实时检测棋盘格,方面调整角度
  7. 按键盘s或者c保存左右视图图片
left_imageright_image

 下面是采集双目摄像头标定板左右视图的Python代码:get_stereo_images.py,除了OpenCV,没啥依赖,直接干就完事。

双目标定的目标是获得左右两个相机的内参、外参和畸变系数,其中内参包括左右相机的fx,fy,cx,cy,外参包括左相机相对于右相机的旋转矩阵和平移向量,畸变系数包括径向畸变系数(k1, k2,k3)和切向畸变系数(p1,p2)。

双目标定工具最常用的莫过于是MATLAB的工具箱: Stereo Camera Calibrator App,网上有太多教程,我就不赘述了。

我采用的是Deepin系统,懒得去安装Matlab了,所以就参考各路神仙,使用OpenCV实现了单目和双目的标定程序。

(2) 单目相机标定和校准

bash scripts/mono_camera_calibration.sh

一点注意事项: 

  • 标定代码会显示每一张图像的棋盘格的角点检测效果,如果发现有检测不到,或者角点检测出错,则需要自己手动删除这些图像,避免引入太大的误差
  • 若误差超过0.1,建议重新调整摄像头并标定,不然效果会差很多

 执行后,在$save_dir目录下会生成left_cam.ymlright_cam.yml左右相机参数文件

其中K是相机内参矩阵,D是畸变系数矩阵

这是完整的代码

(3) 双目相机标定和校准

bash scripts/stereo_camera_calibration.sh

一点注意事项:  

  • 若误差超过0.1,建议重新调整摄像头并标定

执行后,在$save_dir目录下会生成stereo_cam.yml相机参数文件,这个文件,包含了左右相机的相机内参矩阵(K1,K2),畸变系数矩阵(D1,D2),左右摄像机之间的旋转矩阵R和平移矩阵T,以及本质矩阵E和基本矩阵F等.

有了双目相机内外参数信息(stereo_cam.yml),下面就可以进行立体矫正,计算视差了

4.视差图和深度图

(1) 立体校正

这部分基础知识来源于:https://blog.csdn.net/dulingwen/article/details/100115157

立体校正的目的是将拍摄于同一场景的左右两个视图进行数学上的投影变换,使得两个成像平面平行于基线,且同一个点在左右两幅图中位于同一行,简称共面行对准。只有达到共面行对准以后才可以应用三角原理计算距离。

(2) 立体匹配与视差图计算

立体匹配的目的是为左图中的每一个像素点在右图中找到其对应点(世界中相同的物理点),这样就可以计算出视差:(xi和xj分别表示两个对应点在图像中的列坐标)。

大部分立体匹配算法的计算过程可以分成以下几个阶段:匹配代价计算、代价聚合、视差优化、视差细化。立体匹配是立体视觉中一个很难的部分,主要困难在于:

1.图像中可能存在重复纹理和弱纹理,这些区域很难匹配正确;

2.由于左右相机的拍摄位置不同,图像中几乎必然存在遮挡区域,在遮挡区域,左图中有一些像素点在右图中并没有对应的点,反之亦然;

3.左右相机所接收的光照情况不同;

4.过度曝光区域难以匹配;

5.倾斜表面、弯曲表面、非朗伯体表面;

6.较高的图像噪声等。

常用的立体匹配方法基本上可以分为两类:局部方法,例如BM、SGM、ELAS、Patch Match等,非局部的,即全局方法,例如Dynamic Programming、Graph Cut、Belief Propagation等,局部方法计算量小,匹配质量相对较低,全局方法省略了代价聚合而采用了优化能量函数的方法,匹配质量较高,但是计算量也比较大。

目前OpenCV中已经实现的方法有BM、binaryBM、SGBM、binarySGBM、BM(cuda)、Bellief Propogation(cuda)、Constant Space Bellief Propogation(cuda)这几种方法。比较好用的是SGBM算法,它的核心是基于SGM算法,但和SGM算法又有一些不同,比如匹配代价部分用的是BT代价(原图+梯度图)而不是HMI代价等等。有关SGM算法的原理解释,可以参考另一篇博客 : 《双目立体匹配算法:SGM https://blog.csdn.net/dulingwen/article/details/104142149

(3) Demo

  • 运行Demo进行立体矫正,计算视差图并恢复深度图,
  • Demo的参数说明如下
参数类型说明calibration_filestr双目相机的配置文件,如"configs/lenacv-camera/stereo_cam.yml"left_videostr左路相机ID或者视频文件right_videostr右路相机ID或者视频文件left_filestr左路测试图像文件right_filestr右路测试图像文件filterbool是否对视差图进行滤波
  • 视差图滤波:

在立体匹配生成视差图之后,还可以对视差图进行滤波后处理,例如Guided Filter、Fast Global Smooth Filter(一种快速WLS滤波方法)、Bilatera Filter、TDSR、RBS等。 视差图滤波能够将稀疏视差转变为稠密视差,并在一定程度上降低视差图噪声,改善视差图的视觉效果,但是比较依赖初始视差图的质量。

左视图右视图视差图(未滤波)深度图(未滤波)视差图(滤波后)深度图(滤波后)

可以看到,使用WLS滤波后,视差图的整体效果都有明显改善

  • 最终效果图

5.双目测距

得到了视差图之后,就可以计算像素深度了,在opencv中使用StereoRectify()函数可以得到一个重投影矩阵Q,它是一个4*4的视差图到深度图的映射矩阵(disparity-to-depth mapping matrix ),使用Q矩阵和cv2.reprojectImageTo3D即可实现将像素坐标转换为三维坐标,该函数会返回一个3通道的矩阵,分别存储X、Y、Z坐标(左摄像机坐标系下)。

运算如下:

 [X,Y,Z,W] ^T=Q*[x,y,disparity(x,y),1]^ T

_3dImage(x,y)=(X/W,Y/W,Z/W)

重投影矩阵Q中c_xc_y为左相机主点在图像中的坐标,f为焦距,T_x为两台相机投影中心间的平移(负值),即基线baseline,相当于平移向量T[0], c_{x}^{`} 是右相机主点在图像中的坐标。

其中Z即是深度距离depth:

其中 f 为焦距长度(像素焦距),b为基线长度,d为视差,c_{xl}c_{xr}为两个相机主点的列坐标。

这里有个地方需要注意,如果获得视差图像是CV_16S类型的,这样的视差图的每个像素值由一个16bit表示,其中低位的4位存储的是视差值得小数部分,所以真实视差值应该是该值除以16。在进行映射后应该乘以16,以获得毫米级真实位置。

  • 运行demo.py后,鼠标点击图像任意区域,终端会打印对应距离
  • 鼠标点击手部区域会打印距离摄像头的距离约633mm,即0.63米,还是比较准的

 双目测距的精度 说明:

根据上式可以看出,某点像素的深度精度取决于该点处估计的视差d的精度。假设视差d的误差恒定,当测量距离越远,得到的深度精度则越差,因此使用双目相机不适宜测量太远的目标。

如果想要对与较远的目标能够得到较为可靠的深度,一方面需要提高相机的基线距离,但是基线距离越大,左右视图的重叠区域就会变小,内容差异变大,从而提高立体匹配的难度,另一方面可以选择更大焦距的相机,然而焦距越大,相机的视域则越小,导致离相机较近的物体的距离难以估计。


6.3D点云显示

恢复三维坐标后,就可以使用python-pcl和Open3D库显示点云图

PCL Python版比较难安装,如果安装不了,那可以采用Open3D勉强凑合使用吧

如下图所示,你可以用鼠标旋转坐标轴,放大点云

2D-RGBOpen3D点云显示PCL点云显示

7.Demo代码

完整的项目代码】:https://download.csdn.net/download/guyuealian/42517006


8.参考资料


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK