从两张任意拍摄的一对图像(得有大部分重合面积)和相机内参矩阵开始,重建出基于Mesh的三维模型,美观又实用,还不赶快学起来。本文也是记录一下自己学习过程,废话较多,请多包涵,主要代码已注释,请自行下载观看。
总体路线及原理如下:
为了方便地实现三维重建,本文从多张RGB彩色影像出发,结合相机内参,即可重建出近景的三维模型。第一步,利用RGB彩色图像进行极线校正,将两幅影像中的同名点重映射到同一水平方向。程序中使用Opencv计算图像中的ORB特征点和BRIEF描述子,进行匹配,恢复本质矩阵E,再分解本质矩阵E得到左视图到右视图的旋转矩阵R和平移向量T。经过Opencv中的stereoRectify,initUndistortRectifyMap,remap函数处理,即可得到核线像对。第二步,使用核线像对进行立体匹配,得到视差图。立体匹配有传统的SGM方法,也有基于深度学习的方法。从目前研究进展来看,基于深度学习的端到端神经网络在预测速度和精度上较传统方法都有一定优势,但缺点是不容易部署到实际应用中,训练过程需要较大数据集和显存足够大的GPU。本文比较了端到端的深度学习方法和传统的SGM方法,也在各种端到端深度学习方法之间进行了比较,最终选择了在精度效果较好的方法PSM-Net和实时性较好的方法Any-Net上进行改进,最终得到一个平衡了速度和精度的端到端深度神经网络模型。第三步,对视差图进行后处理。将其转换成点云模型,再做点云配准和融合,最后三角化投影重建曲面。有了融合后的点云模型,可以通过PCL库进行下采样,统计滤波去除离群点,mls移动最小二乘法进行平滑处理,对平滑后的点云进行法线估计,将法线和平滑后的点云拼接到一起,经过三角化得到网格模型。
所有代码可以在这里得到:https://github.com/gpcv-luochong/3d_reconstruction
首先是原始影像加相机内参矩阵:
K=【 2905.88 0 1416
0 2905.88 1064
0 0 1】
点击rectify_image.cpp,配置好launch.json,tasks.json,c_cpp_properties.json这三个文件,如下图所示
launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/${fileBasenameNoExtension}.out",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": true,
"preLaunchTask": "build",
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "cppbuild",
"label": "build",
"command": "/usr/bin/g++",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileBasenameNoExtension}.out",
"-I", "/usr/local/include",
"-I", "/usr/local/include/opencv2",
"-L", "/usr/local/lib",
"-l", "opencv_calib3d",
"-l", "opencv_core",
"-l", "opencv_imgproc",
"-l", "opencv_imgcodecs",
"-l", "opencv_video",
"-l", "opencv_ml",
"-l", "opencv_highgui",
"-l", "opencv_objdetect",
"-l", "opencv_features2d",
"-l", "opencv_flann",
"-l", "opencv_imgcodecs",
"-l", "opencv_photo",
"-l", "opencv_videoio"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "compiler: /usr/bin/g++"
}
]
}
c_cpp_properties.json
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/local/include",
"/usr/local/include/opencv2"
],
"defines": [],
"compilerPath": "/usr/bin/g++",
"cStandard": "gnu17",
"cppStandard": "gnu++14",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}
点击VSCode中Terminal菜单下的的Run Build Task,编译rectify_image.cpp,生成rectify_image.out文件。再点击Terminal菜单下的New Terminal,再输入
./rectify_image.out ./images/001.JPG ./images/002.JPG
就可以校正图片了(注意如果要校正自己的图片,需要修改rectify_image.cpp里的相机内参K)。
在文件夹下可以得到image1.png和image2.png。
我们先用传统的SGBM方法获得视差图,再转成点云图。(因为单目相机缺少基线距离,深度信息有所丢失,但深度值和视差值相差一个倍率,在显示上来看是没有多大问题的,不妨设基线距离为1。奇怪的是我用VSCode编译sgbm.cpp文件,编译成功,但是使用./sgbm.out命令执行程序时,出现错误double free or corruption (out)。但是从终端进入build文件夹后,cmake ..,make,./sgbm,程序运行良好。真的是奇了怪了,就这个bug,我调了一天,头都是大的。运行代码需要配置opencv和pcl,看看其他博客应该就可以配置成功,祝你好运)
直接调用opencv中sgbm方法得到的视差图效果较差,存在很多空洞,感兴趣的朋友可以继续优化视差图。整个三维重建中如何获得视差图是很重要的一个环节,直接决定了后续结果是否正确,所以值得研究。但本人为了尽快搞完整个流程,就不在此多费时间,进入下一步,构建Mesh啦!
视差图:
点云图:
//ubuntu终端下进入3d_reconstruction/point2mesh文件夹,依次执行以下命令编译point2mesh.cpp文件
cd point2mesh
mkdir build
cmake ..
make
./point2mesh
最终重建出的Mesh图比较难看,就不放出来啦!但整体流程是走通了的,感觉还是缺少点优化的代码,最终效果有待提高。走过路过,不要错过,记得点赞加三连,你们的支持就是博主的动力,嘿嘿嘿。