[pl-slam]stereoFrame


該類是關於雙目幀的類,包含了提取特征點、線,兩種特征各自的匹配,繪制特征的方法等。

定義的類參數均為public,有:

 1     int frame_idx;      //雙目幀索引
 2     Mat img_l, img_r;   //雙目的左右圖像
 3     Matrix4d Tfw;       //雙目幀的三維變換
 4     Matrix4d DT;        //未知,但在其他類中也經常出現
 5 
 6     Matrix6d Tfw_cov;   //三維變換的協方差矩陣
 7     Vector6d Tfw_cov_eig;   //三維變換的協方差矩陣的對數映射
 8     double   entropy_first;
 9 
10     Matrix6d DT_cov;    //DT的協方差矩陣
11     Vector6d DT_cov_eig;    //DT的協方差矩陣的對數映射
12     double   err_norm;      //誤差的范數
13 
14     vector<PointFeature*> stereo_pt;    //雙目幀的點特征集
15     vector<LineFeature*>  stereo_ls;    //雙目幀的線特征集
16 
17     Mat pdesc_l, pdesc_r, ldesc_l, ldesc_r;     //左右相機的點特征、線特征描述子
18 
19     PinholeStereoCamera* cam;   //相機模型

定義了以下類函數:

 1     StereoFrame();
 2     StereoFrame(const Mat img_l_, const Mat img_r_, const int idx_, PinholeStereoCamera* cam_ );
 3     ~StereoFrame();
 4 
 5     void extractInitialStereoFeatures( int fast_th = 20 );  //對雙目初始幀的提取
 6     void extractStereoFeatures( int fast_th = 20 );         //對雙目幀的提取
 7     //檢測特征,包括線特征和點特征
 8     void detectFeatures(Mat img, vector<KeyPoint> &points, Mat &pdesc, vector<KeyLine> &lines, Mat &ldesc, double min_line_length, int fast_th = 20);
 9     //檢測點特征
10     void detectPointFeatures( Mat img, vector<KeyPoint> &points, Mat &pdesc, int fast_th = 20 );
11     //檢測線特征
12     void detectLineFeatures( Mat img, vector<KeyLine> &lines, Mat &ldesc, double min_line_length );
13     //匹配點特征
14     void matchPointFeatures(BFMatcher* bfm, Mat pdesc_1, Mat pdesc_2, vector<vector<DMatch>> &pmatches_12);
15     //匹配線特征
16     void matchLineFeatures(BFMatcher* bfm, Mat ldesc_1, Mat ldesc_2, vector<vector<DMatch>> &lmatches_12 );
17     //點特征的MAD中位數絕對偏差
18     void pointDescriptorMAD( const vector<vector<DMatch>> matches, double &nn_mad, double &nn12_mad );
19     //線特征的MAD中位數絕對偏差
20     void lineDescriptorMAD( const vector<vector<DMatch>> matches, double &nn_mad, double &nn12_mad );
21     //繪制雙目幀的點特征和線特征
22     Mat  plotStereoFrame();
23     //繪制雙目幀的特征匹配
24     Mat  plotStereoMatches();
25 
26     //求線特征的觀察線段和重投影線段的重合率
27     double lineSegmentOverlapStereo(double spl_obs, double epl_obs, double spl_proj, double epl_proj  );

 1.提取雙目初始幀的特征點和特征線

定義: void extractInitialStereoFeatures( int fast_th = 20 ); //對雙目初始幀的提取 

  1 void StereoFrame::extractInitialStereoFeatures( int fast_th )
  2 {
  3 
  4     // Feature detection and description
  5     // 特征檢測和描述
  6     vector<KeyPoint> points_l, points_r;    //左右點特征集
  7     vector<KeyLine>  lines_l, lines_r;      //左右線特征集
  8     double min_line_length_th = Config::minLineLength() * std::min( cam->getWidth(), cam->getHeight() );
  9     if( Config::lrInParallel() )
 10     {
 11         auto detect_l = async(launch::async, &StereoFrame::detectFeatures, this, img_l, ref(points_l), ref(pdesc_l), ref(lines_l), ref(ldesc_l), min_line_length_th, fast_th  );
 12         auto detect_r = async(launch::async, &StereoFrame::detectFeatures, this, img_r, ref(points_r), ref(pdesc_r), ref(lines_r), ref(ldesc_r), min_line_length_th, fast_th );
 13         detect_l.wait();
 14         detect_r.wait();
 15     }
 16     else
 17     {
 18         //檢測左右img的點特征和線特征
 19         detectFeatures(img_l,points_l,pdesc_l,lines_l,ldesc_l,min_line_length_th,fast_th);
 20         detectFeatures(img_r,points_r,pdesc_r,lines_r,ldesc_r,min_line_length_th,fast_th);
 21     }
 22 
 23     // Points stereo matching
 24     // 雙目點特征匹配
 25     if( Config::hasPoints() && !(points_l.size()==0) && !(points_r.size()==0) )
 26     {
 27         BFMatcher* bfm = new BFMatcher( NORM_HAMMING, false );
 28         vector<vector<DMatch>> pmatches_lr, pmatches_rl, pmatches_lr_;
 29         Mat pdesc_l_;
 30         stereo_pt.clear();  //清除匹配點特征集,因為每一幀需要計算匹配,所以需要做清除處理
 31         // LR and RL matches
 32         if( Config::bestLRMatches() )
 33         {
 34             if( Config::lrInParallel() )
 35             {
 36                 auto match_l = async( launch::async, &StereoFrame::matchPointFeatures, this, bfm, pdesc_l, pdesc_r, ref(pmatches_lr) );
 37                 auto match_r = async( launch::async, &StereoFrame::matchPointFeatures, this, bfm, pdesc_r, pdesc_l, ref(pmatches_rl) );
 38                 match_l.wait();
 39                 match_r.wait();
 40             }
 41             else
 42             {
 43                 bfm->knnMatch( pdesc_l, pdesc_r, pmatches_lr, 2);
 44                 bfm->knnMatch( pdesc_r, pdesc_l, pmatches_rl, 2);
 45             }
 46         }
 47         else
 48             bfm->knnMatch( pdesc_l, pdesc_r, pmatches_lr, 2);
 49         // sort matches by the distance between the best and second best matches
 50         double nn12_dist_th  = Config::minRatio12P();
 51         // resort according to the queryIdx
 52         sort( pmatches_lr.begin(), pmatches_lr.end(), sort_descriptor_by_queryIdx() );
 53         if(Config::bestLRMatches())
 54             sort( pmatches_rl.begin(), pmatches_rl.end(), sort_descriptor_by_queryIdx() );
 55         // bucle around pmatches
 56         int pt_idx = 0;     //初始化提取雙目特征時獨有的
 57         for( int i = 0; i < pmatches_lr.size(); i++ )
 58         {
 59             int lr_qdx, lr_tdx, rl_tdx;
 60             lr_qdx = pmatches_lr[i][0].queryIdx;
 61             lr_tdx = pmatches_lr[i][0].trainIdx;
 62             if( Config::bestLRMatches() )
 63             {
 64                 // check if they are mutual best matches
 65                 rl_tdx = pmatches_rl[lr_tdx][0].trainIdx;
 66             }
 67             else
 68                 rl_tdx = lr_qdx;
 69             // check if they are mutual best matches and the minimum distance
 70             double dist_12 = pmatches_lr[i][0].distance / pmatches_lr[i][1].distance;
 71             if( lr_qdx == rl_tdx  && dist_12 > nn12_dist_th )
 72             {
 73                 // check stereo epipolar constraint
 74                 // 檢測極線約束,因為雙目兩個相機水平放置,因此如果滿足極線約束,兩個特征點的y坐標應該相等
 75                 if( fabsf( points_l[lr_qdx].pt.y-points_r[lr_tdx].pt.y) <= Config::maxDistEpip() )
 76                 {
 77                     // check minimal disparity
 78                     double disp_ = points_l[lr_qdx].pt.x - points_r[lr_tdx].pt.x;
 79                     if( disp_ >= Config::minDisp() ){
 80                         pdesc_l_.push_back( pdesc_l.row(lr_qdx) );
 81                         PointFeature* point_;
 82                         Vector2d pl_; pl_ << points_l[lr_qdx].pt.x, points_l[lr_qdx].pt.y;
 83                         Vector3d P_;  P_ = cam->backProjection( pl_(0), pl_(1), disp_);
 84                         stereo_pt.push_back( new PointFeature(pl_,disp_,P_,pt_idx,points_l[lr_qdx].octave) );
 85                         pt_idx++;   //初始化提取雙目特征時獨有的
 86                     }
 87                 }
 88             }
 89         }
 90         pdesc_l_.copyTo(pdesc_l);
 91     }
 92 
 93     // Line segments stereo matching
 94     // 線特征匹配
 95     if( Config::hasLines() && !lines_l.empty() && !lines_r.empty() )
 96     {
 97         stereo_ls.clear();  //和點特征類似,在每一次檢測匹配之前,都需要先清除線特征匹配集
 98         BFMatcher* bfm = new BFMatcher( NORM_HAMMING, false );
 99         vector<vector<DMatch>> lmatches_lr, lmatches_rl;
100         Mat ldesc_l_;
101         // LR and RL matches
102         if( Config::bestLRMatches() )
103         {
104             if( Config::lrInParallel() )
105             {
106                 auto match_l = async( launch::async, &StereoFrame::matchLineFeatures, this, bfm, ldesc_l, ldesc_r, ref(lmatches_lr) );
107                 auto match_r = async( launch::async, &StereoFrame::matchLineFeatures, this, bfm, ldesc_r, ldesc_l, ref(lmatches_rl) );
108                 match_l.wait();
109                 match_r.wait();
110             }
111             else
112             {
113                 bfm->knnMatch( ldesc_l,ldesc_r, lmatches_lr, 2);
114                 bfm->knnMatch( ldesc_r,ldesc_l, lmatches_rl, 2);
115             }
116         }
117         else
118             bfm->knnMatch( ldesc_l,ldesc_r, lmatches_lr, 2);
119         // sort matches by the distance between the best and second best matches
120         double nn_dist_th, nn12_dist_th;
121         lineDescriptorMAD(lmatches_lr,nn_dist_th, nn12_dist_th);        
122         nn12_dist_th  = nn12_dist_th * Config::descThL();
123         // bucle around pmatches
124         sort( lmatches_lr.begin(), lmatches_lr.end(), sort_descriptor_by_queryIdx() );
125         if( Config::bestLRMatches() )
126             sort( lmatches_rl.begin(), lmatches_rl.end(), sort_descriptor_by_queryIdx() );
127         int n_matches;
128         if( Config::bestLRMatches() )
129             n_matches = min(lmatches_lr.size(),lmatches_rl.size());
130         else
131             n_matches = lmatches_lr.size();
132         int ls_idx = 0;     //初始化提取雙目特征時獨有的
133         for( int i = 0; i < n_matches; i++ )
134         {
135             // check if they are mutual best matches ( if bestLRMatches() )
136             int lr_qdx = lmatches_lr[i][0].queryIdx;
137             int lr_tdx = lmatches_lr[i][0].trainIdx;
138             int rl_tdx;
139             if( Config::bestLRMatches() )
140                 rl_tdx = lmatches_rl[lr_tdx][0].trainIdx;
141             else
142                 rl_tdx = lr_qdx;
143             // check if they are mutual best matches and the minimum distance
144             double dist_12 = lmatches_lr[i][1].distance - lmatches_lr[i][0].distance;
145             double length  = lines_r[lr_tdx].lineLength;
146             if( lr_qdx == rl_tdx && dist_12 > nn12_dist_th )
147             {
148                 // estimate the disparity of the endpoints
149                 // 計算左img線特征重投影后的端點距離
150                 Vector3d sp_l; sp_l << lines_l[lr_qdx].startPointX, lines_l[lr_qdx].startPointY, 1.0;
151                 Vector3d ep_l; ep_l << lines_l[lr_qdx].endPointX,   lines_l[lr_qdx].endPointY,   1.0;
152                 // le_l是sp_l和ep_l的叉乘的單位向量
153                 Vector3d le_l; le_l << sp_l.cross(ep_l); le_l = le_l / sqrt( le_l(0)*le_l(0) + le_l(1)*le_l(1) );
154 
155                 // 計算右img線特征重投影后的端點距離
156                 Vector3d sp_r; sp_r << lines_r[lr_tdx].startPointX, lines_r[lr_tdx].startPointY, 1.0;
157                 Vector3d ep_r; ep_r << lines_r[lr_tdx].endPointX,   lines_r[lr_tdx].endPointY,   1.0;
158                 // le_r是sp_r和ep_r的叉乘向量
159                 Vector3d le_r; le_r << sp_r.cross(ep_r);
160 
161                 // 計算左右img的投影線段的重合比率
162                 double overlap = lineSegmentOverlapStereo( sp_l(1), ep_l(1), sp_r(1), ep_r(1) );
163 
164                 // 計算過程還沒明白,但是如果按照論文的意思,應該是計算線段的投影點到其投影直線的距離,將其作為偏差
165                 sp_r << - (le_r(2)+le_r(1)*lines_l[lr_qdx].startPointY )/le_r(0) , lines_l[lr_qdx].startPointY ,  1.0;
166                 ep_r << - (le_r(2)+le_r(1)*lines_l[lr_qdx].endPointY   )/le_r(0) , lines_l[lr_qdx].endPointY ,    1.0;
167                 double disp_s = lines_l[lr_qdx].startPointX - sp_r(0);
168                 double disp_e = lines_l[lr_qdx].endPointX   - ep_r(0);
169                 // check minimal disparity
170                 if( disp_s >= Config::minDisp() && disp_e >= Config::minDisp()
171                         && fabsf(le_r(0)) > Config::lineHorizTh()
172                         && overlap > Config::stereoOverlapTh() )
173                 {
174                     ldesc_l_.push_back( ldesc_l.row(lr_qdx) );
175                     Vector3d sP_; sP_ = cam->backProjection( sp_l(0), sp_l(1), disp_s);
176                     Vector3d eP_; eP_ = cam->backProjection( ep_l(0), ep_l(1), disp_e);
177                     double angle_l = lines_l[lr_qdx].angle;
178                     stereo_ls.push_back( new LineFeature(Vector2d(sp_l(0),sp_l(1)),disp_s,sP_,Vector2d(ep_l(0),ep_l(1)),
179                                                          disp_e,eP_,le_l,angle_l,ls_idx, lines_l[lr_qdx].octave) );
180                     ls_idx++;       //初始化提取雙目特征時獨有的
181                 }
182             }
183         }
184         ldesc_l_.copyTo(ldesc_l);
185     }
186 
187 }
View Code

其中的點、線特征匹配和stereoFrameHandler中的f2fTracking函數差不多,用互匹配選擇較好的特征。

2.提取雙目幀的特征點和特征線

定義: void extractStereoFeatures( int fast_th = 20 ); //對雙目幀的提取 

過程和提取初始幀的特征點和特征線基本一樣,不同:

(1)對於初始幀,幀數為0,所以在對雙目初始幀提取線特征和點特征時的索引都有變化,在上一個代碼中有標示出來;

(2)在這個函數中,添加雙目幀的線特征前,需要做一次判斷,當線特征的兩個端點的最大協方差矩陣的特征值小於設定值時,才添加這次的線特征。

3.檢測img特征,包括特征點和特征線段

定義:  void detectFeatures(Mat img, vector<KeyPoint> &points, Mat &pdesc, vector<KeyLine> &lines, Mat &ldesc, double min_line_length, int fast_th = 20); 

根據Config::plInParallel()判斷是否同時提取特征點和特征線段來決定采用哪段程序:

 1 /**@brief 提取特征:包括點特征和線特征
 2  * @param img 待檢測的圖像
 3  * @param points 檢測到的特征點集
 4  * @param pdesc 特征點的描述子
 5  * @param lines 檢測到的特征線集
 6  * @param ldesc 特征線的描述子
 7  * @param min_line_length 特征線段的最小長度
 8  * @param fast_th 特征點檢測的參數
 9  */
10 void StereoFrame::detectFeatures(Mat img, vector<KeyPoint> &points, Mat &pdesc, vector<KeyLine> &lines, Mat &ldesc, double min_line_length, int fast_th)
11 {
12 
13     if( Config::plInParallel() && Config::hasPoints() && Config::hasLines() )
14     {
15         //如果是並行計算特征點和特征線段,就需要分別調用檢測特征點和特征線段的函數
16         auto detect_l = async(launch::async, &StereoFrame::detectLineFeatures,  this, img, ref(lines),  ref(ldesc), min_line_length );
17         auto detect_p = async(launch::async, &StereoFrame::detectPointFeatures, this, img, ref(points), ref(pdesc), fast_th );
18         detect_l.wait();
19         detect_p.wait();
20     }
21     else
22     {
23         // Detect point features
24         if( Config::hasPoints() )
25         {
26             int fast_th_ = Config::orbFastTh();
27             if( fast_th != 0 )
28                 fast_th_ = fast_th;
29             Ptr<ORB> orb = ORB::create( Config::orbNFeatures(), Config::orbScaleFactor(), Config::orbNLevels(),
30                                         Config::orbEdgeTh(), 0, Config::orbWtaK(), Config::orbScore(),
31                                         Config::orbPatchSize(), fast_th_ );
32             orb->detectAndCompute( img, Mat(), points, pdesc, false);
33         }
34         // Detect line features
35         Ptr<BinaryDescriptor>   lbd = BinaryDescriptor::createBinaryDescriptor();
36         if( Config::hasLines() )
37         {
38             Ptr<line_descriptor::LSDDetectorC> lsd = line_descriptor::LSDDetectorC::createLSDDetectorC();
39             // lsd parameters
40             line_descriptor::LSDDetectorC::LSDOptions opts;
41             opts.refine       = Config::lsdRefine();
42             opts.scale        = Config::lsdScale();
43             opts.sigma_scale  = Config::lsdSigmaScale();
44             opts.quant        = Config::lsdQuant();
45             opts.ang_th       = Config::lsdAngTh();
46             opts.log_eps      = Config::lsdLogEps();
47             opts.density_th   = Config::lsdDensityTh();
48             opts.n_bins       = Config::lsdNBins();
49             opts.min_length   = min_line_length;
50             lsd->detect( img, lines, Config::lsdScale(), 1, opts);  //檢測特征線段
51             // filter lines
52             // 濾出一些特征線
53             if( lines.size()>Config::lsdNFeatures() && Config::lsdNFeatures()!=0  )
54             {
55                 // sort lines by their response
56                 sort( lines.begin(), lines.end(), sort_lines_by_response() );
57                 //sort( lines.begin(), lines.end(), sort_lines_by_length() );
58                 lines.resize(Config::lsdNFeatures());
59                 // reassign index
60                 for( int i = 0; i < Config::lsdNFeatures(); i++  )
61                     lines[i].class_id = i;
62             }
63             lbd->compute( img, lines, ldesc);   //計算特征線段的描述子
64         }
65     }
66 
67 }
View Code

其中,如果采用的是並行提取特征,需要分別調用檢測特征點的代碼:  void detectPointFeatures( Mat img, vector<KeyPoint> &points, Mat &pdesc, int fast_th = 20 ); 

以及檢測特征線段的代碼: void detectLineFeatures( Mat img, vector<KeyLine> &lines, Mat &ldesc, double min_line_length ); 

過程和方法與上面 detectFeatures()函數一樣。

4.匹配特征點和匹配特征線段

 1 void StereoFrame::matchPointFeatures(BFMatcher* bfm, Mat pdesc_1, Mat pdesc_2, vector<vector<DMatch>> &pmatches_12  )
 2 {
 3     // 對每一個pdesc_1,在pdesc_2中尋找最近的2個描述子
 4     bfm->knnMatch( pdesc_1, pdesc_2, pmatches_12, 2);
 5 }
 6 
 7 void StereoFrame::matchLineFeatures(BFMatcher* bfm, Mat ldesc_1, Mat ldesc_2, vector<vector<DMatch>> &lmatches_12  )
 8 {
 9     // 對每一個ldesc_1,在ldesc_2中尋找最近的2個描述子
10     bfm->knnMatch( ldesc_1, ldesc_2, lmatches_12, 2);
11 }

需要注意的就是兩個匹配都采用的是knnMatch()方法,該方法是用於對query集合中每一個描述子,在train集合中尋找最好的k個匹配,這段代碼里面的k=2。是用於后面篩選匹配,最優/次優的ratio要達到一個比值才算好。

 


免責聲明!

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



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