嗨,各位讀者朋友們好!今天我們接着講DSO全家桶的第二講——提取梯度點。
首先,我們會先介紹一下DSO中從圖像讀取到提取梯度點的整個流程,然后再對重點的部分——“提取梯度點”進行講解,讓大家對整個流程的邏輯有個較為清晰的認識。這樣寫的目的主要是讓大家在后續自己實現代碼的時候,可以有目標地寫測試用例。
圖1. 圖像預處理到提取梯度點流程圖
OK,我們開始吧!如圖1所示,我們將簡單闡述一下每一步的操作。
1. 根據輸入的標定文件確定圖像的原始尺寸和裁剪尺寸,並且甚至圖像的路徑。若有光度標定[1]文件,還需要設置光度標定參數;
2. 在imageRW_OpenCV.cpp[2]中采用cv::imread[3]函數讀取指定序號的圖像,並拷貝到指定數據類型MinimalImageB中;
3. 若事先進行了光度標定,DSO會對輸入圖像先進行光度校正;
4. 對原始圖像進行去畸變[4],並對輸出圖像進行高斯濾波,目標應該是使得圖像平滑一些;
5. 構建圖像金字塔,並逐層逐像素計算梯度值以及梯度值的平方和;
6. 對圖像金字塔第0層(即原始圖像)提取梯度點;
7. 對於圖像金字塔第1-5層,設置固定大小的網格,在每個網格中根據梯度值大小選擇四類點;
8. 對步驟6-7選擇的點進行初始化,包括坐標、深度值和閾值等。
上述步驟即是DSO從輸入圖像開始一直到完成提取梯度點的完整流程。但是我們這里並不會詳細介紹每一個步驟,只挑比較重要的部分進行講解,即上述的步驟6和步驟7。實際上,在自己實現代碼的時候,可以借助OpenCV等工具完成圖像讀取、濾波和構建金字塔等操作,因此我們並不會花大篇幅介紹這些內容。
DSO前端:提取梯度點
DSO中對點的提取策略分為兩類:
1. 原始圖像層的梯度點提取,即圖像金字塔的第0層:由小到大的比較每一個像素的梯度值與閾值,找出滿足要求的點;
2. 圖像金字塔第1-5層的梯度點選擇:在每個固定大小的網格中,提取四類點。
接下來,我們對這兩個步驟分別進行介紹。
圖像金字塔第0層
首先我們需要先設置密度權重,因為整張圖像的像素多到嚇人,為了秉承DSO中的Sparse,DSO將第0層的密度權重設置為0.03,即需要提取的目標點數是0.03*width*height。
處理圖像金字塔第0層的函數是PixelSelector::makeMaps(...),表示從無到有的過程。為了更加直觀地顯示整個過程,筆者引用了泡泡機器人SLAM龔益群同學發布在【泡泡讀者來稿】的作品:DSO代碼解讀[5]中的一些圖示進行介紹。
圖2. 梯度直方圖
1. 如圖2所示,首先需要對每個32*32的網格創建梯度直方圖,在最開始介紹系統流程時,我們提到過需要對圖像計算梯度值以及梯度的平方和(dx2+dy2)。而在這里,就是利用梯度平方和統計直方圖(梯度直方圖的橫軸為梯度值,縱軸為像素個數),統計該網格中同一梯度值的像素個數;
2. 在梯度直方圖中找到處於中間(即中位數)的梯度值,作為閾值;
3. 在步驟2中我們計算了每個32*32的網格中位數的梯度值,對閾值矩陣進行3*3窗口的均值濾波,得到閾值矩陣;
圖3. 圖像金字塔第0層梯度點篩選策略
4. 如圖3所示,DSO中采用了四級網格策略,其中藍色網格是16*16,綠色網格是8*8,紅色網格是4*4,最內層是1*1,並且彼此之間是包含關系;
5. 在提取梯度點的時候需要用到我們步驟3計算得到的閾值矩陣,通過由內到外比較每個點的梯度值與閾值的大小,確定提取到的點。這里有幾個值得注意的地方,第一是閾值的選取:
假設閾值是th,
在1*1的窗口時,閾值被設置為2*th;
在4*4的窗口中,閾值被設置為th;
在8*8的窗口中,閾值被設置為0.75*th;
在16*16的窗口中,閾值被設置為0.75*0.75*th。
由於DSO中采取的策略是,先從最小的網格開始提取梯度點,在確保梯度值大於閾值的前提下找出網格中梯度值最大的點。若不滿足閾值條件,則進入更大的網格進行篩選。而更大的網格都是從小的網格組合得到的,若是閾值不改變,仍然是無法提取到點的。因此,DSO才有這種漸變式的梯度閾值條件。
值得注意的是,DSO中的起始網格大小並不是圖示的4*4,而是3*4。當且僅當第一次提取的點數量不滿足要求時,才有可能改變為2*4或者4*4。DSO中采用0.25作為分界線:
當quotia = numWant / numHave > 1.25,表示提取到的點遠小於期望值,需要減小窗口大小以獲得更多的點,這時窗口會變成2*4;
當quotia = numWant / numHave < 0.25,表示提取到的點遠大於期望值,需要增大窗口大小以獲得更少的點,這時窗口會變成4*4。
圖像金字塔第1-5層
不同於第0層,第1-5層的梯度點篩選是通過在固定網格中計算四類點的最大值來確定的。
圖4. 圖像金字塔第1-5層梯度點篩選策略
1. 首先需要確定的是,DSO預設的網格大小為5*5;
2. 如圖4所示,emmmm....筆者純粹只是為了避免去畫圖(偷懶),姑且這里就按4*4算好了。DSO會在每個4*4的網格中逐個像素地計算梯度值sqrt(dx2+dy2);
3. 整體的閾值條件被設置為th = 1 * 10 *0.75 = 7.5;
4. 后續我們需要對網格中的所有大於閾值th的像素挨個比較gradX / gradY / gradX - gradY / gradX + gradY的大小,選擇該網格中四類條件中最大的四個點。
總結
至此,第二講——提取梯度點,我們就已經講完了。我們在開頭介紹了DSO從輸入流進來到完成提取梯度點的整個流程,可以幫助讀者更快地建立起寫一個測試用例應該要考慮的東西,同時也是直接給讀者介紹了DSO在圖像預處理這塊兒做了哪些功夫。后面我們詳細介紹了DSO是如何提取梯度點的,主要還是區分圖像金字塔第0層和第1-5層的策略。后續筆者完成代碼以后會在總結這邊貼一些實際運行的結果圖,先欠着哈。
再次感謝一些龔益群同學提供的PPT,感興趣的同學可以詳細閱讀參考文獻[5]。也非常歡迎各位同行向泡泡機器人SLAM投稿,我們有專門的泡泡讀者來稿專欄平台供大家發揮,心動不如行動哇。
最后的最后,勞煩各位親愛的讀者朋友們給小弟我點個關注吧!謝謝撒!
參考文獻
[1] 光度標定.
[2] imageRW_OpenCV.cpp.
[3] cv::imread.
[4] 去畸變.
[5] 【泡泡讀者來稿】DSO代碼解讀.
版權聲明
如需轉載,請聯系本人郵箱peichu.ye at mail2.gdut.edu.cn。未經允許,不可轉載。