天天看點

裡程計計算旋轉角度_DSO源碼解讀(二):前端裡程計

話不多說,直接上流程圖吧,一圖勝千言(這張圖是高翔博士部落格裡的,連結:https://zhuanlan.zhihu.com/p/29177540)

裡程計計算旋轉角度_DSO源碼解讀(二):前端裡程計

有了這張圖,我們就對前端裡程計有了清晰的整體認識,下面我們看具體的流程。

DSO的前端和後端在檔案結構上分的比較清楚,前端裡程計全在檔案夾src/FullSystem中,其中裡程計的主流程管理在FullSystem.cpp中,管理主流程的函數是addActiveFrame。

(下面介紹流程的部分内容來自部落格https://blog.csdn.net/gbz3300255/article/details/90511237)

1. 初始化

初始化需要有兩幀。

1.1 第一幀。

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

1.2 第二幀

第二幀依然交由 CoarseInitializer。

CoarseInitializer::trackFrame

處理完成之後,在

FullSystem::initializerFromInitializer

中為第一幀生成 pointHessians,一共2000個左右。随後将第二幀作為 KeyFrame 輸入到 FullSystem::deliverTrackedFrame,最終流入 FullSystem::makeKeyFrame。

1)CoarseInitializer::trackFrame

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

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

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 相當于估計的真值,用于計算誤差。

注意這裡已經将第一幀加入到 EnergyFunctional 的幀中,為後面的優化做準備。搜尋 ef 變量就能看到這個操作。

2. 計算新幀位姿

主要在函數FullSystem::trackNewCoarse中。

  • coarseTracker->lastRef 中存儲了最新關鍵幀,allFrameHistory 存儲了所有幀的位姿。按照 1 倍,2倍,0.5倍,0 倍速度的假設,構造目前幀的位姿假設。這些位姿的假設都來源于前面兩幀與關鍵幀兩兩之間的相對位姿和關鍵幀的絕對位姿。而這些假設中最重要的是,目前幀到前一幀的相對位姿等于前一幀到前前一幀的相對位姿,之是以說這個重要,是因為這樣後面在這樣計算出來的目前幀位姿上,進行了 a TON of 旋轉作為假設,加入到總的假設(lastF_2_fh_tries)中。
  • 進行好這些假設之後,就從第一個假設開始,用 CoarseTracker::trackNewestCoarse 與最新關鍵幀比對。依舊是高斯牛頓優化,而這個優化隻優化相兩幀的相對狀态(相對位姿 6 + 光度仿射變換 2)。當然也不是所有的假設都需要優化一遍,目前假設得到的結果與前面假設得到的結果比較,目前結果與前面一幀比對的結果比較(這個跨度有點大,是上一幀),滿足條件就可以跳出了。
3. 判斷是否需要新增為關鍵幀,并執行相應操作

判斷結果無非兩種,是或不是

3.1 普通幀操作
  • 非關鍵幀的作用是更新視窗中關鍵幀 immaturePoints 的逆深度,FullSystem::makeNonKeyFrame 調用 FullSystem::traceNewCoarse 做這件事情。對于每一個 ImmaturePoint 是在 ImmaturePoint::traceOn 中完成的。
  • ImmaturePoint::traceOn 這個函數是将關鍵幀上的點與非關鍵幀影像進行比對,得到在非關鍵幀影像上的位置,知道這個資訊就可以更新深度了。先進行極線搜尋,搜尋得到一個比較好的值,再使用高斯牛頓優化。
  • 最後更新了 ImmaturePoint::idepth_min 和 ImmaturePoint::idepth_max,對于 immaturePoint 僅僅隻有一個 idepth 的範圍,沒有确切的 idepth。
3.2 關鍵幀操作
  • 一開始的套路和非關鍵幀一樣,調用 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 加入到視窗優化過程中。