Learn SFM

方泉水很甜發表於2019-05-09

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:連結

相關文章