SLAM:理論與實踐
第一講,第二講:概述與預備知識
1,課程內容與預備知識
1.1,SLAM是什麼怎麼學:
1,SLAM:Simultaneous Localization and Mapping
2,參考書:MVG多檢視幾何,機器人學狀態估計State Estimation For Robotics(如何估計相機的運動)
3,視覺SLAM十四講
1.2,基礎知識:
數學:高等數學,線代,矩陣論,概率論
程式設計:等知識
2,SLAM是什麼
2.1,作用:
1,定位:提取點,來確定相機的位置,運動軌跡。
2,建圖:根據單目相機提取的點,來構建稀疏-半稠密的重建,用rgb-d相機可進行三維稠密的重建
2.2,應用:
1,手持裝置定位
2,自動駕駛定位
3,AR增強現實
2.3,感測器:
內質:感受機器人本體的資訊,IMU,鐳射,相機,編碼器
外質:安裝在環境中,二維碼,GPS,導軌(受環境的限制)
2.4,相機:
分類:
單目相機Monocular:
雙目相機Stereo:
深度相機RGB-D:
其他,全景,魚眼相機
相機的特點:二維投影形式記錄了三維世界的資訊,丟失了距離的緯度
3,視覺SLAM數學表述與框架
3.1,框架
視覺里程計:前端,連續的相鄰影象間進行移動軌跡的估計
後端:更長時間內,由於誤差的累計,會出現偏移,是估計,優化問題,符合長時間領域的觀測。
迴環檢測loop closing:由於誤差的累積,可能到同一位置時,路徑有差別,要檢測出軌跡回到某個地方。
建圖:
方法:特徵點法,直接法
後端:
3.2,SLAM問題的數學描述
4,實踐:Linux下的C++程式設計基礎
5,作業
1,熟悉linux
1.1 如何在Ubuntu 中安裝軟體(命令⾏界⾯)?它們通常被安裝在什麼地⽅?
1)apt-get 方式的安裝;
普通安裝:sudo apt-get install XXX (包名)
修復安裝:sudo apt-get -f install XXX
重新安裝:sudo apt-get -f reinstall XXX
(例如安裝g++編譯環境,可在終端中輸入命令:sudo apt-get install g++,附加說明可
通過g++ -v命令檢視g++版本)
2)dpkg方式安裝
sudo dpkg -i package_name.deb
安裝後軟體預設位置 /usr/share
1.2 linux 的環境變數是什麼?我如何定義新的環境變數?
環境變數:環境變數是在作業系統中一個具有特定名字的物件,它包含了一個或多個應用程式將使用到的資訊;它分為永久生效和shell臨時生效兩種。
1)對所有使用者生效的永久性變數(系統級):
這類變數對系統內的所有使用者都生效,所有使用者都可以使用這類變數。作用範圍是整個系統。
設定方式: 用vim 或者gedit 開啟/etc/profile 檔案,然後新增我們想要的環境變數,用export指令新增環境變數。這個檔案只有在root(超級使用者)下才能修改。我們可以在etc目錄下使用ls -l檢視這個檔案的使用者及許可權。以gedit為例:
$ gedit /etc/profile
$ export 新增環境變數
注:新增完成後新的環境變數不會立即生效,需要source /etc/profile 該檔案才會生效。否則只能在下次重進此使用者時才能生效。記住要在終端source一下。。。
2)對當前使用者生效的永久性變數(使用者級):
只針對當前使用者,和上面的一樣,只不過不需要在etc 下面進行新增,直接在.bash_profile檔案最下面用export新增就好了。
這裡 .bashrc和.bash_profile原則上來說設定此類環境變數時在這兩個檔案任意一個裡面新增都是可以的。
~/.bash_profile是互動式login方式進入bash shell執行。
~/ .bashrc是互動式non-login方式進入bash shell執行。
二者設定大致相同。
.bash_profile檔案只會在使用者登入的時候讀取一次;而.bashrc在每次開啟終端進行一次新的會話時都會讀取。
3)臨時有效的環境變數(只對當前shell有效):
此類環境變數只對當前的shell有效。當我們退出登入或者關閉終端再重新開啟時,這個環境變數就會消失,是臨時的,可以直接使用export指令新增。
$ export 新增環境變數
`1.3 linux 根⽬錄下⾯的⽬錄結構是什麼樣的?⾄少說出3 個⽬錄的⽤途。
根目錄一般結構如下:(以本人筆記本目前ubuntu16.04系統為例,截圖如下)
1)/bin 使用者二進位制檔案
包含二進位制可執行檔案,系統所有使用者可執行檔案都在這個資料夾裡,例如:ls,cp,ping等。
2)/sbin 系統二進位制檔案
包含二進位制可執行檔案,但只能由系統管理員執行,對系統進行維護。
3)/etc 配置檔案
包含所有程式配置檔案,也包含了用於啟動/停止單個程式的啟動和關閉shell指令碼。
4)/dev 裝置檔案
包含終端所有裝置,USB或連線到系統的任何裝置。例如:/dev/tty1、/dev/usbmon0
5)/proc 程序資訊
包含系統程序的相關資訊。
這是一個虛擬的檔案系統,包含有關正在執行的程序的資訊。例如:/proc/{pid}目錄中包含的與特定pid相關的資訊。
6)/var 變數檔案
可以找到內容可能增長的檔案。這包括 - 系統日誌檔案(/var/log);包和資料庫檔案(/var/lib);電子郵件(/var/mail);列印佇列(/var/spool);鎖檔案(/var/lock);多次重新啟動需要的臨時檔案(/var/tmp);
7)/tmp 臨時檔案
包含系統和使用者建立的臨時檔案。當系統重新啟動時,這個目錄下的檔案都將被刪除。
8)/usr 使用者程式
包含二進位制檔案、庫檔案、文件和二級程式的原始碼。
/usr/bin 中包含使用者程式的二進位制檔案。如果你在/bin 中找不到使用者二進位制檔案,到/usr/bin目錄看看。例如:at、awk、cc、less、scp。
/usr/sbin 中包含系統管理員的二進位制檔案。如果你在/sbin 中找不到系統二進位制檔案,
到/usr/sbin目錄看看。例如:atd、cron、sshd、useradd、userdel。
/usr/lib中包含了/usr/bin和/usr/sbin用到的庫。
/usr/local 中包含了從源安裝的使用者程式。例如,當你從源安裝Apache,它會在/usr/local/apache2中。
9)/home HOME目錄
所有使用者用來存檔他們的個人檔案。
10)/boot 引導載入程式檔案
包含引導載入程式相關的檔案。核心的initrd、vmlinux、grub檔案位於/boot下。
11)/lib 系統庫
包含支援位於/bin和/sbin下的二進位制檔案的庫檔案,庫檔名為 ld或lib.so.*
12)/opt 可選的附加應用程式
opt代表opitional;包含從個別廠商的附加應用程式。附加應用程式應該安裝在/opt/或者/opt/的子目錄下。
13)/mnt 掛載目錄
臨時安裝目錄,系統管理員可以掛載檔案系統。
14)/media 可移動媒體裝置
用於掛載可移動裝置的臨時目錄。
舉例來說,掛載CD-ROM的/media/cdrom,掛載軟盤驅動器的/media/floppy;
15)/srv 服務資料
srv代表服務。包含伺服器特定服務相關的資料。例如,/srv/cvs包含cvs相關的資料。
1.4 假設我要給a.sh 加上可執⾏許可權,該輸⼊什麼命令?
chmod 777 +檔名: 將檔案設定成對擁有者、組成員、其他人可讀、可寫、可執行。
chmod a+x +檔名:將檔案在原來的配置上增加可執行許可權。
1.5假設我要將a.sh ⽂件的所有者改成xiang:xiang,該輸⼊什麼命令?
chown xiang:xiang a.sh
第三講:三維空間的剛體運動
第四講:李群與李代數
第五講:第六講:相機模型與非線性優化
第七講:特徵點法視覺里程計
章節總括:代數的方式去求解,優化的方式去求解,有了2D的資訊能夠推出3D的資訊,深度的資訊,三角化
1,特徵點提取與匹配
1,由於是RGBD影象,2d-2d 2d-3d 3d-3d的程式碼都可以用。
2,ORB是一個類
ORB特徵點提取,匹配程式碼:feature_extraction.cpp程式碼
1 #include <iostream> 2 #include <opencv2/core/core.hpp> 3 #include <opencv2/features2d/features2d.hpp> //features2d模組支援很多很多的特徵,包括orb。 4 #include <opencv2/highgui/highgui.hpp> 5 6 using namespace std; 7 using namespace cv; 8 9 int main ( int argc, char** argv ) 10 { 11 if ( argc != 3 ) 12 { 13 cout<<"usage: feature_extraction img1 img2"<<endl; 14 return 1; 15 } 16 //-- 讀取影象 17 Mat img_1 = imread ( argv[1], CV_LOAD_IMAGE_COLOR ); 18 Mat img_2 = imread ( argv[2], CV_LOAD_IMAGE_COLOR ); 19 20 //-- 初始化 21 std::vector<KeyPoint> keypoints_1, keypoints_2; 22 Mat descriptors_1, descriptors_2; 23 Ptr<FeatureDetector> detector = ORB::create(); 24 Ptr<DescriptorExtractor> descriptor = ORB::create(); 25 // Ptr<FeatureDetector> detector = FeatureDetector::create(detector_name); 26 // Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create(descriptor_name); 27 Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create ( "BruteForce-Hamming" ); 28 29 //-- 第一步:檢測 Oriented FAST 角點位置 30 detector->detect ( img_1,keypoints_1 ); 31 detector->detect ( img_2,keypoints_2 ); 32 33 //-- 第二步:根據角點位置計算 BRIEF 描述子 34 descriptor->compute ( img_1, keypoints_1, descriptors_1 ); 35 descriptor->compute ( img_2, keypoints_2, descriptors_2 ); 36 37 Mat outimg1; 38 drawKeypoints( img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT ); 39 imshow("ORB特徵點",outimg1); 40 41 //-- 第三步:對兩幅影象中的BRIEF描述子進行匹配,使用 Hamming 距離 42 vector<DMatch> matches; 43 //BFMatcher matcher ( NORM_HAMMING ); 44 matcher->match ( descriptors_1, descriptors_2, matches ); 45 46 //-- 第四步:匹配點對篩選 47 double min_dist=10000, max_dist=0; 48 49 //找出所有匹配之間的最小距離和最大距離, 即是最相似的和最不相似的兩組點之間的距離 50 for ( int i = 0; i < descriptors_1.rows; i++ ) 51 { 52 double dist = matches[i].distance; 53 if ( dist < min_dist ) min_dist = dist; 54 if ( dist > max_dist ) max_dist = dist; 55 } 56 57 // 僅供娛樂的寫法 58 min_dist = min_element( matches.begin(), matches.end(), [](const DMatch& m1, const DMatch& m2) {return m1.distance<m2.distance;} )->distance; 59 max_dist = max_element( matches.begin(), matches.end(), [](const DMatch& m1, const DMatch& m2) {return m1.distance<m2.distance;} )->distance; 60 61 printf ( "-- Max dist : %f \n", max_dist ); 62 printf ( "-- Min dist : %f \n", min_dist ); 63 64 //當描述子之間的距離大於兩倍的最小距離時,即認為匹配有誤.但有時候最小距離會非常小,設定一個經驗值30作為下限. 65 std::vector< DMatch > good_matches; 66 for ( int i = 0; i < descriptors_1.rows; i++ ) 67 { 68 if ( matches[i].distance <= max ( 2*min_dist, 30.0 ) ) 69 { 70 good_matches.push_back ( matches[i] ); 71 } 72 } 73 74 //-- 第五步:繪製匹配結果 75 Mat img_match; 76 Mat img_goodmatch; 77 drawMatches ( img_1, keypoints_1, img_2, keypoints_2, matches, img_match ); 78 drawMatches ( img_1, keypoints_1, img_2, keypoints_2, good_matches, img_goodmatch ); 79 imshow ( "所有匹配點對", img_match ); 80 imshow ( "優化後匹配點對", img_goodmatch ); 81 waitKey(0); 82 83 return 0; 84 }
2,2D-2D 對極幾何
從單應矩陣恢復R,t;
求得R,t利用三角化計算特徵點的3D位置,即深度
給E算R 和 t
2D-2D對極幾何程式碼:pose_estimation_2d2d.cpp
1 #include <iostream> 2 #include <opencv2/core/core.hpp> 3 #include <opencv2/features2d/features2d.hpp> 4 #include <opencv2/highgui/highgui.hpp> 5 #include <opencv2/calib3d/calib3d.hpp> 6 // #include "extra.h" // use this if in OpenCV2 7 using namespace std; 8 using namespace cv; 9 10 /**************************************************** 11 * 本程式演示瞭如何使用2D-2D的特徵匹配估計相機運動 12 * **************************************************/ 13 14 void find_feature_matches ( 15 const Mat& img_1, const Mat& img_2, 16 std::vector<KeyPoint>& keypoints_1, 17 std::vector<KeyPoint>& keypoints_2, 18 std::vector< DMatch >& matches ); 19 20 void pose_estimation_2d2d ( 21 std::vector<KeyPoint> keypoints_1, 22 std::vector<KeyPoint> keypoints_2, 23 std::vector< DMatch > matches, 24 Mat& R, Mat& t ); 25 26 // 畫素座標轉相機歸一化座標 27 Point2d pixel2cam ( const Point2d& p, const Mat& K ); 28 29 int main ( int argc, char** argv ) 30 { 31 if ( argc != 3 ) 32 { 33 cout<<"usage: pose_estimation_2d2d img1 img2"<<endl; 34 return 1; 35 } 36 //-- 讀取影象 37 Mat img_1 = imread ( argv[1], CV_LOAD_IMAGE_COLOR ); 38 Mat img_2 = imread ( argv[2], CV_LOAD_IMAGE_COLOR ); 39 40 vector<KeyPoint> keypoints_1, keypoints_2; 41 vector<DMatch> matches; 42 find_feature_matches ( img_1, img_2, keypoints_1, keypoints_2, matches ); 43 cout<<"一共找到了"<<matches.size() <<"組匹配點"<<endl; 44 45 //-- 估計兩張影象間運動 46 Mat R,t; 47 pose_estimation_2d2d ( keypoints_1, keypoints_2, matches, R, t ); 48 49 //-- 驗證E=t^R*scale 50 Mat t_x = ( Mat_<double> ( 3,3 ) << 51 0, -t.at<double> ( 2,0 ), t.at<double> ( 1,0 ), 52 t.at<double> ( 2,0 ), 0, -t.at<double> ( 0,0 ), 53 -t.at<double> ( 1.0 ), t.at<double> ( 0,0 ), 0 ); 54 55 cout<<"t^R="<<endl<<t_x*R<<endl; 56 57 //-- 驗證對極約束 58 Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 ); 59 for ( DMatch m: matches ) 60 { 61 Point2d pt1 = pixel2cam ( keypoints_1[ m.queryIdx ].pt, K ); 62 Mat y1 = ( Mat_<double> ( 3,1 ) << pt1.x, pt1.y, 1 ); 63 Point2d pt2 = pixel2cam ( keypoints_2[ m.trainIdx ].pt, K ); 64 Mat y2 = ( Mat_<double> ( 3,1 ) << pt2.x, pt2.y, 1 ); 65 Mat d = y2.t() * t_x * R * y1; 66 cout << "epipolar constraint = " << d << endl; 67 } 68 return 0; 69 } 70 71 void find_feature_matches ( const Mat& img_1, const Mat& img_2, 72 std::vector<KeyPoint>& keypoints_1, 73 std::vector<KeyPoint>& keypoints_2, 74 std::vector< DMatch >& matches ) 75 { 76 //-- 初始化 77 Mat descriptors_1, descriptors_2; 78 // used in OpenCV3 79 Ptr<FeatureDetector> detector = ORB::create(); 80 Ptr<DescriptorExtractor> descriptor = ORB::create(); 81 // use this if you are in OpenCV2 82 // Ptr<FeatureDetector> detector = FeatureDetector::create ( "ORB" ); 83 // Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create ( "ORB" ); 84 Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create ( "BruteForce-Hamming" ); 85 //-- 第一步:檢測 Oriented FAST 角點位置 86 detector->detect ( img_1,keypoints_1 ); 87 detector->detect ( img_2,keypoints_2 ); 88 89 //-- 第二步:根據角點位置計算 BRIEF 描述子 90 descriptor->compute ( img_1, keypoints_1, descriptors_1 ); 91 descriptor->compute ( img_2, keypoints_2, descriptors_2 ); 92 93 //-- 第三步:對兩幅影象中的BRIEF描述子進行匹配,使用 Hamming 距離 94 vector<DMatch> match; 95 //BFMatcher matcher ( NORM_HAMMING ); 96 matcher->match ( descriptors_1, descriptors_2, match ); 97 98 //-- 第四步:匹配點對篩選 99 double min_dist=10000, max_dist=0; 100 101 //找出所有匹配之間的最小距離和最大距離, 即是最相似的和最不相似的兩組點之間的距離 102 for ( int i = 0; i < descriptors_1.rows; i++ ) 103 { 104 double dist = match[i].distance; 105 if ( dist < min_dist ) min_dist = dist; 106 if ( dist > max_dist ) max_dist = dist; 107 } 108 109 printf ( "-- Max dist : %f \n", max_dist ); 110 printf ( "-- Min dist : %f \n", min_dist ); 111 112 //當描述子之間的距離大於兩倍的最小距離時,即認為匹配有誤.但有時候最小距離會非常小,設定一個經驗值30作為下限. 113 for ( int i = 0; i < descriptors_1.rows; i++ ) 114 { 115 if ( match[i].distance <= max ( 2*min_dist, 30.0 ) ) 116 { 117 matches.push_back ( match[i] ); 118 } 119 } 120 } 121 122 123 Point2d pixel2cam ( const Point2d& p, const Mat& K ) 124 { 125 return Point2d 126 ( 127 ( p.x - K.at<double> ( 0,2 ) ) / K.at<double> ( 0,0 ), 128 ( p.y - K.at<double> ( 1,2 ) ) / K.at<double> ( 1,1 ) 129 ); 130 } 131 132 133 void pose_estimation_2d2d ( std::vector<KeyPoint> keypoints_1, 134 std::vector<KeyPoint> keypoints_2, 135 std::vector< DMatch > matches, 136 Mat& R, Mat& t ) 137 { 138 // 相機內參,TUM Freiburg2 139 Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 ); 140 141 //-- 把匹配點轉換為vector<Point2f>的形式 142 vector<Point2f> points1; 143 vector<Point2f> points2; 144 145 for ( int i = 0; i < ( int ) matches.size(); i++ ) 146 { 147 points1.push_back ( keypoints_1[matches[i].queryIdx].pt ); 148 points2.push_back ( keypoints_2[matches[i].trainIdx].pt ); 149 } 150 151 //-- 計算基礎矩陣 152 Mat fundamental_matrix; 153 fundamental_matrix = findFundamentalMat ( points1, points2, CV_FM_8POINT ); 154 cout<<"fundamental_matrix is "<<endl<< fundamental_matrix<<endl; 155 156 //-- 計算本質矩陣 157 Point2d principal_point ( 325.1, 249.7 ); //相機光心, TUM dataset標定值 158 double focal_length = 521; //相機焦距, TUM dataset標定值 159 Mat essential_matrix; 160 essential_matrix = findEssentialMat ( points1, points2, focal_length, principal_point ); 161 cout<<"essential_matrix is "<<endl<< essential_matrix<<endl; 162 163 //-- 計算單應矩陣 164 Mat homography_matrix; 165 homography_matrix = findHomography ( points1, points2, RANSAC, 3 ); 166 cout<<"homography_matrix is "<<endl<<homography_matrix<<endl; 167 168 //-- 從本質矩陣中恢復旋轉和平移資訊. 169 recoverPose ( essential_matrix, points1, points2, R, t, focal_length, principal_point ); 170 cout<<"R is "<<endl<<R<<endl; 171 cout<<"t is "<<endl<<t<<endl; 172 173 }
3,3D-2D PnP
在雙目或者Rgbd,從3D到2D位姿的估計,已知3D點的空間位置,和相機上的投影點,求相機的旋轉和平移(外參)
代數的解法:
1,DLT:直接線性變換
非代數的解法:非線性優化的方式
1 #include <iostream> 2 #include <opencv2/core/core.hpp> 3 #include <opencv2/features2d/features2d.hpp> 4 #include <opencv2/highgui/highgui.hpp> 5 #include <opencv2/calib3d/calib3d.hpp> 6 #include <Eigen/Core> 7 #include <Eigen/Geometry> 8 #include <g2o/core/base_vertex.h> 9 #include <g2o/core/base_unary_edge.h> 10 #include <g2o/core/block_solver.h> 11 #include <g2o/core/optimization_algorithm_levenberg.h> 12 #include <g2o/solvers/csparse/linear_solver_csparse.h> 13 #include <g2o/types/sba/types_six_dof_expmap.h> 14 #include <chrono> 15 16 using namespace std; 17 using namespace cv; 18 19 void find_feature_matches ( 20 const Mat& img_1, const Mat& img_2, 21 std::vector<KeyPoint>& keypoints_1, 22 std::vector<KeyPoint>& keypoints_2, 23 std::vector< DMatch >& matches ); 24 25 // 畫素座標轉相機歸一化座標 26 Point2d pixel2cam ( const Point2d& p, const Mat& K ); 27 28 void bundleAdjustment ( 29 const vector<Point3f> points_3d, 30 const vector<Point2f> points_2d, 31 const Mat& K, 32 Mat& R, Mat& t 33 ); 34 35 int main ( int argc, char** argv ) 36 { 37 if ( argc != 5 ) 38 { 39 cout<<"usage: pose_estimation_3d2d img1 img2 depth1 depth2"<<endl; 40 return 1; 41 } 42 //-- 讀取影象 43 Mat img_1 = imread ( argv[1], CV_LOAD_IMAGE_COLOR ); 44 Mat img_2 = imread ( argv[2], CV_LOAD_IMAGE_COLOR ); 45 46 vector<KeyPoint> keypoints_1, keypoints_2; 47 vector<DMatch> matches; 48 find_feature_matches ( img_1, img_2, keypoints_1, keypoints_2, matches ); 49 cout<<"一共找到了"<<matches.size() <<"組匹配點"<<endl; 50 51 // 建立3D點 52 Mat d1 = imread ( argv[3], CV_LOAD_IMAGE_UNCHANGED ); // 深度圖為16位無符號數,單通道影象 53 Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 ); 54 vector<Point3f> pts_3d; 55 vector<Point2f> pts_2d; 56 for ( DMatch m:matches ) 57 { 58 ushort d = d1.ptr<unsigned short> (int ( keypoints_1[m.queryIdx].pt.y )) [ int ( keypoints_1[m.queryIdx].pt.x ) ]; 59 if ( d == 0 ) // bad depth 60 continue; 61 float dd = d/5000.0; 62 Point2d p1 = pixel2cam ( keypoints_1[m.queryIdx].pt, K ); 63 pts_3d.push_back ( Point3f ( p1.x*dd, p1.y*dd, dd ) ); 64 pts_2d.push_back ( keypoints_2[m.trainIdx].pt ); 65 } 66 67 cout<<"3d-2d pairs: "<<pts_3d.size() <<endl; 68 69 Mat r, t; 70 solvePnP ( pts_3d, pts_2d, K, Mat(), r, t, false ); // 呼叫OpenCV 的 PnP 求解,可選擇EPNP,DLS等方法 71 Mat R; 72 cv::Rodrigues ( r, R ); // r為旋轉向量形式,用Rodrigues公式轉換為矩陣 73 74 cout<<"R="<<endl<<R<<endl; 75 cout<<"t="<<endl<<t<<endl; 76 77 cout<<"calling bundle adjustment"<<endl; 78 79 bundleAdjustment ( pts_3d, pts_2d, K, R, t ); 80 } 81 82 void find_feature_matches ( const Mat& img_1, const Mat& img_2, 83 std::vector<KeyPoint>& keypoints_1, 84 std::vector<KeyPoint>& keypoints_2, 85 std::vector< DMatch >& matches ) 86 { 87 //-- 初始化 88 Mat descriptors_1, descriptors_2; 89 // used in OpenCV3 90 Ptr<FeatureDetector> detector = ORB::create(); 91 Ptr<DescriptorExtractor> descriptor = ORB::create(); 92 // use this if you are in OpenCV2 93 // Ptr<FeatureDetector> detector = FeatureDetector::create ( "ORB" ); 94 // Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create ( "ORB" ); 95 Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create ( "BruteForce-Hamming" ); 96 //-- 第一步:檢測 Oriented FAST 角點位置 97 detector->detect ( img_1,keypoints_1 ); 98 detector->detect ( img_2,keypoints_2 ); 99 100 //-- 第二步:根據角點位置計算 BRIEF 描述子 101 descriptor->compute ( img_1, keypoints_1, descriptors_1 ); 102 descriptor->compute ( img_2, keypoints_2, descriptors_2 ); 103 104 //-- 第三步:對兩幅影象中的BRIEF描述子進行匹配,使用 Hamming 距離 105 vector<DMatch> match; 106 // BFMatcher matcher ( NORM_HAMMING ); 107 matcher->match ( descriptors_1, descriptors_2, match ); 108 109 //-- 第四步:匹配點對篩選 110 double min_dist=10000, max_dist=0; 111 112 //找出所有匹配之間的最小距離和最大距離, 即是最相似的和最不相似的兩組點之間的距離 113 for ( int i = 0; i < descriptors_1.rows; i++ ) 114 { 115 double dist = match[i].distance; 116 if ( dist < min_dist ) min_dist = dist; 117 if ( dist > max_dist ) max_dist = dist; 118 } 119 120 printf ( "-- Max dist : %f \n", max_dist ); 121 printf ( "-- Min dist : %f \n", min_dist ); 122 123 //當描述子之間的距離大於兩倍的最小距離時,即認為匹配有誤.但有時候最小距離會非常小,設定一個經驗值30作為下限. 124 for ( int i = 0; i < descriptors_1.rows; i++ ) 125 { 126 if ( match[i].distance <= max ( 2*min_dist, 30.0 ) ) 127 { 128 matches.push_back ( match[i] ); 129 } 130 } 131 } 132 133 Point2d pixel2cam ( const Point2d& p, const Mat& K ) 134 { 135 return Point2d 136 ( 137 ( p.x - K.at<double> ( 0,2 ) ) / K.at<double> ( 0,0 ), 138 ( p.y - K.at<double> ( 1,2 ) ) / K.at<double> ( 1,1 ) 139 ); 140 } 141 142 void bundleAdjustment ( 143 const vector< Point3f > points_3d, 144 const vector< Point2f > points_2d, 145 const Mat& K, 146 Mat& R, Mat& t ) 147 { 148 // 初始化g2o 149 typedef g2o::BlockSolver< g2o::BlockSolverTraits<6,3> > Block; // pose 維度為 6, landmark 維度為 3 150 Block::LinearSolverType* linearSolver = new g2o::LinearSolverCSparse<Block::PoseMatrixType>(); // 線性方程求解器 151 Block* solver_ptr = new Block ( linearSolver ); // 矩陣塊求解器 152 g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg ( solver_ptr ); 153 g2o::SparseOptimizer optimizer; 154 optimizer.setAlgorithm ( solver ); 155 156 // vertex 157 g2o::VertexSE3Expmap* pose = new g2o::VertexSE3Expmap(); // camera pose 158 Eigen::Matrix3d R_mat; 159 R_mat << 160 R.at<double> ( 0,0 ), R.at<double> ( 0,1 ), R.at<double> ( 0,2 ), 161 R.at<double> ( 1,0 ), R.at<double> ( 1,1 ), R.at<double> ( 1,2 ), 162 R.at<double> ( 2,0 ), R.at<double> ( 2,1 ), R.at<double> ( 2,2 ); 163 pose->setId ( 0 ); 164 pose->setEstimate ( g2o::SE3Quat ( 165 R_mat, 166 Eigen::Vector3d ( t.at<double> ( 0,0 ), t.at<double> ( 1,0 ), t.at<double> ( 2,0 ) ) 167 ) ); 168 optimizer.addVertex ( pose ); 169 170 int index = 1; 171 for ( const Point3f p:points_3d ) // landmarks 172 { 173 g2o::VertexSBAPointXYZ* point = new g2o::VertexSBAPointXYZ(); 174 point->setId ( index++ ); 175 point->setEstimate ( Eigen::Vector3d ( p.x, p.y, p.z ) ); 176 point->setMarginalized ( true ); // g2o 中必須設定 marg 參見第十講內容 177 optimizer.addVertex ( point ); 178 } 179 180 // parameter: camera intrinsics 181 g2o::CameraParameters* camera = new g2o::CameraParameters ( 182 K.at<double> ( 0,0 ), Eigen::Vector2d ( K.at<double> ( 0,2 ), K.at<double> ( 1,2 ) ), 0 183 ); 184 camera->setId ( 0 ); 185 optimizer.addParameter ( camera ); 186 187 // edges 188 index = 1; 189 for ( const Point2f p:points_2d ) 190 { 191 g2o::EdgeProjectXYZ2UV* edge = new g2o::EdgeProjectXYZ2UV(); 192 edge->setId ( index ); 193 edge->setVertex ( 0, dynamic_cast<g2o::VertexSBAPointXYZ*> ( optimizer.vertex ( index ) ) ); 194 edge->setVertex ( 1, pose ); 195 edge->setMeasurement ( Eigen::Vector2d ( p.x, p.y ) ); 196 edge->setParameterId ( 0,0 ); 197 edge->setInformation ( Eigen::Matrix2d::Identity() ); 198 optimizer.addEdge ( edge ); 199 index++; 200 } 201 202 chrono::steady_clock::time_point t1 = chrono::steady_clock::now(); 203 optimizer.setVerbose ( true ); 204 optimizer.initializeOptimization(); 205 optimizer.optimize ( 100 ); 206 chrono::steady_clock::time_point t2 = chrono::steady_clock::now(); 207 chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>> ( t2-t1 ); 208 cout<<"optimization costs time: "<<time_used.count() <<" seconds."<<endl; 209 210 cout<<endl<<"after optimization:"<<endl; 211 cout<<"T="<<endl<<Eigen::Isometry3d ( pose->estimate() ).matrix() <<endl; 212 }
4,3D-3D ICP
1 #include <iostream> 2 #include <opencv2/core/core.hpp> 3 #include <opencv2/features2d/features2d.hpp> 4 #include <opencv2/highgui/highgui.hpp> 5 #include <opencv2/calib3d/calib3d.hpp> 6 #include <Eigen/Core> 7 #include <Eigen/Geometry> 8 #include <Eigen/SVD> 9 #include <g2o/core/base_vertex.h> 10 #include <g2o/core/base_unary_edge.h> 11 #include <g2o/core/block_solver.h> 12 #include <g2o/core/optimization_algorithm_gauss_newton.h> 13 #include <g2o/solvers/eigen/linear_solver_eigen.h> 14 #include <g2o/types/sba/types_six_dof_expmap.h> 15 #include <chrono> 16 17 using namespace std; 18 using namespace cv; 19 20 void find_feature_matches ( 21 const Mat& img_1, const Mat& img_2, 22 std::vector<KeyPoint>& keypoints_1, 23 std::vector<KeyPoint>& keypoints_2, 24 std::vector< DMatch >& matches ); 25 26 // 畫素座標轉相機歸一化座標 27 Point2d pixel2cam ( const Point2d& p, const Mat& K ); 28 29 void pose_estimation_3d3d ( 30 const vector<Point3f>& pts1, 31 const vector<Point3f>& pts2, 32 Mat& R, Mat& t 33 ); 34 35 void bundleAdjustment( 36 const vector<Point3f>& points_3d, 37 const vector<Point3f>& points_2d, 38 Mat& R, Mat& t 39 ); 40 41 // g2o edge 42 class EdgeProjectXYZRGBDPoseOnly : public g2o::BaseUnaryEdge<3, Eigen::Vector3d, g2o::VertexSE3Expmap> 43 { 44 public: 45 EIGEN_MAKE_ALIGNED_OPERATOR_NEW; 46 EdgeProjectXYZRGBDPoseOnly( const Eigen::Vector3d& point ) : _point(point) {} 47 48 virtual void computeError() 49 { 50 const g2o::VertexSE3Expmap* pose = static_cast<const g2o::VertexSE3Expmap*> ( _vertices[0] ); 51 // measurement is p, point is p' 52 _error = _measurement - pose->estimate().map( _point ); 53 } 54 55 virtual void linearizeOplus() 56 { 57 g2o::VertexSE3Expmap* pose = static_cast<g2o::VertexSE3Expmap *>(_vertices[0]); 58 g2o::SE3Quat T(pose->estimate()); 59 Eigen::Vector3d xyz_trans = T.map(_point); 60 double x = xyz_trans[0]; 61 double y = xyz_trans[1]; 62 double z = xyz_trans[2]; 63 64 _jacobianOplusXi(0,0) = 0; 65 _jacobianOplusXi(0,1) = -z; 66 _jacobianOplusXi(0,2) = y; 67 _jacobianOplusXi(0,3) = -1; 68 _jacobianOplusXi(0,4) = 0; 69 _jacobianOplusXi(0,5) = 0; 70 71 _jacobianOplusXi(1,0) = z; 72 _jacobianOplusXi(1,1) = 0; 73 _jacobianOplusXi(1,2) = -x; 74 _jacobianOplusXi(1,3) = 0; 75 _jacobianOplusXi(1,4) = -1; 76 _jacobianOplusXi(1,5) = 0; 77 78 _jacobianOplusXi(2,0) = -y; 79 _jacobianOplusXi(2,1) = x; 80 _jacobianOplusXi(2,2) = 0; 81 _jacobianOplusXi(2,3) = 0; 82 _jacobianOplusXi(2,4) = 0; 83 _jacobianOplusXi(2,5) = -1; 84 } 85 86 bool read ( istream& in ) {} 87 bool write ( ostream& out ) const {} 88 protected: 89 Eigen::Vector3d _point; 90 }; 91 92 int main ( int argc, char** argv ) 93 { 94 if ( argc != 5 ) 95 { 96 cout<<"usage: pose_estimation_3d3d img1 img2 depth1 depth2"<<endl; 97 return 1; 98 } 99 //-- 讀取影象 100 Mat img_1 = imread ( argv[1], CV_LOAD_IMAGE_COLOR ); 101 Mat img_2 = imread ( argv[2], CV_LOAD_IMAGE_COLOR ); 102 103 vector<KeyPoint> keypoints_1, keypoints_2; 104 vector<DMatch> matches; 105 find_feature_matches ( img_1, img_2, keypoints_1, keypoints_2, matches ); 106 cout<<"一共找到了"<<matches.size() <<"組匹配點"<<endl; 107 108 // 建立3D點 109 Mat depth1 = imread ( argv[3], CV_LOAD_IMAGE_UNCHANGED ); // 深度圖為16位無符號數,單通道影象 110 Mat depth2 = imread ( argv[4], CV_LOAD_IMAGE_UNCHANGED ); // 深度圖為16位無符號數,單通道影象 111 Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 ); 112 vector<Point3f> pts1, pts2; 113 114 for ( DMatch m:matches ) 115 { 116 ushort d1 = depth1.ptr<unsigned short> ( int ( keypoints_1[m.queryIdx].pt.y ) ) [ int ( keypoints_1[m.queryIdx].pt.x ) ]; 117 ushort d2 = depth2.ptr<unsigned short> ( int ( keypoints_2[m.trainIdx].pt.y ) ) [ int ( keypoints_2[m.trainIdx].pt.x ) ]; 118 if ( d1==0 || d2==0 ) // bad depth 119 continue; 120 Point2d p1 = pixel2cam ( keypoints_1[m.queryIdx].pt, K ); 121 Point2d p2 = pixel2cam ( keypoints_2[m.trainIdx].pt, K ); 122 float dd1 = float ( d1 ) /5000.0; 123 float dd2 = float ( d2 ) /5000.0; 124 pts1.push_back ( Point3f ( p1.x*dd1, p1.y*dd1, dd1 ) ); 125 pts2.push_back ( Point3f ( p2.x*dd2, p2.y*dd2, dd2 ) ); 126 } 127 128 cout<<"3d-3d pairs: "<<pts1.size() <<endl; 129 Mat R, t; 130 pose_estimation_3d3d ( pts1, pts2, R, t ); 131 cout<<"ICP via SVD results: "<<endl; 132 cout<<"R = "<<R<<endl; 133 cout<<"t = "<<t<<endl; 134 cout<<"R_inv = "<<R.t() <<endl; 135 cout<<"t_inv = "<<-R.t() *t<<endl; 136 137 cout<<"calling bundle adjustment"<<endl; 138 139 bundleAdjustment( pts1, pts2, R, t ); 140 141 // verify p1 = R*p2 + t 142 for ( int i=0; i<5; i++ ) 143 { 144 cout<<"p1 = "<<pts1[i]<<endl; 145 cout<<"p2 = "<<pts2[i]<<endl; 146 cout<<"(R*p2+t) = "<< 147 R * (Mat_<double>(3,1)<<pts2[i].x, pts2[i].y, pts2[i].z) + t 148 <<endl; 149 cout<<endl; 150 } 151 } 152 153 void find_feature_matches ( const Mat& img_1, const Mat& img_2, 154 std::vector<KeyPoint>& keypoints_1, 155 std::vector<KeyPoint>& keypoints_2, 156 std::vector< DMatch >& matches ) 157 { 158 //-- 初始化 159 Mat descriptors_1, descriptors_2; 160 // used in OpenCV3 161 Ptr<FeatureDetector> detector = ORB::create(); 162 Ptr<DescriptorExtractor> descriptor = ORB::create(); 163 // use this if you are in OpenCV2 164 // Ptr<FeatureDetector> detector = FeatureDetector::create ( "ORB" ); 165 // Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create ( "ORB" ); 166 Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming"); 167 //-- 第一步:檢測 Oriented FAST 角點位置 168 detector->detect ( img_1,keypoints_1 ); 169 detector->detect ( img_2,keypoints_2 ); 170 171 //-- 第二步:根據角點位置計算 BRIEF 描述子 172 descriptor->compute ( img_1, keypoints_1, descriptors_1 ); 173 descriptor->compute ( img_2, keypoints_2, descriptors_2 ); 174 175 //-- 第三步:對兩幅影象中的BRIEF描述子進行匹配,使用 Hamming 距離 176 vector<DMatch> match; 177 // BFMatcher matcher ( NORM_HAMMING ); 178 matcher->match ( descriptors_1, descriptors_2, match ); 179 180 //-- 第四步:匹配點對篩選 181 double min_dist=10000, max_dist=0; 182 183 //找出所有匹配之間的最小距離和最大距離, 即是最相似的和最不相似的兩組點之間的距離 184 for ( int i = 0; i < descriptors_1.rows; i++ ) 185 { 186 double dist = match[i].distance; 187 if ( dist < min_dist ) min_dist = dist; 188 if ( dist > max_dist ) max_dist = dist; 189 } 190 191 printf ( "-- Max dist : %f \n", max_dist ); 192 printf ( "-- Min dist : %f \n", min_dist ); 193 194 //當描述子之間的距離大於兩倍的最小距離時,即認為匹配有誤.但有時候最小距離會非常小,設定一個經驗值30作為下限. 195 for ( int i = 0; i < descriptors_1.rows; i++ ) 196 { 197 if ( match[i].distance <= max ( 2*min_dist, 30.0 ) ) 198 { 199 matches.push_back ( match[i] ); 200 } 201 } 202 } 203 204 Point2d pixel2cam ( const Point2d& p, const Mat& K ) 205 { 206 return Point2d 207 ( 208 ( p.x - K.at<double> ( 0,2 ) ) / K.at<double> ( 0,0 ), 209 ( p.y - K.at<double> ( 1,2 ) ) / K.at<double> ( 1,1 ) 210 ); 211 } 212 213 void pose_estimation_3d3d ( 214 const vector<Point3f>& pts1, 215 const vector<Point3f>& pts2, 216 Mat& R, Mat& t 217 ) 218 { 219 Point3f p1, p2; // center of mass 220 int N = pts1.size(); 221 for ( int i=0; i<N; i++ ) 222 { 223 p1 += pts1[i]; 224 p2 += pts2[i]; 225 } 226 p1 = Point3f( Vec3f(p1) / N); 227 p2 = Point3f( Vec3f(p2) / N); 228 vector<Point3f> q1 ( N ), q2 ( N ); // remove the center 229 for ( int i=0; i<N; i++ ) 230 { 231 q1[i] = pts1[i] - p1; 232 q2[i] = pts2[i] - p2; 233 } 234 235 // compute q1*q2^T 236 Eigen::Matrix3d W = Eigen::Matrix3d::Zero(); 237 for ( int i=0; i<N; i++ ) 238 { 239 W += Eigen::Vector3d ( q1[i].x, q1[i].y, q1[i].z ) * Eigen::Vector3d ( q2[i].x, q2[i].y, q2[i].z ).transpose(); 240 } 241 cout<<"W="<<W<<endl; 242 243 // SVD on W 244 Eigen::JacobiSVD<Eigen::Matrix3d> svd ( W, Eigen::ComputeFullU|Eigen::ComputeFullV ); 245 Eigen::Matrix3d U = svd.matrixU(); 246 Eigen::Matrix3d V = svd.matrixV(); 247 cout<<"U="<<U<<endl; 248 cout<<"V="<<V<<endl; 249 250 Eigen::Matrix3d R_ = U* ( V.transpose() ); 251 Eigen::Vector3d t_ = Eigen::Vector3d ( p1.x, p1.y, p1.z ) - R_ * Eigen::Vector3d ( p2.x, p2.y, p2.z ); 252 253 // convert to cv::Mat 254 R = ( Mat_<double> ( 3,3 ) << 255 R_ ( 0,0 ), R_ ( 0,1 ), R_ ( 0,2 ), 256 R_ ( 1,0 ), R_ ( 1,1 ), R_ ( 1,2 ), 257 R_ ( 2,0 ), R_ ( 2,1 ), R_ ( 2,2 ) 258 ); 259 t = ( Mat_<double> ( 3,1 ) << t_ ( 0,0 ), t_ ( 1,0 ), t_ ( 2,0 ) ); 260 } 261 262 void bundleAdjustment ( 263 const vector< Point3f >& pts1, 264 const vector< Point3f >& pts2, 265 Mat& R, Mat& t ) 266 { 267 // 初始化g2o 268 typedef g2o::BlockSolver< g2o::BlockSolverTraits<6,3> > Block; // pose維度為 6, landmark 維度為 3 269 Block::LinearSolverType* linearSolver = new g2o::LinearSolverEigen<Block::PoseMatrixType>(); // 線性方程求解器 270 271 Block* solver_ptr = new Block( linearSolver ); // 矩陣塊求解器 272 273 g2o::OptimizationAlgorithmGaussNewton* solver = new g2o::OptimizationAlgorithmGaussNewton( solver_ptr ); 274 275 g2o::SparseOptimizer optimizer; 276 optimizer.setAlgorithm( solver ); 277 278 // vertex 279 g2o::VertexSE3Expmap* pose = new g2o::VertexSE3Expmap(); // camera pose 280 pose->setId(0); 281 pose->setEstimate( g2o::SE3Quat( 282 Eigen::Matrix3d::Identity(), 283 Eigen::Vector3d( 0,0,0 ) 284 ) ); 285 optimizer.addVertex( pose ); 286 287 // edges 288 int index = 1; 289 vector<EdgeProjectXYZRGBDPoseOnly*> edges; 290 for ( size_t i=0; i<pts1.size(); i++ ) 291 { 292 EdgeProjectXYZRGBDPoseOnly* edge = new EdgeProjectXYZRGBDPoseOnly( 293 Eigen::Vector3d(pts2[i].x, pts2[i].y, pts2[i].z) ); 294 edge->setId( index ); 295 edge->setVertex( 0, dynamic_cast<g2o::VertexSE3Expmap*> (pose) ); 296 edge->setMeasurement( Eigen::Vector3d( 297 pts1[i].x, pts1[i].y, pts1[i].z) ); 298 edge->setInformation( Eigen::Matrix3d::Identity()*1e4 ); 299 optimizer.addEdge(edge); 300 index++; 301 edges.push_back(edge); 302 } 303 304 chrono::steady_clock::time_point t1 = chrono::steady_clock::now(); 305 optimizer.setVerbose( true ); 306 optimizer.initializeOptimization(); 307 optimizer.optimize(10); 308 chrono::steady_clock::time_point t2 = chrono::steady_clock::now(); 309 chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2-t1); 310 cout<<"optimization costs time: "<<time_used.count()<<" seconds."<<endl; 311 312 cout<<endl<<"after optimization:"<<endl; 313 cout<<"T="<<endl<<Eigen::Isometry3d( pose->estimate() ).matrix()<<endl; 314 315 }
5,三角化與深度估計
第八講:直接法視覺里程計
1,光流法(不需要特徵點)
估計的是畫素點
2,直接法 ,光流法稍加修改。
估計的是相機是怎麼運動的
第九講:實踐
第十講,第十一講:後端優化
第十三講:迴環檢測
第十四講:展望
#include<iostream> #include<opencv2/core/core.hpp> #include<opencv2/features2d/features2d.hpp> #include<opencv2/highgui/highgui.hpp> #include<opencv2/calib3d/calib3d.hpp> // #include "extra.h" // use this if in OpenCV2 using namespace std; using namespace cv;/**************************************************** * 本程式演示瞭如何使用2D-2D的特徵匹配估計相機運動 * **************************************************/
void find_feature_matches ( const Mat& img_1, const Mat& img_2, std::vector<KeyPoint>& keypoints_1, std::vector<KeyPoint>& keypoints_2, std::vector< DMatch >& matches );
void pose_estimation_2d2d ( std::vector<KeyPoint> keypoints_1, std::vector<KeyPoint> keypoints_2, std::vector< DMatch > matches, Mat& R, Mat& t );
// 畫素座標轉相機歸一化座標 Point2d pixel2cam ( const Point2d& p, const Mat& K );
int main ( int argc, char** argv ) { if ( argc != 3 ) { cout<<"usage: pose_estimation_2d2d img1 img2"<<endl; return 1; } //-- 讀取影象 Mat img_1 = imread ( argv[1], CV_LOAD_IMAGE_COLOR ); Mat img_2 = imread ( argv[2], CV_LOAD_IMAGE_COLOR );
vector<KeyPoint> keypoints_1, keypoints_2; vector<DMatch> matches; find_feature_matches ( img_1, img_2, keypoints_1, keypoints_2, matches ); cout<<"一共找到了"<<matches.size() <<"組匹配點"<<endl;
//-- 估計兩張影象間運動 Mat R,t; pose_estimation_2d2d ( keypoints_1, keypoints_2, matches, R, t );
//-- 驗證E=t^R*scale Mat t_x = ( Mat_<double> ( 3,3 ) << 0, -t.at<double> ( 2,0 ), t.at<double> ( 1,0 ), t.at<double> ( 2,0 ), 0, -t.at<double> ( 0,0 ), -t.at<double> ( 1.0 ), t.at<double> ( 0,0 ), 0 );
cout<<"t^R="<<endl<<t_x*R<<endl;
//-- 驗證對極約束 Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 ); for ( DMatch m: matches ) { Point2d pt1 = pixel2cam ( keypoints_1[ m.queryIdx ].pt, K ); Mat y1 = ( Mat_<double> ( 3,1 ) << pt1.x, pt1.y, 1 ); Point2d pt2 = pixel2cam ( keypoints_2[ m.trainIdx ].pt, K ); Mat y2 = ( Mat_<double> ( 3,1 ) << pt2.x, pt2.y, 1 ); Mat d = y2.t() * t_x * R * y1; cout << "epipolar constraint = " << d << endl; } return 0; }
void find_feature_matches ( const Mat& img_1, const Mat& img_2, std::vector<KeyPoint>& keypoints_1, std::vector<KeyPoint>& keypoints_2, std::vector< DMatch >& matches ) { //-- 初始化 Mat descriptors_1, descriptors_2; // used in OpenCV3 Ptr<FeatureDetector> detector = ORB::create(); Ptr<DescriptorExtractor> descriptor = ORB::create(); // use this if you are in OpenCV2 // Ptr<FeatureDetector> detector = FeatureDetector::create ( "ORB" ); // Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create ( "ORB" ); Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create ( "BruteForce-Hamming" ); //-- 第一步:檢測 Oriented FAST 角點位置 detector->detect ( img_1,keypoints_1 ); detector->detect ( img_2,keypoints_2 );
//-- 第二步:根據角點位置計算 BRIEF 描述子 descriptor->compute ( img_1, keypoints_1, descriptors_1 ); descriptor->compute ( img_2, keypoints_2, descriptors_2 );
//-- 第三步:對兩幅影象中的BRIEF描述子進行匹配,使用 Hamming 距離 vector<DMatch> match; //BFMatcher matcher ( NORM_HAMMING ); matcher->match ( descriptors_1, descriptors_2, match );
//-- 第四步:匹配點對篩選 double min_dist=10000, max_dist=0;
//找出所有匹配之間的最小距離和最大距離, 即是最相似的和最不相似的兩組點之間的距離 for ( int i = 0; i < descriptors_1.rows; i++ ) { double dist = match[i].distance; if ( dist < min_dist ) min_dist = dist; if ( dist > max_dist ) max_dist = dist; }
printf ( "-- Max dist : %f \n", max_dist ); printf ( "-- Min dist : %f \n", min_dist );
//當描述子之間的距離大於兩倍的最小距離時,即認為匹配有誤.但有時候最小距離會非常小,設定一個經驗值30作為下限. for ( int i = 0; i < descriptors_1.rows; i++ ) { if ( match[i].distance <= max ( 2*min_dist, 30.0 ) ) { matches.push_back ( match[i] ); } } }
Point2d pixel2cam ( const Point2d& p, const Mat& K ) { return Point2d ( ( p.x - K.at<double> ( 0,2 ) ) / K.at<double> ( 0,0 ), ( p.y - K.at<double> ( 1,2 ) ) / K.at<double> ( 1,1 ) ); }
void pose_estimation_2d2d ( std::vector<KeyPoint> keypoints_1, std::vector<KeyPoint> keypoints_2, std::vector< DMatch > matches, Mat& R, Mat& t ) { // 相機內參,TUM Freiburg2 Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );
//-- 把匹配點轉換為vector<Point2f>的形式 vector<Point2f> points1; vector<Point2f> points2;
for ( int i = 0; i < ( int ) matches.size(); i++ ) { points1.push_back ( keypoints_1[matches[i].queryIdx].pt ); points2.push_back ( keypoints_2[matches[i].trainIdx].pt ); }
//-- 計算基礎矩陣 Mat fundamental_matrix; fundamental_matrix = findFundamentalMat ( points1, points2, CV_FM_8POINT ); cout<<"fundamental_matrix is "<<endl<< fundamental_matrix<<endl;
//-- 計算本質矩陣 Point2d principal_point ( 325.1, 249.7 ); //相機光心, TUM dataset標定值 double focal_length = 521; //相機焦距, TUM dataset標定值 Mat essential_matrix; essential_matrix = findEssentialMat ( points1, points2, focal_length, principal_point ); cout<<"essential_matrix is "<<endl<< essential_matrix<<endl;
//-- 計算單應矩陣 Mat homography_matrix; homography_matrix = findHomography ( points1, points2, RANSAC, 3 ); cout<<"homography_matrix is "<<endl<<homography_matrix<<endl;
//-- 從本質矩陣中恢復旋轉和平移資訊. recoverPose ( essential_matrix, points1, points2, R, t, focal_length, principal_point ); cout<<"R is "<<endl<<R<<endl; cout<<"t is "<<endl<<t<<endl; }