Learn SFM

方泉水很甜

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是经典的单目重建公开库之一,很多工作都是在此基础上进行的,其运行过程大致分为三步:

  1. 使用Perl脚本 extract_focal.pl 提取图像焦距信息并存储到 image list 中;

  2. 在每幅图像上寻找SIFT特征点;

  3. 在各个图像中匹配特征点,匹配的特征存储在 matches.init.txt 中;

  4. 运行bundler,生成点云和相机参数;

配置的大致步骤是:下载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 输出

输出包含三种格式的文件:

  1. .ply:点云文件咯;

  2. .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
  1. .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 运行

  1. 运行bundler:cd到example下的ET或者kermit目录下,运行 ../../RunBundler.sh,参数默认的就行,成功后会生成./bundle目录,成功的话会有 bundle_XXX.out 和 pointsXXX.ply 文件;

  2. 转换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)
  3. 运行pmvs目录下的prep_pmvs.sh校正和生成vis.dat文件:先修改prep_pmvs.shBUNDLER_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出错。
    
  4. 执行CMVS:先执行CMVS prefix 20 2,这里已经到了cmvs的部分了,需要先把编译好的三个文件copy过来(cmvs.exe,genOption.exe,pmvs2.exe),参数的含义可以看cmvs自带的release路径下的readme。

  5. 生成pmvs的参数文件:经过cmvs之后,原来的图片被分成一个个的图片簇(如果你的图片较少则只会有一个簇),所以相应的pmvs参数也要改变,这正是这一步的意义。命令:genOption path,这里的path建议和上一步的path一致。

  6. 生成稠密点云:最后执行pmvs.bat生成稠密点云,运行前可能需要修改路径。

5.4 关于路径

我是在Windows下运行的,所有用到的路径都是相对方式给定,如果运行指令没有得到正确结果,请着重检查你的路径!总结起来就是一句话:

正确的路径(相对) + 正确的参数(参数中的路径也是相对于执行命令所在目录的)

遇到问题可以这样做:

仔细查看readme -> 到github或者官网看issue和文档 -> google或者问答社区 -> 源代码(有基础可以直接看源码)。

我的codepen:链接

阅读 7.6k

不急不躁,不卑不亢,生活的模样。

291 声望
27 粉丝
0 条评论
你知道吗?

不急不躁,不卑不亢,生活的模样。

291 声望
27 粉丝
宣传栏