重建基本流程:特征提取与匹配->稀疏重建->稠密重建->表面重建。
以下很多使用来自于B站视频。(图有点问题,openMVS可以进行Dense cloud)
了解一下VisualSFM:
官网地址。github地址。可以直接下载它的bin运行,是带GUI的,
“VisualSFM is free for personal, non-profit or academic use.”
它得到的数据格式就是 “.nvm” 的
英伟达开源的 instant-ngp 是基于nerf,它的一个数据预处理,也是用colmap实现的,主要就是用colmap的特征提取、特征匹配、去畸变,stereo(后面两个不确定有用到没),
主要还是参考B站这个视频;纹理贴图(meshlab实现的)知乎这篇文章,里面还带有命令行,这文章对应的也有B站视频;这两篇meshlab的贴图放这里做个参考吧,后续看能不能用到,1、2。
即:稀疏重建、稠密重建是用的colmap;mesh时可用colmap或是meshlab(这次用的clomap);最终的纹理贴图是用的meshlab。示例教程如下:
首先要搞定的是colmap的linux环境编译,一定要带cuda、gui的编译,不带gui会使OpenGL被禁用,一些使用了OpenGL关键字的代码就会编译error,官方地址。
下面是可能遇到的错误及解决办法:
cmake时可能遇到的错误:
CMake Error at CMakeLists.txt:255 (message): You must set CMAKE_CUDA_ARCHITECTURES to e.g. ‘native’, ‘all-major’, ‘70’, etc. More information at https://cmake.org/cmake/help/latest/prop_tgt/CUDA_ARCHITECTURES.html
解决办法,指定CMAKE_CUDA_ARCHITECTURES参数的值(可能需要cmake3.24及以上版本): cmake -DCMAKE_INSTALL_PREFIX=/opt/colmap/my_install -DCMAKE_CUDA_ARCHITECTURES=’native’ ..
若不行的话,再指定自己的CUDA版本试试,比如 cuda_11.4,那就是-DCMAKE_CUDA_ARCHITECTURES=’114’
运行时还可能遇到这样的错误:
“./colmap: error while loading shared libraries: libQt5Core.so.5: cannot open shared object file: No such file or directory” # 也可能是其它动态库
解决办法:全局搜索一下libQt5Core.so.5,前面安装环境都搞完的话,在docker容器中的/usr/lib/x86_64-linux-gnu里是有的,那就执行如下命令: strip –remove-section=.note.ABI-tag /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
要注意 libQt5Core.so.5 所在的实际路径
这次使用的数据集是一组大象elephant,共有52张图。
使用colmap做第一阶段:
数据准备:cd ~/project/elephant # elephant文件夹中只有一个名为“images”的文件夹,这个文件夹里有那52张大象图
启动:colmap gui # 打开gui
下面就是gui中的操作:
File–>new project,然后new一个database的名字,图片就选刚刚的“images”文件夹
Processing–>Feature extraction # 直接Extract执行,使用默认参数进行特征提取(几秒)
Processing–>Feature matching # 直接Run执行,使用默认参数(num_threads给-1代表根据硬件最大线程数),参数问题后续还要去熟悉,(这是会用cuda的,就很快,10来秒,不然cpu的话时间就很长很长)
稀疏重建:Reconstruction–>Start reconstruction # 2080Ti的cpu占用在一半左右跳动,(时间大概在2分23秒)
*导出稀疏云:File->Export model as # 一定要选择”Bundler(.out)”这个数据格式 **
上面导出后会得到“my_sparse.out”、“my_sparse.out.list.txt”这两个文件,这很重要,后续做贴图的时候是需要的 # 这一步非常重要,一定是要有的
稠密重建:Reconstruction–>Dense reconstruction # 会弹出来一个界面,
17分钟
),这个过程也能用meshlab去实现,那就是cpu跑,可能就要几天了(还是要看重建量的大小)4分钟
左右吧注:是不是考虑在稠密点云结果的基础上直接做目标检测,然后删除掉一些东西,然后在目标点云上做Poisson Mesh,这样的结果会小很多,然后再在这个上面做贴图,不然噪点太多,mesh建面出来的结果是出不来的。
此路径下的project.ini
就是项目跑完后存下来的默认配置文件,里面很多参数可作参考。
注:==meshlab按住ctrl就可以拖拽模型的左右上下位置==。
使用meshlab制作贴图和减面 # 参考的的主要是还是这个视频。以下的步骤是在colmap完成了泊松建面后,再在meshlab中做的贴图。可以尝试先点云配准,把目标物的稠密点云搞出来,然后再poisson-Mesh。
第一步用meshlab打开前面保存的 sparse.out 稀疏云,然后它会让打开 sparse.out.list.txt #记得要把用于重建的52张图放到这俩文件夹的同一个文件夹
用meshlab导入colmap中生成的 meshed-poisson.ply mesh,然后右上角隐藏掉稀疏云
选中大象的部分,然后反选,把多余的mesh部分面删掉,
制作纹理:
制作之前,要删除一些面: # 不然下一步会报错
分辨率一般给到4096: # 这个功能在texture中
很多时候可能用下面这种方式好一些,先稠密点云把不要的删了,再来poissonMesh(这个mesh用colmap、meshlab都行),不然有的数据干扰太多,一个通用化的参数就不好设置,得到的mesh是有问题的,就成了一团。上面也讲到过,colmap做泊松建面时出不来结果,就可能是先用meshlab将稠密点云选中目标区再反选,删除所有噪点,再mesh重建。
在meshlab中对稠密点云进行建面时,深度至少选择10,参考colmap我给的13
一顿计算之后,就再做贴图就好了,跟上面一样,经过验证发现这样的方式也是可以。
colmap是支持命令的行,但编译的时候还是要记得编译带gui的,colmap命令行地址。
以下脚本路径中只有一个名为”images”的文件夹,里面是全部的图像。
==方式一==:总的一次生成
# cd到一个只有"images"文件夹的文件夹里执行如下:
colmap automatic_reconstructor --workspace_path . --image_path ./images/ # 这里会自动做泊松建面
colmap model_converter --input_path sparse/0 --output_path ./my_sparse --output_type Bundler # ./my_spase是自己起的文件名
注:
只有8张图那个例子,用这计算(gui也得不行),是得不到结果的,大概的提示是: “=> Could not register, trying another image.” “=> No good initial image pair found.” 最后一行还有“ERROR: failed to create sparse model”
所以后续工程化要去做判定,这里直接就返回提示数据问题。
第二个命令是为了导出格式好用于后面贴图,它执行之后就会得到 spase.bundle.out、spase.list.txt
==方式二==:各阶段命令分开,便于修改参数
# 1.特征提取
colmap feature_extractor \
--database_path ./colmap.db \
--image_path ./images
# 2.特征匹配
colmap exhaustive_matcher --database_path ./colmap.db
# 3.稀疏重建
mkdir sparse
colmap mapper \
--database_path ./colmap.db \
--image_path ./images --output_path ./sparse
# 4.深度重建(dense/0/ 是为了跟方式一路径保持一致)
mkdir -p dense/0
# 深度重建-去畸变
colmap image_undistorter --image_path ./images \
--input_path ./sparse/0 \
--output_path ./dense/0 \
--output_type COLMAP \
--max_image_size 2000
# 深度重建-下面这会把所有gpu利用率拉满,可设置指定哪张gpu
colmap patch_match_stereo \
--workspace_path ./dense/0 \
--workspace_format COLMAP \
--PatchMatchStereo.geom_consistency true
# 深度重建-融合(fused.ply就是稠密点云)
colmap stereo_fusion \
--workspace_path ./dense/0 \
--workspace_format COLMAP \
--input_type geometric \
--output_path ./dense/0/fused.ply
# 5.最后把稀疏重建的内容转换一个格式,方便后续做贴图
# my_sparse是自己起的文件名
colmap model_converter \
--input_path ./sparse/0 \
--output_path ./my_sparse \
--output_type Bundler
# 以下的mesh建面可以选做,或许对fused.ply做了去噪后再来mesh
colmap poisson_mesher \
--input_path ./dense/0/fused.ply \
--output_path ./dense/0/meshed-poisson.ply
colmap poisson_mesher时,有一个参数是 –PoissonMeshing.depth,就用它默认是13就好了,建议是用大于10的值,我用过15,大概13用时2m57s,15用时3m49s,15的结果更大,面数更多,但效果没看到明显的更好。
还有 delaunay 算法mesh建面:colmap delaunay_mesher –input_path ./dense –output_path ./dense/meshed-delaunay.ply
# 稠密重建那里还有一种别的方式,只简单试了,但试的量不够,效果不好说
colmap patch_match_stereo \
--workspace_path ./dense \
--workspace_format COLMAP \
--PatchMatchStereo.geom_consistency false # 1.这里改了
colmap stereo_fusion \
--workspace_path ./dense \
--workspace_format COLMAP \
--input_type photometric \ # 2.主要就是这个参数
--output_path ./dense/fused.ply
注意:
WARNING: Bundler only supports SIMPLE_RADIAL
, RADIAL
, and pinhole camera models.
默认是使用的 SIMPLE_RADIAL 模式,然后instant-ngp的colmap数据预处理用的是 OPENCV ,这就不能转Bundler格式,就无法用meshlab做图片贴图。
meshlab暂时还不支持命令行,代码是QT写的,比较难剥离,去试试pymeshlab
数据的影响,周围噪点问题,数据好,colmap能直接建面出来
用colmap、meshlab建面的不同:colmap是没有就没有,那一块就是有缺失的,就很容易出现破面缺面,周围一些小的噪点数据就会不管;meshlab是会自己去补充,物体缺的,他补充出来就没那么好,各种凹凸不平,就是不怎么会缺面破面,同时它会把周围的一些小噪点都连接起来,形成大片没有的面,手动好去掉这些面,但是自动的难度就很大。
用meshlab导出的obj模型在win自带的3D查看器没有颜色,大抵是因为.mtl描述文件的问题
newmtl material_0
Ka 0.200000 0.200000 0.200000 # white
Kd 1.000000 1.000000 1.000000 # white
# Ks 1.000000 1.000000 1.000000 # black # 这就是有问题的,主要是这行,它的值太影响颜色,全部改成0,其它的不变都是ok的。
Ks 0.000 0.000 0.000 # black # 这样子就能正常显示了
Tr 1.000000
illum 2
Ns 0.000000
map_Kd texture.png
ok的:
Ka 1.000 1.000 1.000 # white
Kd 1.000 1.000 1.000 # white
Ks 0.000 0.000 0.000 # black
原始图片:周围有很多噪点:
colmap得到的稠密点云+直接此稠密点云poisson-mesh,效果很差:
稠密点云(有很多杂质): 直接根据上面的稠密点云直接mesh的结果(根本出不来效果):
在上述杂质很多的稠密点云上做了手动去噪(删掉90%以上不感兴趣的点云区域),再用colmap的泊松建面,效果还是可以的:
原图–>colmap直接得到的稠密点云–>从未处理的稠密点云直接colmap来mesh得到的模型:
干净数据:
colmap直接得到的稠密点云:
从未处理的稠密点云直接colmap来mesh得到的模型:
周围有很多噪点:
colmap直接得到的稠密点云:
从未处理的稠密点云直接colmap来mesh得到的模型:
总结:干净的数据在一定程度上可直接出来好的结果,但一定程度上可以说是无需再做3D点云的一个处理,会节省很多时间和工作量。
当稠密点云数据一样,且都是用Poisson建面算法,colmap与meshlab得到的模型结果也是有比较大的差异的:
colmap会舍弃掉一些数据不足的点,模型周围小的噪点机会就没干扰,但缺点就是会造成模型本身一些点位不够的位置的一个破面:
meshlab则会把所有点都保留,那模型周围的一些噪点也会被连接起来,生成大片毫无意义的面,还需手动去除,但优点是会把模型本身数据不足的地方还是补全取来,虽然因为数据不足补全的面很是凹凸不平,精度有很大的问题,但是不会有破面的出现:
这主要是用来做mesh建面和贴图的。需要cuda才能运行。
非常重要的注意事项:使用openmvs时,图片的后缀名一定要是小写
,可以看它源码“libs/IO/Image.cpp”中的 CImage* CImage::Create(LPCTSTR szName, IMCREATE mode)方法,然后它是不支持jpeg或者格式为大写的,可以直接改源码,改在jpg里面,参考这。
否则像大象那个数据“DSC07775.JPG”,其实没问题,但因为代码写法,就会报错“failed loading image header”,然而图片的路径确是实际存在的。如下:
这是一方面,还有一个方面是,环境有问题,导出虽然编译出来了,但是运行这里一直报这个错误,然后用它的“Dockerfile_CUDA”文件搞的新一个镜像容器就是ok的。
B站视频,openMVG+openMVS,主要参考的是这个教程。效果很一般,很多东西都出不来,(但可以看下它关于openMVS的使用)
使用:直接下载openMVG的win上编译好的版本,然后使用里面的“python tutorial_demo.py”,可以直接运行它的示例demo,如果是自己的数据集,直接在tutorial_demo.py中修改自己的数据地址,后续的openMVS的使用根据上面教程来就可。
自定义数据的话,tutorial_demo.py中要改自己的参数,要获取以像素为单位的相机焦距,这个参数一定要指定,不然openMVG_main_IncrementalSfM就没结果,就是下面的 -f,它的值算法公式:-f 1.2×max(img_w, img_h),这是官方说明。
pIntrisics = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_SfMInit_ImageListing"), "-i", input_dir, "-o", matches_dir, "-f", "2755"] )
基本可以放弃这个openMVG了。
这是大象的效果,就没效果,桃子的直接出不来:
用openMVG创建的数据,再用openMVS来处理,几乎没有效果,elephant、桃子等,都没好的效果。直接暂时放弃这。
教程地址参考。简单来说,还是colmap那一套,然后后续的减面、贴图处理用openMVS。跟超链接教程有些不同的是,colmap的sparse数据不需要转成TXT格式,.bin格式可以直接转换。
这是仅用colmap进行稀疏重建,然后拿到数据直接用openMVS进行稠密重建、mesh面化、网格细分(不是必须,但这会直接减面,效果也会更好)、贴图(这里也会导出模型,默认为ply,还有obj、glb、gltf可选)。
注:==仅使用稀疏重建,一定要指定ImageReader.camera_model为“PINHOLE”的model==。
把脚本放进colmap的“*.db”的同级目录运行就好了,最终生成的结果再“mvs”文件夹中。
# 1.特征提取
colmap feature_extractor --database_path ./colmap.db --image_path ./images --ImageReader.camera_model PINHOLE # 这里一定要指定PINHOLE,不然openMVS后面会报错“error: no valid cameras (make sure they are in PINHOLE model)”
# 2.特征匹配
colmap exhaustive_matcher --database_path ./colmap.db
# 3.稀疏重建
mkdir sparse
colmap mapper --database_path ./colmap.db --image_path ./images --output_path ./sparse
# 上面是colmap的稀疏重建,下面是用openMVS做处理了
cp ./sparse/0/*.bin ./sparse/ # 不用把.bin文件转成.txt
mkdir colmap_sparse_mvs && cd colmap_sparse_mvs
# 1.转换colmap数据为mvs数据
InterfaceCOLMAP -i .. -o scene.mvs --image-folder ../images/
# 2.稠密重建
DensifyPointCloud scene.mvs # 或 -i scene.mvs -o 自己命名.mvs
# 2.稠密重建之可以带mask掩码(背景是黑的,这里就是忽略像素为0的,还可以是其他值)
DensifyPointCloud scene.mvs --ignore-mask-label 0 --mask-path ../mask/
注:存的mask掩码图,一定都要以“.mask.png”作为结尾,一定。
# 3.mesh网格化
ReconstructMesh scene_dense.mvs # scene_dense.mvs是上一步不给-o参数默认生成的
# 4.生成精细网格(用cuda比cpu快太多了)
RefineMesh scene_dense_mesh.mvs --max-face-area 16 --cuda-device 1 # 其它都默认用cuda,这步默认是用的cpu,scene_dense_mesh.mvs是上一步不给-o参数默认生成的
# 5.贴图并导出三维模型
TextureMesh scene_dense_mesh_refine.mvs # 可以 -o 指定生成模型名字
# 5这步会直接成对应的名为 scene_dense_mesh_refine_texture.ply模型和scene_dense_mesh_refine_texture.png这贴图
# 5.这一步也可以在贴图时同时选择成成的格式,默认为ply,还可选 obj、glb、gltf
TextureMesh scene_dense_mesh_refine.mvs --export-type obj
# TextureMesh的参数“--texture-size-multiple”,从源码看了,不为0的话就return ((sizeTex+mult-1)/mult)*mult; 这个mult就是传进来的参数--texture-size-multiple,只会让贴图更大,不会让其变小,已经实验过了
以上命令的一些可选参数,可参看一下官方文档,就几个,也没参数文档,还是看源码吧。
优点:速度很快,colmap的稀疏重建很快,openMVS的速度也很快,经过测试,一个模型的处理,==能把时间控制在10分钟左右==。
TransformScene scene_dense_mesh_refine.mvs –transform-file ./trans.txt
即用colmap完成整个稀疏、稠密重建,仅使用openMVS的mesh面化,网格细分、贴图导模型等。把下面的脚本放进colmap的“*.db”的同级目录运行就好了,最终生成的结果再“mvs”文件夹中。
# 1.特征提取(这里用默认的SIMPLE_RADIAL也是可以的,并不需要像3.3.1那样指定为PINHOLE)
colmap feature_extractor --database_path ./colmap.db --image_path ./images
# 2.特征匹配
colmap exhaustive_matcher --database_path ./colmap.db
# 3.稀疏重建
mkdir sparse
colmap mapper --database_path ./colmap.db --image_path ./images --output_path ./sparse
# 4.稠密重建(这很消耗时间)
mkdir -p dense/0
colmap image_undistorter --image_path ./images --input_path ./sparse/0 --output_path ./dense/0 --output_type COLMAP --max_image_size 2000
colmap patch_match_stereo --workspace_path ./dense/0 --workspace_format COLMAP --PatchMatchStereo.geom_consistency true
colmap stereo_fusion --workspace_path ./dense/0 --workspace_format COLMAP --input_type geometric --output_path ./dense/0/fused.ply
# 上面是colmap的稀疏重建,下面是用openMVS做处理了
mkdir colmap_dense_mvs && cd colmap_dense_mvs
# 1.转换colmap数据为mvs数据
InterfaceCOLMAP -i ../dense/0/ -o scene.mvs --image-folder ../dense/0/images/
# 2.这就没有了DensifyPointCloud整个步骤
ReconstructMesh scene.mvs && RefineMesh scene_mesh.mvs --max-face-area 16 --cuda-device 1 && TextureMesh scene_mesh_refine.mvs --export-type obj # 最后导模型时可看上面
注:
3.3.1、3.3.2效果对比:
就小黄人数据来说,在–ImageReader.camera_model PINHOLE 模式下,使用colmap做了稀疏、稠密重建,然后再把这两种得到的数据用openMVS做处理,发现openMVS用colmap稀疏重建的数据得到的结果更好。
可能colmap在做稠密重建的时候把一些比较离散的点给去掉了,所以小黄人腿部缺失。同时也可以看到第一幅图(稀疏)后面的报纸空白的面比第二幅图(稠密)的更大,即在保留更多细节的时候就会留住更多噪点。
记录了一下instant-ngp使用colmap做稀疏重建的命令:
# 1.特征提取
colmap feature_extractor --ImageReader.camera_model OPENCV --ImageReader.camera_params "" --SiftExtraction.estimate_affine_shape=true --SiftExtraction.domain_size_pooling=true --ImageReader.single_camera 1 --database_path colmap.db --image_path "images"
# 2.特征匹配
colmap exhaustive_matcher --SiftMatching.guided_matching=true --database_path colmap.db
# 3.稀疏重建
mkdir sparse
colmap mapper --database_path colmap.db --image_path "images" --output_path sparse/
colmap bundle_adjuster --input_path sparse/0/ --output_path sparse/0/ --BundleAdjustment.refine_principal_point 1
colmap mapper –database_path colmap.db –image_path “images” –output_path sparse/ && colmap bundle_adjuster –input_path sparse/0/ –output_path sparse/0/ –BundleAdjustment.refine_principal_point 1 && colmap model_converter –input_path ./sparse/0 –output_path ./my_sparse.ply –output_type PLY
与2.3colmap默认命令行重建区别:
对比效果:可能不好确定,有数据的偶然性,但就这次的“礼盒数据”实验来看,同时仅使用colmap做稀疏重建,得到的数据再用openMVS做处理,最终模型的结果是:==使用colmap默认参数稀疏重建的最终效果比使用instnt-ngp的这种colmap稀疏重建的效果要好==(有的数据也一样不好)。
对比:
openMVS可以设置感兴趣区域的boundbox的坐标,还可以使用.mask掩码的方式,(后者还未探究,前者基本搞定),这个的思路来自官方的Issue。
就还是使用3.5.1的方式吧,其它的多少有点问题。
推荐这种方式:图片放在./images/123.jpg,mask放在./mask/123.mask.png (一定是“.mask.png”结尾,前面的名字是对应图片的名字)
==这是colmap仅稀疏重建,跟上面一样,然后在openMVS中加mask==。
这是把image用mask直接进行处理,然后把结果存下来,去进行重建:
把原图和mask图位或叠加后的图直接去做重建:就是这种:(去掉了背景)
分两种方式:(都是用的peach数据)
colmap也可以加mask,图片是在./images/123.png,那mask对应的就是./mask/123.png.png #即原图名(带后缀的)+”.png”,命令:
colmap feature_extractor --database_path ./colmap.db \
--image_path ./images \
--ImageReader.mask_path ./mask/ \
--ImageReader.camera_model PINHOLE
结果:怎样做都不推荐
由于colmap做出来的模型都是倒着的,需要将其摆正,现在openMVS加了旋转矩阵。
在TransformScene.cpp中,参数是“–transform-file”也可以是“-t”,我去修改了它的源码并提交了PR。
以后模型做完细分后来做模型的旋转(虽然测试过在做深度重建前就可以先执行这旋转,但还是最后来做旋转吧)
# scene_dense_mesh_refine.mvs是细分后得到的
TransformScene scene_dense_mesh_refine.mvs --transform-file ./trans.txt
其中./trans.txt中存放的变换矩阵如下:(这是绕X轴旋转180°)
1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1
计算机图形学中:齐次坐标有利于进行仿射变换,可以看这教程的变换矩阵,写得非常好。
这里把3D绕轴旋转的变换矩阵写这里:(旋转多少度,带进去α就好了) 旋转一个模型就会是:变换矩阵×[x, y, z, 1]^T^
同样的,最后生成的obj模型,或是其它的模型,要绕x轴旋转180°,可以代码直接读取模型,然后根据旋转矩阵的特性,x值不变,y值变为相反数、z值也变为相反数,可以结合3D场景进行想象。
要改空洞处的默认颜色:
TextureMesh scene_dense_mesh_refine.mvs –empty-color 16711719 # 绿色填充
openMVS默认得到的.ply模型文件可以直接导入到meshlab中做减面的处理,再导出成obj格式。
cp *.ply *.mtl *.obj *.jpg *.png ~/3d_datas/
最后放一个使用open3d进行贴图的教程,还未使用过,放这里吧。
ply转换:colmap model_converter –input_path ./sparse/0 –output_path ./my_sparse.ply –output_type PLY
不支持做贴图:https://github.com/Totoro97/NeuS/issues/4
有效果,但需要的数据预处理