Structure from Motion (SfM):從運動中恢復結構,通俗地講:從序列圖像中重建目標三維結構,並估計相機及空間參數。html
用於SfM的主要有三個庫:git
SfM輸出的是稀疏點雲以及相機參數,這些信息將用於後續的稠密重建。app
MultiView Stereo(MVS):多視點匹配,用於生成稠密點雲。post
一般MVS能計匹配到更多的點,生成更稠密的點雲,作到這一點須要用到對極幾何的原理:一副圖像上的一個像素點是空間上一條線(相機視點和該像素連起來的直線)的投影,而這條線在另一幅圖像上每每會投影爲一條線。SFM須要在整個二維圖像上搜索匹配點,而MVS只須要在一條線上搜索。
以上是PMVS2作的事,作到這些須要大量複雜的計算,CMVS就是用於簡化此的。CMVS把SfM輸出的聚類爲區域,PMVS2將在這些「區域」上匹配,最後CMVS將這些區域映射爲三維模型。
關於兩者的聯繫和區別性能
bundler是經典的單目重建公開庫之一,不少工做都是在此基礎上進行的,其運行過程大體分爲三步:google
使用Perl腳本 extract_focal.pl
提取圖像焦距信息並存儲到 image list
中;命令行
在每幅圖像上尋找SIFT特徵點;
在各個圖像中匹配特徵點,匹配的特徵存儲在 matches.init.txt
中;
運行bundler,生成點雲和相機參數;
配置(Windows):
配置的大體步驟是:下載bundler源碼,Windows下安裝cygwin,安裝相關依賴,編譯和運行。其中編譯有兩種方式:make
命令和VS編譯,若是是在VS編譯編譯可參考:issue 26。
格式: 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>表示在二維圖像上的座標(以圖像中心的原點);
--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
:輸出全部的選項信息;
PMVS是 Yasutaka Furukawa開發的 Patch-based Multi-view Stereo Software(PMVS)——分段式多視角匹配軟件(目前是第二版)。PMVS的輸入爲一組圖像和相應的相機參數,輸出是「半稠密」的點雲。須要注意的是,PMVS只重建剛性機構的目標,自動忽略行人等柔性目標。關於MVS有一個跑分排行。
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將聚合到一起以重建點雲)。默認爲0表示不使用此信息,使用時設置爲1;
sequence:用於序列化圖像,默認爲-1,表示不使用此選項,若是爲3則表示當前圖像的先後各3個圖像將用於重建;
quad:重建的點周圍的點中,與之類似的越多則該點越不可能被濾除。一般此選項不須要調節,可省略;
maxAngle:當兩個相機的夾角大於此閾值則不被重建,減少maxAngle將容許更大範圍的目標被重建出來,同時噪點也更多;
輸出包含三種格式的文件:
.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也很差編譯,因此就不運行了。
Clustering Views for Multi-view Stereo (CMVS)也是 Yasutaka Furukawa (博士後時期)寫的,它包含了PMVS2的內容,提高的地方在於:更高的性能,更好的效果。CMVS將SFM的輸出分紅一個個小的圖片簇,而後獨立並行的重建。幾者的關係的關係:
Bundler->CMVS->PMVS2
ske.dat:包含集羣信息;
vis.dat:可見性信息;
centers-%04d.ply:各個圖片簇的相機位置;
centers-all.ply:全部的相機位置信息;
option-%04d:PMVS2配置文件;
pmvs.sh or pmvs.bat:用於執行PMVS2;
這裏以 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目錄下)。
運行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
生成稠密點雲,運行前可能須要修改路徑。
我是在Windows下運行的,全部用到的路徑都是相對方式給定,若是運行指令沒有獲得正確結果,請着重檢查你的路徑!總結起來就是一句話:
正確的路徑(相對) + 正確的參數(參數中的路徑也是相對於執行命令所在目錄的)
遇到問題能夠這樣作:
仔細查看readme -> 到github或者官網看issue和文檔 -> google或者問答社區 -> 源代碼(有基礎能夠直接看源碼)。
個人codepen:連接