DSO 代碼框架


從數據流的角度講一遍 DSO 代碼框架。

DSO 的入口是 FullSystem::addActiveFrame,輸入的影像生成 FrameHessian 和 FrameShell 的 Object,FrameShell 是 FrameHessian 的成員變量,FrameHessian 保存影像信息,FrameShell 保存幀的位置姿態信息。代碼中一般用 fh 指針變量指向當前幀的 FrameHessian。在處理完成當前幀之后,會刪除 FrameHessian,而保存 FrameShell 在變量 allFrameHistory 中,作為最后整條軌跡的輸出。

其實,輸入影像在 main_dso_pangolin.cpp 中已經處理,影像值不是原始的灰度值,而是輻射值,這些輻射值的范圍依舊是 [0, 255],float 型。(如果有進行輻射標定。)

數據預處理部分是在 FullSystem::addActiveFrame 中調用的 FrameHessian::makeImages,這個函數為當前幀的影像建立影像金字塔,並且計算每一層影像的梯度。這些計算結果都存儲在 FrameHessian 的成員變量中,1. dIp 每一層影像的輻射值、x 方向梯度、y 方向梯度;2. dI 指向 dIp[0] 也就是原始影像的信息;3. absSquaredGrad 存儲 xy 方向梯度值的平方和。

1. 第一幀

進入 FullSystem::addActiveFrame,首先判斷是否完成了初始化,如果沒有完成初始化,就將當前幀 fh 輸入 CoarseInitializer::setFirst 中。完成改函數之后,退出,接着處理下一幀。

CoarseInitializer::setFirst,在影像的每一層選取點,作為后續第二幀匹配生成 pointHessians 和 immaturePoints 的候選點,這些點存儲在 CoarseInitializer::points 中。每一層點之間都有聯系,在 CoarseInitializer::makeNN 中計算每個點最鄰近的10個點 neighbours,在上一層的最鄰近點 parent。

pointHessians 是成熟點,具有逆深度信息的點,能夠在其他影像追蹤到的點。immaturePoints 是未成熟點,需要使用非關鍵幀的影像對它的逆深度進行優化,在使用關鍵幀將它轉換成 pointHessians,並且加入到窗口優化。

2. 第二幀

初始化需要有兩幀,所以第二幀依然交由 CoarseInitializer。CoarseInitializer::trackFrame 處理完成之后,在 FullSystem::initializerFromInitializer 中為第一幀生成 pointHessians,一共2000個左右。隨后將第二幀作為 KeyFrame 輸入到 FullSystem::deliverTrackedFrame,最終流入 FullSystem::makeKeyFrame。(FullSystem::deliverTrackedFrame 的作用就是實現多線程的數據輸入。)

2.1 CoarseInitializer::trackFrame

CoarseInitializer::trackFrame 中將所有 points (第一幀上的點)的逆深度初始化為1。從金字塔最高層到最底層依次匹配,每一層的匹配都是高斯牛頓優化過程,在 CoarseIntializer::calcResAndGS 中計算Hessian矩陣等信息,計算出來的結果在 CoarseInitializer::trackFrame 中更新相對位姿(存儲在局部變量中,現在還沒有確定要不要接受這一步優化),在 CoarseInitializer::trackFrame 中調用 CoarseInitializer::doStep 中更新點的逆深度信息。隨后再調用一次 CoarseIntializer::calcResAndGS,計算新的能量,如果新能量更低,那么就接受這一步優化,在 CoarseInitializer::applyStep 中生效前面保存的優化結果。

一些加速優化過程的操作:1. 每一層匹配開始的時候,調用一次 CoarseInitializer::propagateDown,將當前層所有點的逆深度設置為的它們 parent (上一層)的逆深度;2. 在每次接受優化結果,更新每個點的逆深度,調用一次 CoarseInitializer::optReg 將所有點的 iR 設置為其 neighbour 逆深度的中位數,其實這個函數在 CoarseInitializer::propagateDown 和 CoarseInitializer::propagateUp 中都有調用,iR 變量相當於是逆深度的真值,在優化的過程中,使用這個值計算逆深度誤差,效果是幅面中的逆深度平滑。

優化過程中的 lambda 和點的逆深度有關系,起一個加權的作用,也不是很明白對 lambda 增減的操作。在完成所有層的優化之后,進行 CoarseInitializer::propagateUp 操作,使用低一層點的逆深度更新其高一層點 parent 的逆深度,這個更新是基於 iR 的,使得逆深度平滑。高層的點逆深度,在后續的操作中,沒有使用到,所以這一步操作我認為是無用的。

2.2 FullSystem::initializeFromInitializer

FullSystem::initializeFromInitializer,第一幀是 firstFrame,第二幀是 newFrame,從 CoarseInitializer 中抽取出 2000 個點作為 firstFrame 的 pointHessians。設置的逆深度有 CoarseIntiailzier::trackFrame 中計算出來的 iR 和 idepth,而這里使用了 rescaleFactor 這個局部變量,保證所有 iR 的均值為 1。iR 設置的是 PointHessian 的 idepth,而 idepth 設置的是 PointHessian 的 idepth_zero,idepth_zero 相當於估計的真值,用於計算誤差。(這里和 CoarseInitializer 對 idepth 和 iR 的定義是相反的,我再想想。)

注意這里已經將第一幀加入到 EnergyFunctional 的幀中,為后面的優化做准備。搜索 ef 變量就能看到這個操作。

3. 第 3 4 5 6 7 8 ... 幀

后面幀的流程就是先使用 FullSystem::trackNewCoarse 將當前幀與上一個關鍵幀進行匹配,得到初始位姿,隨后判斷當前幀是否需要成為關鍵幀,並輸入到 FullSystem::deliverTrackedFrame,最終輸入到 FullSystem::makeKeyFrame 或 FullSystem::makeNonKeyFrame 中。

3.1 FullSystem::trackNewCoarse

coarseTracker->lastRef 中存儲了最新關鍵幀,allFrameHistory 存儲了所有幀的位姿。按照 1 倍,2倍,0.5倍,0 倍速度的假設,構造當前幀的位姿假設。這些位姿的假設都來源於前面兩幀與關鍵幀兩兩之間的相對位姿和關鍵幀的絕對位姿。而這些假設中最重要的是,當前幀到前一幀的相對位姿等於前一幀到前前一幀的相對位姿,之所以說這個重要,是因為這樣后面在這樣計算出來的當前幀位姿上,進行了 a TON of 旋轉作為假設,加入到總的假設(lastF_2_fh_tries)中。

進行好這些假設之后,就從第一個假設開始,用 CoarseTracker::trackNewestCoarse 與最新關鍵幀匹配。依舊是高斯牛頓優化,而這個優化只優化相兩幀的相對狀態(相對位姿 6 + 光度仿射變換 2)。當然也不是所有的假設都需要優化一遍,當前假設得到的結果與前面假設得到的結果比較,當前結果與前面一幀匹配的結果比較(這個跨度有點大,是上一幀),滿足條件就可以跳出了。

3.2 FullSystem::makeNonKeyFrame

非關鍵幀的作用是更新窗口中關鍵幀 immaturePoints 的逆深度,FullSystem::makeNonKeyFrame 調用 FullSystem::traceNewCoarse 做這件事情。對於每一個 ImmaturePoint 是在 ImmaturePoint::traceOn 中完成的。

3.2.1 ImmaturePoint::traceOn

ImmaturePoint::traceOn 這個函數是將關鍵幀上的點與非關鍵幀影像進行匹配,得到在非關鍵幀影像上的位置,知道這個信息就可以更新深度了。先進行極線搜索,搜索得到一個比較好的值,再使用高斯牛頓優化。

最后更新了 ImmaturePoint::idepth_min 和 ImmaturePoint::idepth_max,對於 immaturePoint 僅僅只有一個 idepth 的范圍,沒有確切的 idepth。

3.3 FullSystem::makeKeyFrame

一開始的套路和非關鍵幀一樣,調用 FullSystem::traceNewCoarse。

調用 FullSystem::flagFramesForMarginalization 標記需要被 marg 掉的幀,這些幀在 frameHessians 這個 std::vector 中存儲方式是越老的幀越前。

遍歷窗口中所有幀的 pointHessians,建立它們鏈接自己 hostframe 與當前幀的 PointFrameResidual,加入到 EnergyFunctional 中。

調用 FullSystem::activatePointsMT 遍歷窗口中所有幀的 immaturePoints,如果可以投影到的當前幀上,那么再試一試這些幀能不能投影到窗口中其他幀上去,找到所有能夠投影的鏈接,形成 PointFrameresisual。(FullSystem::optimizeImmaturePoint)為了將這些點投影到盡可能多的幀上去,也是進行了高斯牛頓優化它的逆深度。這里的逆深度就是 immaturePoint 的 idepth_max 和 idepth_min,在 ImmaturePoint::traceOn 中計算得到的。

接下來就是調用 FullSystem::optimize 窗口優化。窗口優化完,就是 marg 掉不需要的幀和點。

做完這些操作之后,有一個重要的操作是 FullSystem::makeNewTraces 在當前關鍵幀提取 immaturePoints,這樣下一個關鍵幀處理的時候可以生成 pointHessians 加入到窗口優化過程中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM