1. SFM
Structure from Motion (SfM):从运动中恢复结构,通俗地讲:从序列图像中重建目标三维结构,并估计相机及空间参数。
用于SfM的主要有三个库:
SfM输出的是稀疏点云以及相机参数,这些信息将用于后续的稠密重建。
2. MVS
MultiView Stereo(MVS):多视点匹配,用于生成稠密点云。
通常MVS能计匹配到更多的点,生成更稠密的点云,做到这一点需要用到对极几何的原理:一副图像上的一个像素点是空间上一条线(相机视点和该像素连起来的直线)的投影,而这条线在另外一幅图像上往往会投影为一条线。SFM需要在整个二维图像上搜索匹配点,而MVS只需要在一条线上搜索。
以上是PMVS2做的事,做到这些需要大量复杂的计算,CMVS就是用于简化此的。CMVS把SfM输出的聚类为区域,PMVS2将在这些“区域”上匹配,最后CMVS将这些区域映射为三维模型。
关于二者的联系和区别
3. bundler
3.1 配置
bundler是经典的单目重建公开库之一,很多工作都是在此基础上进行的,其运行过程大致分为三步:
使用Perl脚本
extract_focal.pl
提取图像焦距信息并存储到image list
中;在每幅图像上寻找SIFT特征点;
在各个图像中匹配特征点,匹配的特征存储在
matches.init.txt
中;运行bundler,生成点云和相机参数;
-
配置(Windows):
配置的大致步骤是:下载bundler源码,Windows下安装cygwin,安装相关依赖,编译和运行。其中编译有两种方式:make
命令和VS编译,如果是在VS编译编译可参考:issue 26。
3.2 输出
格式:
bundle_<n>.out
;包含内容:场景估计和相机几何参数;
-
具体:
版本信息:
# Bundle file v0.3
;<num_cameras> <num_points>:两个整数,分别表示相机总数(其实就是输入图像的数量)和匹配到的点数;
接下来依次是每个相机的内参和外参信息;
-
内参:
<f> <k1> <k2>:焦距,两个径向畸变参数;
-
外参:
<R>:表示相机旋转的矩阵,3X3;
<T>:相机平移矩阵,1X3;
-
匹配点:
-
<position>:点空间位置的描述,1X3;
<color>:RGB信息,1X3;
<view list>:描述点可见性的向量,包括 view list 长度,<camera>第几个相机,<key>表示该相机中第几个sift特征点,<x>和<y>表示在二维图像上的坐标(以图像中心的原点);
-
3.3 选项
options.txt默认包含的选项
--match_table
: 指定点匹配信息存储的文件,默认:matches.init.txt
;--output
: 指定包含最终计算结果的文件名称,默认:bundle.out
;--output_all
:指定中间结果存储的前缀,默认:bundle_
;--output_dir
: 指定计算结果的保存目录,默认:bundle
;--variable_focal_length
:为指定每张图片指定独立的焦距,默认:无(源码中为Option
结构);--use_focal_estimate
:指定是否使用从EXIF中提取的焦距信息,默认:true
;--constrain_focal
:指定计算出的相机焦距是否被初始焦距(从EXIF中提取)所约束,默认:true
;--constrain_focal_weight
: 指定焦距约束条件的权重,通常一个较小的数可矣,默认:0.0001
;--estimate_distortion
:指定是否为每一张图片估计畸变参数,默认:true
;--ray_angle_threshold
: 默认:2.0;--run_bundle
:指定是否运行SFM,默认:true
;
其它选项
--init_pair1
,--init_pair2
:指定初始匹配的两张图像,通常由程序自动选择匹配点最多的一对图像,如果效果不好再启用此选项; ---sift_binary
: 指定计算SIFT特征的外部接口,一般是/usr/bin/sift
or/cygdrive/c/usr/bin/siftWin32.exe
;--add_images
: 在已有重建基础上额外添加新图像时使用此选项;options_file
:指定bundler参数文件,默认:options.txt
;--help
:输出所有的选项信息;
4. PMVS2
PMVS是 Yasutaka Furukawa开发的 Patch-based Multi-view Stereo Software(PMVS)——分段式多视角匹配软件(目前是第二版)。PMVS的输入为一组图像和相应的相机参数,输出是“半稠密”的点云。需要注意的是,PMVS只重建刚性机构的目标,自动忽略行人等柔性目标。关于MVS有一个跑分排行。
4.1 输入
images:要求
jpeg
或者ppm
格式,命名必须是8位(兼容4位)的数字:%08d.jpg
;camera parameters:命名格式与图像命名相同,内容含义:
-------------------------------------------
CONTOUR //固定的header
//P[3][4]表示投影矩阵(三维点和投影矩阵相乘得到二维坐标)
P[0][0] P[0][1] P[0][2] P[0][3]
P[1][0] P[1][1] P[1][2] P[1][3]
P[2][0] P[2][1] P[2][2] P[2][3]
-------------------------------------------
segmentation masks:以
pgm
格式文件给定,灰度小于127是背景,否则为前景;-
option file:指定使用的参数文件,具体选项如下:
timages:指定目标图像(必选项),可以用列举(
timages 5 1 3 5 7 9
)或者范围(timages -1 0 6
)的方式指定;oimages:other images(必选项),用于指定那些图片能输出重建结果(重建结果会等所有timages运算完输出,但是通过oimages指定的图像会在中间过程输出),指定方式和timages相同,如果不想用此选项,指定为0;
level:指定图像金字塔(降采样的层次)的高度,指定1(默认)表示降采样一半(宽高缩小一倍,总像素量为原来的四分之一),指定为0表示不进行降采样;
csize:指定重建的最小区块,用于控制重建结果的密度,默认为2(重建中每 2X2 个像素生成一个点云中的点);
threshold:区块重建可接受的最小阈值(默认0.7),总的范围是-1到1,算法有三次迭代,每次迭代自动减0.05;
wsize:指定光度一致性计算的视口大小,默认为7;
minImageNum:一个3D点必须至少在minImageNum张图像中可见才会被输出,默认为3(必须同时出现在三张以上的图像中才会被输出,选项应该和纹理密度成反比);
CPU:程序支持多线程,CPU表示线程数,默认为4;
useVisData:指定是否利用已知(从SFM计算而来)的图像关联信息加速重建过程,程序会利用SFM的输出生成
vis.dat
文件生成PMVS需要的格式(0 2 1 2
:0的含义自定义,2表示后面共2个数,1和2表示相应的图像序列,如果这是第一行则表示,图像0和图像2、图像2将聚合到一块儿以重建点云)。默认为0表示不使用此信息,使用时设置为1;sequence:用于序列化图像,默认为-1,表示不使用此选项,如果为3则表示当前图像的前后各3个图像将用于重建;
quad:重建的点周围的点中,与之相似的越多则该点越不可能被滤除。通常此选项不需要调节,可省略;
maxAngle:当两个相机的夹角大于此阈值则不被重建,减小maxAngle将允许更大范围的目标被重建出来,同时噪点也更多;
4.2 输出
输出包含三种格式的文件:
.ply
:点云文件咯;.patch
:包含所有重建信息:以PATCHS开头,包含所有重建点的信息
PATCHES
452393 //共452393个点
PATCHS
-1.20727 -0.718245 -7.1088 1 //三维坐标
-0.0750093 -0.981341 -0.177041 0 //估计的法线
0.992487 0.0207491 0.385701 //第一个数表示光度一致性测度,后两个供debug
3 //该点在3张图像中可见且纹理匹配良好
2 0 1 //三张图像的index
15 //该点在15张图像中可见,但是纹理匹配不够好
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //这15张图像的index
.pset
:简化版的重建信息,包括点坐标和法线信息;
因为最新的CMVS包含了PMVS2,而且我使用的Windows也不好编译,所以就不运行了。
5. CMVS
Clustering Views for Multi-view Stereo (CMVS)也是 Yasutaka Furukawa (博士后时期)写的,它包含了PMVS2的内容,提升的地方在于:更高的性能,更好的效果。CMVS将SFM的输出分成一个个小的图片簇,然后独立并行的重建。几者的关系的关系:
Bundler->CMVS->PMVS2
5.1 输出
ske.dat:包含集群信息;
vis.dat:可见性信息;
centers-%04d.ply:各个图片簇的相机位置;
centers-all.ply:所有的相机位置信息;
option-%04d:PMVS2配置文件;
pmvs.sh or pmvs.bat:用于执行PMVS2;
5.2 编译(Windows & VS2013)
这里以 Pierre Moulon 开发的Windows版本 CMVS + genOption + PMVS2 为例说明(链接,github)。
首先,代码中包含自带的已经编译好的程序,目录:./binariesWin-Linux/Win64-VS2010
。以下是自己编译的过程:
使用CMake生成VS工程,CMake文件是
./program/CMakeLists.txt
,打开CMake GUI,选择源码路径为program,配置好,点击生成,最后会生成一个叫做CMVS-PMVS2.sln
的VS工程。-
打开工程,使用VS编译,当然,一般都会报几个错误:
命令行 error D8016: “/O1”和“/RTC1”命令行选项不兼容:将 项目->C/C++->代码生成->基本运行时检查设置为default,问题解决(参考)。
解决了这个问题之后就没有遇到其他问题了!接下来就是将要用到的输出copy出来运行(在main的Debug目录下)。
5.3 运行
运行bundler:cd到example下的ET或者kermit目录下,运行
../../RunBundler.sh
,参数默认的就行,成功后会生成./bundle
目录,成功的话会有 bundle_XXX.out 和 pointsXXX.ply 文件;-
转换bundler输出为PMVS所需格式:cd 到 kermit 目录下,运行:
../../bin/Bundle2PMVS.exe prepare/list.txt bundle/bundle.out
,得到结果如下(可见的结果就是生成了一个pmvs的目录):[ReadBundleFile] Bundle version: 0.300 [ReadBundleFile] Reading 11 images and 671 points... [GetJPEGDimensions] File ./kermit000.jpg: ( 640 , 480 ) ...... @@ Conversion complete, execute "sh pmvs/prep_pmvs.sh" to finalize @@ (you will first need to edit prep_pmvs.sh to specify your bundler path, @@ so that the script knows where to find your @@ RadialUndistort and Bundle2Vis binaries)
-
运行pmvs目录下的
prep_pmvs.sh
校正和生成vis.dat文件:先修改prep_pmvs.sh
中BUNDLER_BIN_PATH
的值,我是在kermit路径下运行的,所以改为:"../../bin"
。> 这里遇到一个bug:提示无法找到“XXXXX.rd.jpg”,实际pmvs目录下生成的图像不带“rd”。看了哈源代码,是**RadialUndistort.cpp**中出的问题,大概是C字符串和string转换造成的吧,将`file[i].rfint('.')`改成`file[i].fint('.',1)`,**file[i]**为list.txt下的一行,后面带的数字中有小数点,导致源代码提取basename出错。
执行CMVS:先执行
CMVS prefix 20 2
,这里已经到了cmvs的部分了,需要先把编译好的三个文件copy过来(cmvs.exe
,genOption.exe
,pmvs2.exe
),参数的含义可以看cmvs自带的release路径下的readme。生成pmvs的参数文件:经过cmvs之后,原来的图片被分成一个个的图片簇(如果你的图片较少则只会有一个簇),所以相应的pmvs参数也要改变,这正是这一步的意义。命令:
genOption path
,这里的path建议和上一步的path一致。生成稠密点云:最后执行
pmvs.bat
生成稠密点云,运行前可能需要修改路径。
5.4 关于路径
我是在Windows下运行的,所有用到的路径都是相对方式给定,如果运行指令没有得到正确结果,请着重检查你的路径!总结起来就是一句话:
正确的路径(相对) + 正确的参数(参数中的路径也是相对于执行命令所在目录的)
遇到问题可以这样做:
仔细查看readme -> 到github或者官网看issue和文档 -> google或者问答社区 -> 源代码(有基础可以直接看源码)。
我的codepen:链接
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。