note

一、概述

重建基本流程:特征提取与匹配->稀疏重建->稠密重建->表面重建。

以下很多使用来自于B站视频。(图有点问题,openMVS可以进行Dense cloud)

image-20230605092737527

了解一下VisualSFM:

官网地址。github地址。可以直接下载它的bin运行,是带GUI的,

“VisualSFM is free for personal, non-profit or academic use.”

它得到的数据格式就是 “.nvm” 的


英伟达开源的 instant-ngp 是基于nerf,它的一个数据预处理,也是用colmap实现的,主要就是用colmap的特征提取、特征匹配、去畸变,stereo(后面两个不确定有用到没),

二、colmap+meshlab

​ 主要还是参考B站这个视频;纹理贴图(meshlab实现的)知乎这篇文章,里面还带有命令行,这文章对应的也有B站视频;这两篇meshlab的贴图放这里做个参考吧,后续看能不能用到,12

​ 即:稀疏重建、稠密重建是用的colmap;mesh时可用colmap或是meshlab(这次用的clomap);最终的纹理贴图是用的meshlab。示例教程如下:

2.1 colmap安装

​ 首先要搞定的是colmap的linux环境编译,一定要带cuda、gui的编译,不带gui会使OpenGL被禁用,一些使用了OpenGL关键字的代码就会编译error,官方地址

下面是可能遇到的错误及解决办法:

2.2 GUI操作

这次使用的数据集是一组大象elephant,共有52张图。

2.2.1 colmap重建

使用colmap做第一阶段:

​ 数据准备:cd ~/project/elephant # elephant文件夹中只有一个名为“images”的文件夹,这个文件夹里有那52张大象图

​ 启动:colmap gui # 打开gui

下面就是gui中的操作:

注:是不是考虑在稠密点云结果的基础上直接做目标检测,然后删除掉一些东西,然后在目标点云上做Poisson Mesh,这样的结果会小很多,然后再在这个上面做贴图,不然噪点太多,mesh建面出来的结果是出不来的。

​ 此路径下的project.ini就是项目跑完后存下来的默认配置文件,里面很多参数可作参考。

2.2.2 meshlab贴图

注:==meshlab按住ctrl就可以拖拽模型的左右上下位置==。

​ 使用meshlab制作贴图和减面 # 参考的的主要是还是这个视频。以下的步骤是在colmap完成了泊松建面后,再在meshlab中做的贴图。可以尝试先点云配准,把目标物的稠密点云搞出来,然后再poisson-Mesh。


​ 很多时候可能用下面这种方式好一些,先稠密点云把不要的删了,再来poissonMesh(这个mesh用colmap、meshlab都行),不然有的数据干扰太多,一个通用化的参数就不好设置,得到的mesh是有问题的,就成了一团。上面也讲到过,colmap做泊松建面时出不来结果,就可能是先用meshlab将稠密点云选中目标区再反选,删除所有噪点,再mesh重建。

在meshlab中对稠密点云进行建面时,深度至少选择10,参考colmap我给的13
image-20230525151233328

一顿计算之后,就再做贴图就好了,跟上面一样,经过验证发现这样的方式也是可以。

2.3 ==命令行实现==

colmap是支持命令的行,但编译的时候还是要记得编译带gui的,colmap命令行地址

以下脚本路径中只有一个名为”images”的文件夹,里面是全部的图像。


meshlab暂时还不支持命令行,代码是QT写的,比较难剥离,去试试pymeshlab

2.4 注意事项

  1. 用colmap计算式,如果图太少或有问题,无论gui还是命令行,都不会报错,但是是得不到结果的,自动重建会有这样的提示:(只有8张图那个数据) “=> Could not register, trying another image.” “=> No good initial image pair found.”
  2. colmap feature_extractor 特征提取时,还有很多参数,instant-ngp是用的–ImageReader.camera_model OPENCV 这里会报错,还是就用默认值。 “对于大型图像,在CPU上提取SIFT特征可能会消耗每个线程大量的RAM。考虑减小最大图像大小和/或第一个八度,或者手动限制提取线程的数量。如果计算机有足够的内存支持当前设置,请忽略此”
  3. 在meshlab中对稠密点云进行了简化,然后再mesh,得到的结果,贴图在win10中打开不对,meshlab中没问题,贴图小的细节就没了,这种方式还待考虑。
  4. ma

2.5 效果必看重点

数据的影响,周围噪点问题,数据好,colmap能直接建面出来

​ 用colmap、meshlab建面的不同:colmap是没有就没有,那一块就是有缺失的,就很容易出现破面缺面,周围一些小的噪点数据就会不管;meshlab是会自己去补充,物体缺的,他补充出来就没那么好,各种凹凸不平,就是不怎么会缺面破面,同时它会把周围的一些小噪点都连接起来,形成大片没有的面,手动好去掉这些面,但是自动的难度就很大。

2.5.0 ==win查看导出的obj模型没有颜色==

用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

2.5.1 周围有很多噪点的图片数据

原始图片:周围有很多噪点: image-20230605100436904

2.5.2 干净数据与周围有噪点数据对比

原图–>colmap直接得到的稠密点云–>从未处理的稠密点云直接colmap来mesh得到的模型:

总结:干净的数据在一定程度上可直接出来好的结果,但一定程度上可以说是无需再做3D点云的一个处理,会节省很多时间和工作量。

2.5.3 colmap与meshlab建面的差别

​ 当稠密点云数据一样,且都是用Poisson建面算法,colmap与meshlab得到的模型结果也是有比较大的差异的:

三、openMVS

3.1 安装

这主要是用来做mesh建面和贴图的。需要cuda才能运行。


​ 非常重要的注意事项:使用openmvs时,图片的后缀名一定要是小写,可以看它源码“libs/IO/Image.cpp”中的 CImage* CImage::Create(LPCTSTR szName, IMCREATE mode)方法,然后它是不支持jpeg或者格式为大写的,可以直接改源码,改在jpg里面,参考

​ 否则像大象那个数据“DSC07775.JPG”,其实没问题,但因为代码写法,就会报错“failed loading image header”,然而图片的路径确是实际存在的。如下: image-20230612110529958

这是一方面,还有一个方面是,环境有问题,导出虽然编译出来了,但是运行这里一直报这个错误,然后用它的“Dockerfile_CUDA”文件搞的新一个镜像容器就是ok的。

3.2 openMVG重建

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了。

这是大象的效果,就没效果,桃子的直接出不来: image-20230612174211201

用openMVG创建的数据,再用openMVS来处理,几乎没有效果,elephant、桃子等,都没好的效果。直接暂时放弃这。

3.3 colmap重建

教程地址参考。简单来说,还是colmap那一套,然后后续的减面、贴图处理用openMVS。跟超链接教程有些不同的是,colmap的sparse数据不需要转成TXT格式,.bin格式可以直接转换。

3.3.1 colmap仅稀疏重建

​ 这是仅用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

3.3.2 colmap稠密重建

​ 即用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在做稠密重建的时候把一些比较离散的点给去掉了,所以小黄人腿部缺失。同时也可以看到第一幅图(稀疏)后面的报纸空白的面比第二幅图(稠密)的更大,即在保留更多细节的时候就会留住更多噪点。

3.3.3 与instant-ngp的预处理的对比

记录了一下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稀疏重建的效果要好==(有的数据也一样不好)。 image-20230613175330552 image-20230613175343611

对比:


3.4 openMVS的boundingbox

​ openMVS可以设置感兴趣区域的boundbox的坐标,还可以使用.mask掩码的方式,(后者还未探究,前者基本搞定),这个的思路来自官方的Issue

3.5 mask

就还是使用3.5.1的方式吧,其它的多少有点问题。

3.5.1 openMVS加mask

推荐这种方式:图片放在./images/123.jpg,mask放在./mask/123.mask.png (一定是“.mask.png”结尾,前面的名字是对应图片的名字)

==这是colmap仅稀疏重建,跟上面一样,然后在openMVS中加mask==。

3.5.2 用mask把原图直接进行处理

这是把image用mask直接进行处理,然后把结果存下来,去进行重建:

​ 把原图和mask图位或叠加后的图直接去做重建:就是这种:(去掉了背景) ​ image-20230713091915350

分两种方式:(都是用的peach数据)

3.5.3 colmap加mask

​ 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

结果:怎样做都不推荐

3.6 transfer matrix

由于colmap做出来的模型都是倒着的,需要将其摆正,现在openMVS加了旋转矩阵。

​ 在TransformScene.cpp中,参数是“–transform-file”也可以是“-t”,我去修改了它的源码并提交了PR。

​ 以后模型做完细分后来做模型的旋转(虽然测试过在做深度重建前就可以先执行这旋转,但还是最后来做旋转吧)

# scene_dense_mesh_refine.mvs是细分后得到的
TransformScene scene_dense_mesh_refine.mvs --transform-file ./trans.txt

​ 同样的,最后生成的obj模型,或是其它的模型,要绕x轴旋转180°,可以代码直接读取模型,然后根据旋转矩阵的特性,x值不变,y值变为相反数、z值也变为相反数,可以结合3D场景进行想象。

要改空洞处的默认颜色:

TextureMesh scene_dense_mesh_refine.mvs –empty-color 16711719 # 绿色填充

openMVS默认得到的.ply模型文件可以直接导入到meshlab中做减面的处理,再导出成obj格式。

colmap直接解决纯色的问题,肯定是不行的。所以考虑结合instant-ngp一起使用

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

四、Neus

不支持做贴图:https://github.com/Totoro97/NeuS/issues/4

有效果,但需要的数据预处理