SLAM程序閱讀(第8講 LK光流法)

2021-02-18 小白學視覺

本期,小綠帶大家閱讀高翔Slambook第8講中LK光流法程序。

細心的同學已經發現,小綠換了文章的封皮,因為有一些同學都覺得原來那張圖比較撈,不沉穩也不正經…而更細心的同學也會發現,小綠連題目都改了,原來叫「解讀」,現在叫「閱讀」,這也是因為一些熱心的同學在後臺積極提問,然而小綠作為一個門徒,實在是有些束手無策,沒法很透徹的解答同學們的問題…

 

確實,比方說在第7講中的幾個.cpp,求E矩陣需要使用findEssentialMat函數,求F矩陣需要使用findFundamentalMat函數,這兩個函數雖由OpenCV提供,而且原理使用對極約束,但具體求取E、F時構造的是如何的一個最小二乘問題?求解時又使用何種方式求解?再比如說三角測量中,使用的triangulatePoint函數,其1、2號形參是兩個projection matrix,也叫投影矩陣,那麼這兩個投影矩陣是怎麼求得?又為什麼簡單地把R、t做一個增廣就叫做projection matrix而不叫function matrix….等等諸如此類的問題,小綠確實由於沒有深入閱讀OpenCV源碼,直接當做封裝好的函數,當做一個工具去使用,卻並沒有深入其原理。

 

所以從本期開始,小綠沒法再帶著大家去「解讀」程序啦o(╥﹏╥)o…小綠只能帶著大家去「閱讀」程序~~

 

好了,閒話到此為止,現在咱們來看一下Slambook第8講的第一個程序:useLK.cpp

 

首先,來了解一下程序的用途:useLK.cpp這個程序是一個演示使用LK光流法跟蹤特徵點運動軌跡的實例,通過從資料庫截取9張RGB圖像(這裡雖然data數據集裡包含了9張深度圖,然而只是為了讀取RGB圖像方便,為了使用associate.txt中排好序的圖像名稱,而在之後使用直接法求解位姿時才使用深度信息),在第一張圖像中尋找FAST角點作為特徵點,進而在後續的圖像中使用LK光流法對這些角點進行跟蹤。本程序只進行特徵點的跟蹤,並沒有涉及幀與幀之間的位姿變換運算,可以說是光流法的一個基礎例程。這裡可以先展示一下程序的運行結果:

下面我們來看代碼。由於本程序沒有子函數,在這裡就直接把主函數貼在這裡:

 

#include <iostream>
#include <fstream>
#include <list>
#include <vector>
#include <chrono>
using namespace std;

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/video/tracking.hpp>

int main( int argc, char** argv )
{
   if ( argc != 2 )
   {
       cout<<"usage: useLK path_to_dataset"<<endl;
       return 1;
   }
   string path_to_dataset = argv[1];
   string associate_file = path_to_dataset + "/associate.txt";
   
   ifstream fin( associate_file );
   if ( !fin )
   {
       cerr<<"I cann't find associate.txt!"<<endl;
       return 1;
   }
   
   string rgb_file, depth_file, time_rgb, time_depth;
   list< cv::Point2f > keypoints;      
   cv::Mat color, depth, last_color;
   
   for ( int index=0; index<100; index++ )
   {
       fin>>time_rgb>>rgb_file>>time_depth>>depth_file;
       color = cv::imread( path_to_dataset+"/"+rgb_file );
       depth = cv::imread( path_to_dataset+"/"+depth_file, -1 );
       if (index ==0 )
       {
           
           vector<cv::KeyPoint> kps;
           cv::Ptr<cv::FastFeatureDetector> detector = cv::FastFeatureDetector::create();
           detector->detect( color, kps );
           for ( auto kp:kps )
               keypoints.push_back( kp.pt );
           last_color = color;
           continue;
       }
       if ( color.data==nullptr || depth.data==nullptr )
           continue;
       
       vector<cv::Point2f> next_keypoints;
       vector<cv::Point2f> prev_keypoints;
       for ( auto kp:keypoints )
           prev_keypoints.push_back(kp);
       vector<unsigned char> status;
       vector<float> error;
       chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
       cv::calcOpticalFlowPyrLK( last_color, color, prev_keypoints, next_keypoints, status, error );
       chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
       chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>( t2-t1 );
       cout<<"LK Flow use time:"<<time_used.count()<<" seconds."<<endl;
       
       int i=0;
       for ( auto iter=keypoints.begin(); iter!=keypoints.end(); i++)
       {
           if ( status[i] == 0 )
           {
               iter = keypoints.erase(iter);
               continue;
           }
           *iter = next_keypoints[i];
           iter++;
       }
       cout<<"tracked keypoints: "<<keypoints.size()<<endl;
       if (keypoints.size() == 0)
       {
           cout<<"all keypoints are lost."<<endl;
           break;
       }
       
       cv::Mat img_show = color.clone();
       for ( auto kp:keypoints )
           cv::circle(img_show, kp, 10, cv::Scalar(0, 240, 0), 1);
       cv::imshow("corners", img_show);
       cv::waitKey(0);
       last_color = color;
   }
   return 0;
}

 

首先是對輸入參數個數的判斷。這裡我們只需傳入「數據集排序文件」associate.txt所在的文件夾就可以,因而argc的判別值為2。

 

string path_to_dataset = argv[1];
string associate_file = path_to_dataset + "/associate.txt";

 

這裡定義了兩個string類變量,即兩個字符串,分別存儲associate.txt所在文件夾的絕對路徑,與associate.txt的結對路徑。

 

ifstream fin( associate_file );
   if ( !fin )
   {
       cerr<<"I cann't find associate.txt!"<<endl;
       return 1;
   }

 

這裡實例化了一個ifstream輸入文件流類的變量fin,並直接初始化為associate_file所存儲的字符串。隨後判斷是否能夠打開fin所存儲路徑下的文件,而在判斷語句中「!fin」並不是說判斷fin是否為0或者為空,而是ifstream類重載了「!」操作符,所以當我們如此使用的時候,是「!」操作符函數返回一個bool類變量來標記是否成功,成功則為1。

 

list< cv::Point2f > keypoints;

 

還記得之前用於存儲特徵點的keypoints是Point2f類的容器,而現在使用list鍊表是為了方便插入與刪除某個元素,這裡是為了方便在後續光流法跟蹤時刪除跟丟的點。

 

for ( int index=0; index<100; index++ )
   {
      ...
   }

 

在每次循環中,輸入流fin輸入associate.txt每行的數據,因為associate文件的每一行分別是time_color、color、time_depth、depth,所以分別將其賦值給存儲文件名稱或文件產生時間的變量:

 

fin>>time_rgb>>rgb_file>>time_depth>>depth_file;

 

此後,針對第一張圖像,按照FAST角點尋找特徵點並存入keypoints中;進而在後續幀之間使用LK進行特徵點的跟蹤。

 

if ( color.data==nullptr || depth.data==nullptr )
           continue;

 

這裡,通過判斷color與depth兩個Mat類變量中數據存儲區data是否為空指針,來判斷是否成功找到了本幀所對應的彩色圖與深度圖。如果有一項為空,則continue進行下一次循環。

 

vector<cv::Point2f> next_keypoints;
vector<cv::Point2f> prev_keypoints;
for ( auto kp:keypoints )
     prev_keypoints.push_back(kp);

 

這裡定義了兩個存儲Point2f類的容器next_keypoints與prev_keypoints,分別用來存儲當前幀(下一幀)通過光流跟蹤得到的特徵點的像素坐標,與前一幀特徵點的像素坐標。其中,前一幀的特徵點需要將存儲特徵點的list進行遍歷(每次光流跟蹤後會有壞點剔除),分別存入prev_keypoints。

 

cv::calcOpticalFlowPyrLK( last_color, color, prev_keypoints, next_keypoints, status, error );

 

這裡調用OpenCV提供的光流法計算函數calcOpticalFlowPyrLK,通過金字塔LK光流法計算當前幀跟蹤得到的特徵點的像素坐標並存入next_keypoints,同時會將每一個特徵點的跟蹤情況存入同維度的容器status與error,用來判斷該特徵點是否被成功跟蹤。

 


       int i=0;
       for ( auto iter=keypoints.begin(); iter!=keypoints.end(); i++)
       {
           if ( status[i] == 0 )
           {
               iter = keypoints.erase(iter);
               continue;
           }
           *iter = next_keypoints[i];
           iter++;
       }

 

這裡創建一個鍊表keypoints的迭代器iter,依次訪問內部元素,通過判斷status容器內同位置的標誌量是否為0,來選擇是否在鍊表內部刪除該特徵點。若未跟丟,則使用當前幀該特徵點運動到的像素位置替換keypoints中該特徵點存儲的像素位置(即在前一幀的位置)。

 


       cv::Mat img_show = color.clone();
       for ( auto kp:keypoints )
           cv::circle(img_show, kp, 10, cv::Scalar(0, 240, 0), 1);
       cv::imshow("corners", img_show);

 

最後,深拷貝當前幀(用「=」淺拷貝會修改原圖),並使用CV提供的特徵點圈畫函數circle畫出特徵點,並將圈畫過的圖像輸出到屏幕上。

 

程序的運行結果在文首已經展示過了。如果大家沒看過癮,第九章Project部分提供了一個RGBD數據集,我們在包含數百張RGBD-深度圖像的數據集中再次運行本程序進行LK光流跟蹤,結果如下(由於上傳的gif不能超過2m,小綠只截取了其中的一些幀):

本期程序useLK.cpp小綠就帶領大家閱讀到這裡,水平有限,難免有疏漏之處。如有問題或者疑問,儘管在後臺向小綠指出,在此表示感謝。

相關閱讀

圖像特徵點|moravec特徵點

入門學習SLAM(Ubuntu16.04安裝ROS kinetic)

高翔Slambook第七講代碼解讀(特徵點提取)

高翔Slambook第七講代碼解讀(三角測量)

高翔Slambook第七講代碼解讀(2d-2d位姿估計)

高翔Slambook第七講代碼解讀(3d-2d位姿估計)

高翔Slambook第七講代碼解讀(3d-3d位姿估計)

相關焦點

  • CV學習筆記(九):光流法的實現
    後臺回復CV入坑必備獲得CV入坑學習資料在上一篇文章中,我們簡單了解一下光流法的原理在接下來,我們來看一下在OpenCV中lk算法的實現.代碼的路徑在opencv\sources\samples\python\lk_track.py代碼本身有英文的注釋,我一起把注釋翻譯成中文,捋順以後發現原理還是很好理解.
  • 新手入門SLAM必備資料
    第四要會用openCV, PCL, Eigen等第三方庫。只有學會了這些東西,才能真正上手編一個SLAM系統。如果要跑實際機器人,還要會ROS。下面將為大家推薦SLAM入門的學習書籍、SLAM公開課、SLAM學習網站、SLAM開原始碼等資料大全。
  • CV學習筆記(八):光流法原理
    在推廣光流法的時候,我們要有兩個前提假設:第一:所追蹤的像素目標在連續的幀之間要保持基本不變.第二:所追蹤的像素目標在連續的幀之間要有相似的運動趨勢.在下一篇中我們要通過OpenCV中自帶的calcOpticalFlowPyrLK()函數來去實現簡單的光流法.!重磅!【深度學習衝鴨技術交流二群】微信交流群已成立額外贈送福利資源!
  • 碩士研究生階段如何學習slam機器人?
    spm_id_from=333.788.videocard.3&rt=V%2FymTlOu4ow%2Fy4xxNWPUZ%2Bv9LDQVz8nh4eMlp4A7JJY%3D必看入門書籍:視覺SLAM十四講  稍微進階:機器人學的狀態估計,多視圖幾何https://www.bilibili.com/video/BV1YW411P7AU
  • 學SLAM的女生,很酷
    在這裡,不得不感謝深藍學院和浙江大學章國鋒老師組織的第一屆暑期學校和SLAM論壇,去年當我踏上去浙大紫金港的列車的時候我並不知道會收穫什麼,但踏上歸途的時候,所得到的遠遠超出了我的想像,見到了楷神(初生牛犢不怕虎,當時並不知道楷是神,此處應有捂臉的表情),他給我講了SLAM世界裡的大神和故事,依稀記得當時他說跟賀博坐在一起,臉皮薄沒過去!
  • SLAM綜述(3)-視覺與慣導,視覺與深度學習SLAM
    【8】Mark Froehlich, Salman Azhar, and Matthew Vanture. An investigation of google tango R tablet for low cost 3d scanning. In ISARC.
  • 2019學個英語單詞第十二回:The word of Slam
    To slam someone or something means to criticize them very severely. 據外媒報導,1月19日星期六晚,32歲的流行歌手Lady Gaga在拉斯維加斯駐唱演出時突然中斷表演借政府關門風波直接向唐納·川普總統開火。
  • Slam dunk? 巨大成功
    Reader question:Please explain 「slam dunk」, as in someone saying 「the new smartphone is a slam dunk」.
  • 【泡泡機器人SLAM原創專欄-滑動窗算法】: Sliding Window Filter for SLAM
    這篇專題的idea主要來源於2006年Gabe Sibley的論文A Sliding Window Filter for SLAM,此論文詳細說明了SLAM後端優化的稀疏性的緣由,並提出了應用於slam
  • 關上close,shut,slam區別
    3-slam:強調「關」傳送出來的情緒。最早指關上門的聲音,很明顯,這個詞是帶情緒的詞,什麼樣的情緒會促使你去大聲的關門,你在生氣你在憤怒。情緒在激動,放東西也是呯的一下「猛放」;氣得想打人系列-單詞有含義「猛烈攻擊」;表現在語言上「猛烈抨擊」。
  • 英國老師這樣教同音異字c,k, ck, qu, que, cc, ch, lk 之二【自然拼讀四階網課總結10】
    英國老師這樣教同音異字f/ff-ph/gh/lf(1)【自然拼讀四階網課總結6】7.英國老師這樣教同音異字f/ff-ph/gh/lf(2 )【自然拼讀四階網課總結7】8.英國老師這樣教同音異字j-g-ge-dge-d; h-wh【自然拼讀四階網課總結8】
  • python爬蟲入門-通過茅臺腳本講些爬蟲知識,應用和價值
    官方地講,爬蟲是請求網站並提取數據的自動化程序。使用茅臺腳本來舉例,腳本程序實現模擬登陸京東帳號,保存登陸信息,也實現訪問商品-茅臺,並且幫你搶購商品,成功下訂單等等。這類似於你打開京東網站登陸帳號密碼,瀏覽商品並下單的操作,腳本將這些操作都實現,形成一個自動化的程序。
  • 五寒第4講 《時鐘問題》課前閱讀材料(下)
    分針80分鐘走過360度,所以每分鐘走360÷80=9/2度;分針的速度是時針的8倍,所以時針每分鐘走4.5÷8=9/16度。然後我們把這句話重新描述一下,讓它更像環形跑道問題。「從2:00開始,48分鐘後,時針和分針相距多少度?」
  • 無人駕駛技術的靈魂——SLAM的現在與未來
    這些在本書的第8講和第13講均有討論。不過,LSD-SLAM是在單目圖像進行半稠密的跟蹤,實現原理要比本書的例程更加複雜。LSD-SLAM在CPU上實現了半稠密場景的重建,這在之前的方案中是很少見到的。基於特徵點的方法只能是稀疏的,而進行稠密重建的方案大多要使用RGB-D傳感器,或者使用GPU構建稠密地圖。
  • 源碼詳解專題直播公開課 | ORBSLAM2特徵均勻化、雷射SLAM數據處理
    ORB-SLAM源碼講解專題一:詳解ORBSLAM2中ORB特徵點提取與均勻化代碼》時間:2020年5月23日(本周六)20:00——21:00內容:詳解ORBSLAM2中ORB特徵點提取與均勻化代碼,包括:圖像金字塔提特徵點、特徵點四叉樹均勻化源碼疑難點剖析嘉賓:小六,計算機視覺life公眾號負責人,計算機視覺算法工程師,研究方向視覺slam
  • 個人所得稅完成今年第8講,總78講
    第一講:中華人民共和國個人所得稅法解剖學——非居民個人(講課周期2021年1月1日至1月10日,實際講課日期是2021年1月3日,總第71講)第二講:以色列名模逃稅罪名成立,也是一個典型的居民個人的案例(講課周期2021年1月11日至1月17日,實際講課日期是2021年1月11日,總第72講)第三講:法律的名稱可以有哪些?
  • 英語閱讀不是英譯漢,是英譯英,是「說」英語U.S. President
    當然,如果我們的「英語閱讀」依然還停留在下面這樣的「英譯漢」式閱讀,嚴格意義講不是「英語閱讀」,而是變相「中文閱讀」,因為你的「英語閱讀」是讓你「滿腦子都是中文」,而不是英語。1.divest vt.剝奪;迫使放棄 2.within 90 days 90天內3.slam v.猛烈抨擊 4.blatant bullying 明目張胆地,公然地欺負 5.market economy rules 市場經濟規則
  • 英語閱讀不是英譯漢,是英譯英,是「說」英語U.S. President
    當然,如果我們的「英語閱讀」依然還停留在下面這樣的「英譯漢」式閱讀,嚴格意義講不是「英語閱讀」,而是變相「中文閱讀」,因為你的「英語閱讀」是讓你「滿腦子都是中文」,而不是英語。1.divest vt.剝奪;迫使放棄 2.within 90 days 90天內3.slam v.猛烈抨擊 4.blatant bullying 明目張胆地,公然地欺負 5.market economy rules 市場經濟規則當然,一般人認為「能」這樣「英語閱讀」也十分重要,還有人把「英語閱讀」當做「語音練習」,當做「記生詞」練習,都可以理解,但是,
  • 第8講:LEN、LEFT、RIGHT和MID函數
    常考函數精講視頻《Excel2016常考函數精講視頻》是我們公眾號送給全體粉絲的福利,在以往考試中,二級MS Office的考生,最怕的就是Excel各類函數題,因為這是考點中最難的部分,佔30分,幾乎決定了你能不能通過考試。
  • 2019暑期第11屆全國高校師資班7月27日-8月1日青島大學
    :自然語言處理代碼和案例實踐:輸入法設計HMM分詞文本摘要的生成智能對話系統和SeqSeq模型閱讀理解的實現與Attention第6天 天 第1講    課程引論第2講 圖像獲取第3講    圖像預處理及特徵提取(配合動手實驗)(1)  圖像預處理及空域濾波的常用方法,包括平均濾波,高斯濾波,中值濾波等;結合實例介紹OpenCV實現及結果對比(2)  圖像邊緣檢測方法