天天看點

orb-slam系列 LoopClosing線程 ComputeSim3(十一)

ComputeSim3

/********************************
 * 	計算每一個回環候選關鍵幀與目前關鍵幀之間的相似矩陣
 * 		1. 首先通過BOW向量将回環候選關鍵幀和目前關鍵幀進行比對,得到比對地圖點,通過比對地圖點初始化相似矩陣求解器
 * 		2. 周遊所有的回環候選關鍵幀和目前關鍵幀計算sim矩陣,并優化sim矩陣,根據優化sim矩陣确定比對内點數量,進而确定此sim矩陣的準确性,以及是否可以判定為一回環.
 * 		3. 在找到的回環關鍵幀周圍查找共視關鍵幀   并比對共視關鍵幀的地圖點和目前關鍵幀  相當與是比對covisual graph和目前關鍵幀  根據比對點數量确定目前幀是否發生了回環
 **************************************/
bool LoopClosing::ComputeSim3()
{
    // For each consistent loop candidate we try to compute a Sim3

    const int nInitialCandidates = mvpEnoughConsistentCandidates.size();  //候選回環幀

    // We compute first ORB matches for each candidate
    // If enough matches are found, we setup a Sim3Solver
    ORBmatcher matcher(0.75,true);
    // 相似性矩陣求解器
    vector<Sim3Solver*> vpSim3Solvers;
    vpSim3Solvers.resize(nInitialCandidates);
    // 比對地圖點容器   
    vector<vector<MapPoint*> > vvpMapPointMatches;   //vvpMapPointMatches[i][j]   候選回環幀i 對應于 待回環幀第j地圖點 對應的地圖點
    vvpMapPointMatches.resize(nInitialCandidates);
    // 取消候選回環關鍵幀的标志變量
    vector<bool> vbDiscarded;
    vbDiscarded.resize(nInitialCandidates);

    int nCandidates=0; //candidates with enough matches
    // 初始化目前幀和候選關鍵幀的相似矩陣求解器,計算比對地圖點,取消回環關鍵幀标志變量的賦初值
    for(int i=0; i<nInitialCandidates; i++)
    {
        KeyFrame* pKF = mvpEnoughConsistentCandidates[i];

        // avoid that local mapping erase it while it is being processed in this thread
        pKF->SetNotErase();

        if(pKF->isBad())
        {
            vbDiscarded[i] = true;
            continue;
        }
	// 待回環關鍵幀與回環候選關鍵幀進行比對得到比對地圖點
        int nmatches = matcher.SearchByBoW(mpCurrentKF,pKF,vvpMapPointMatches[i]);

        if(nmatches<20)
        {
            vbDiscarded[i] = true;
            continue;
        }
        else
        {
	  //相似性矩陣的求解器初始化
            Sim3Solver* pSolver = new Sim3Solver(mpCurrentKF,pKF,vvpMapPointMatches[i],mbFixScale);
            pSolver->SetRansacParameters(0.99,20,300);
            vpSim3Solvers[i] = pSolver;
        }

        nCandidates++;
    }

    bool bMatch = false;

    // Perform alternatively RANSAC iterations for each candidate
    // until one is succesful or all fail
    // RANSAC疊代每一個回環候選關鍵幀和目前待回環關鍵幀
    // 通過SearchByBoW比對得到初步比對點,根據此比對計算兩關鍵幀之間的sim矩陣
    // 
    while(nCandidates>0 && !bMatch)
    {   
        for(int i=0; i<nInitialCandidates; i++)
        {
            if(vbDiscarded[i])  //小于20
                continue;

            KeyFrame* pKF = mvpEnoughConsistentCandidates[i];

            // Perform 5 Ransac Iterations
            vector<bool> vbInliers;
            int nInliers;
            bool bNoMore;

            Sim3Solver* pSolver = vpSim3Solvers[i];
            cv::Mat Scm  = pSolver->iterate(5,bNoMore,vbInliers,nInliers);

            // If Ransac reachs max. iterations discard keyframe   如果疊代次數達到最大  則證明目前候選幀不是回環幀
            if(bNoMore)
            {
                vbDiscarded[i]=true;
                nCandidates--;
            }

            // If RANSAC returns a Sim3, perform a guided matching and optimize with all correspondences
            // 如果RANSAC求取了一個合适的相似變換矩陣sim   則通過sim矩陣重新進行地圖點比對,并優化sim矩陣并确定内點  根據内點數量判定sim矩陣是否符合要求
            if(!Scm.empty())
            {
                vector<MapPoint*> vpMapPointMatches(vvpMapPointMatches[i].size(), static_cast<MapPoint*>(NULL));    //比對  待回環幀i幀 對應的兩地圖點 =>> vpMapPointMatches[i] = loopkfi.map[j] 此幀i點 對應于 回環幀的j點
                for(size_t j=0, jend=vbInliers.size(); j<jend; j++)
                {
                    if(vbInliers[j])
                       vpMapPointMatches[j]=vvpMapPointMatches[i][j];   //第i候選回環幀 中 對應待回環幀 j 點的地圖點
                }
		
                cv::Mat R = pSolver->GetEstimatedRotation();
                cv::Mat t = pSolver->GetEstimatedTranslation();
                const float s = pSolver->GetEstimatedScale();
                matcher.SearchBySim3(mpCurrentKF,pKF,vpMapPointMatches,s,R,t,7.5);

                g2o::Sim3 gScm(Converter::toMatrix3d(R),Converter::toVector3d(t),s);   //sim3  current點到 回環幀點的sim3
                const int nInliers = Optimizer::OptimizeSim3(mpCurrentKF, pKF, vpMapPointMatches, gScm, 10, mbFixScale);

                // If optimization is succesful stop ransacs and continue  
                if(nInliers>=20)
                {
                    bMatch = true;
                    mpMatchedKF = pKF;
                    g2o::Sim3 gSmw(Converter::toMatrix3d(pKF->GetRotation()),Converter::toVector3d(pKF->GetTranslation()),1.0);
                    mg2oScw = gScm*gSmw;
                    mScw = Converter::toCvMat(mg2oScw);

                    mvpCurrentMatchedPoints = vpMapPointMatches;
                    break;
                }
            }
        }
    }

    if(!bMatch)
    {
        for(int i=0; i<nInitialCandidates; i++)
             mvpEnoughConsistentCandidates[i]->SetErase();
        mpCurrentKF->SetErase();
        return false;
    }

    // Retrieve MapPoints seen in Loop Keyframe and neighbors   
    // 在找到的回環關鍵幀周圍查找共視關鍵幀   并比對共視關鍵幀的地圖點和目前關鍵幀  相當于是比對covisual graph和目前關鍵幀  改動關聯
    vector<KeyFrame*> vpLoopConnectedKFs = mpMatchedKF->GetVectorCovisibleKeyFrames();
    vpLoopConnectedKFs.push_back(mpMatchedKF);
    mvpLoopMapPoints.clear();
    for(vector<KeyFrame*>::iterator vit=vpLoopConnectedKFs.begin(); vit!=vpLoopConnectedKFs.end(); vit++)
    {
        KeyFrame* pKF = *vit;
        vector<MapPoint*> vpMapPoints = pKF->GetMapPointMatches();     //回環幀及其共視幀的地圖點
        for(size_t i=0, iend=vpMapPoints.size(); i<iend; i++)
        {
            MapPoint* pMP = vpMapPoints[i];
            if(pMP)
            {
                if(!pMP->isBad() && pMP->mnLoopPointForKF!=mpCurrentKF->mnId)
                {
                    mvpLoopMapPoints.push_back(pMP);
                    pMP->mnLoopPointForKF=mpCurrentKF->mnId;
                }
            }
        }
    }

    // Find more matches projecting with the computed Sim3  比對共視關鍵幀的地圖點和目前關鍵幀
    matcher.SearchByProjection(mpCurrentKF, mScw, mvpLoopMapPoints, mvpCurrentMatchedPoints,10);

    // If enough matches accept Loop   如果有足夠的比對點 則說明找到了回環,否則查找回環失敗
    int nTotalMatches = 0;
    for(size_t i=0; i<mvpCurrentMatchedPoints.size(); i++)
    {
        if(mvpCurrentMatchedPoints[i])
            nTotalMatches++;
    }

    if(nTotalMatches>=40)
    {
        for(int i=0; i<nInitialCandidates; i++)
            if(mvpEnoughConsistentCandidates[i]!=mpMatchedKF)  //回環幀是 mpMatchedKF
                mvpEnoughConsistentCandidates[i]->SetErase();
        return true;
    }
    else
    {
        for(int i=0; i<nInitialCandidates; i++)
            mvpEnoughConsistentCandidates[i]->SetErase();
        mpCurrentKF->SetErase();
        return false;
    }

}
           

1)每一個候選回環幀都要跟待回環幀進行bow的詞袋模型的比對,并且得到比對點。

(同一單詞中兩幀的關鍵點進行最佳比對)

2)比對點數大于等于20的時候進行sim3計算。

3)通過pSolver->iterate(5,bNoMore,vbInliers,nInliers); 計算出Scm

在坐标系 幀一幀二中任意選3個對點 計算sim3算法(也可以icp計算)得到位姿變化 T12

4)matcher.SearchBySim3(mpCurrentKF,pKF,vpMapPointMatches,s,R,t,7.5); 進行sim3驗證

雙向比對:

根據相似矩陣将關鍵幀1中的地圖點向關鍵幀2中投影,确定投影區域,并在投影區域内尋找關鍵幀1中地圖點的比對

根據相似矩陣将關鍵幀2中的地圖點向關鍵幀1中投影,确定投影區域,并在投影區域内尋找關鍵幀2中地圖點的比對

根據雙向比對結果,如果兩次比對都能成功,則确定該對比對是有效的.将其存入vpMatches12容器

最終傳回比對點對個數

幀一的地圖三維點,在相機一的坐标,用優化的T12轉化到相機二的坐标。驗證幀二是否有這個像素點,幀二的門檻值範圍内找特征點比對,找到最佳比對。

雙向 同理。

都成功是比對點。

5)const int nInliers = Optimizer::OptimizeSim3(mpCurrentKF, pKF, vpMapPointMatches, gScm, 10, mbFixScale)

優化的政策

關鍵點1的uv值- K1 * t12 * p3dc2

同理 關鍵點2的uv值- K2 * t21 * p3dc1

優化得到sim3

6)修正

g2o::Sim3 gSmw(Converter::toMatrix3d(pKF->GetRotation()),Converter::toVector3d(pKF->GetTranslation()),1.0);
                    mg2oScw = gScm*gSmw;
           

因為gSmw是待回環幀在世界坐标系的位姿,計算出來目前幀 在正确世界坐标系的位姿

7)mvpCurrentMatchedPoints是目前幀和 回環幀的比對點。mpMatchedKF是回環幀

8)mvpLoopMapPoints 回環幀及其共視幀的地圖點 vpLoopConnectedKFs回環幀及其共視幀

9)matcher.SearchByProjection(mpCurrentKF, mScw, mvpLoopMapPoints, mvpCurrentMatchedPoints,10);

注意 裡面3d點的來源是 mvpLoopMapPoints 也就是正确的位置3d點

p3Dc得到了在目前相機坐标系下的坐标,門檻值範圍内找最佳比對 傳回比對點數目

10)數額足夠 完成

繼續閱讀