• ORB-SLAM(八)ORBmatcher 特征匹配


    该类负责特征点与特征点之间,地图点与特征点之间通过投影关系、词袋模型或者Sim3位姿匹配。用来辅助完成单目初始化,三角化恢复新的地图点,tracking,relocalization以及loop closing,因此比较重要。

    该类提供的API是:

    1. 几个重载的SearchByProjection函数(第一个形参代表需要在其中寻找匹配点的当前图像帧/query;第二个形参则包含待匹配特征/train),用于

      a. 跟踪局部地图(在局部地图中寻找与当前帧特征点匹配的)。因为在TrackReferenceKeyFrame和TrackWithMotionModel中,仅仅是两帧之间跟踪,会跟丢地图点,这里通过跟踪局部地图,在当前帧中恢复出一些当前帧的地图点。  其中的阈值th一般根据单目还是双目,或者最近有没有进行过重定位来确定,代表在投影点的这个平面阈值范围内寻找匹配特征点。匹配点不仅需要满足对极几何,初始位姿的约束;还需要满足描述子之间距离较小。

    int ORBmatcher::SearchByProjection(Frame &F, const vector<MapPoint*> &vpMapPoints, const float th);

      b. 匹配上一帧的地图点,即前后两帧匹配,用于TrackWithMotionModel。

    int ORBmatcher::SearchByProjection(Frame &CurrentFrame, const Frame &LastFrame, const float th, const bool bMono);

      c. 在当前帧中匹配所有关键帧中的地图点,用于Relocalization。

    int ORBmatcher::SearchByProjection(Frame &CurrentFrame, KeyFrame *pKF, const set<MapPoint*> &sAlreadyFound, const float th , const int ORBdist);

      d. 在当前关键帧中匹配所有关键帧中的地图点,需要计算sim3,用于Loop Closing。

    int ORBmatcher::SearchByProjection(KeyFrame* pKF, cv::Mat Scw, const vector<MapPoint*> &vpPoints, vector<MapPoint*> &vpMatched, int th);

    2. 两个重载的SearchByBow函数(注意这里形参表示的匹配的主被动关系和SearchByProjection是反的),用于

      a. 在当前帧中匹配关键帧中的地图点,用于TrackReferenceKeyFrame和Relocalization。

    int ORBmatcher::SearchByBoW(KeyFrame* pKF,Frame &F, vector<MapPoint*> &vpMapPointMatches);

      b. 在当前关键帧中匹配所有关键帧中的地图点,用于Loop Closing。

    int ORBmatcher::SearchByBoW(KeyFrame *pKF1, KeyFrame *pKF2, vector<MapPoint *> &vpMatches12);

    3. 用于单目初始化的SearchForInitialization,以及利用三角化,在两个关键帧之间恢复出一些地图点SearchForTriangulation。

    int ORBmatcher::SearchForInitialization(Frame &F1, Frame &F2, vector<cv::Point2f> &vbPrevMatched, vector<int> &vnMatches12, int windowSize);
    int ORBmatcher::SearchForTriangulation(KeyFrame *pKF1, KeyFrame *pKF2, cv::Mat F12,
                                           vector<pair<size_t, size_t> > &vMatchedPairs, const bool bOnlyStereo);

    4. 两个重载的Fuse函数,用于地图点的融合:

      地图点能匹配上当前关键帧的地图点,也就是地图点重合了,选择观测数多的地图点替换;地图点能匹配上当前帧的特征点,但是该特征点还没有生成地图点,则生成新的地图点)。

      重载的函数是为了减小尺度漂移的影响,需要知道当前关键帧的sim3位姿。

    int ORBmatcher::Fuse(KeyFrame *pKF, const vector<MapPoint *> &vpMapPoints, const float th);
    int ORBmatcher::Fuse(KeyFrame *pKF, cv::Mat Scw, const vector<MapPoint *> &vpPoints, float th, vector<MapPoint *> &vpReplacePoint);

    5. 计算描述子之间的hanmming距离

    int ORBmatcher::DescriptorDistance(const cv::Mat &a, const cv::Mat &b);

    选取其中一个用于Relocalization的投影匹配着重理解。疑问是,何时用投影匹配,何时用DBow2进行匹配?在Relocalization和LoopClosing中进行匹配的是在很多帧关键帧集合中匹配,属于Place Recognition,因此需要用DBow,而投影匹配适用于两帧之间,或者投影范围内(局部地图,前一个关键帧对应地图点)的MapPoints与当前帧之间。

    int ORBmatcher::SearchByProjection(Frame &CurrentFrame, KeyFrame *pKF, const set<MapPoint*> &sAlreadyFound, const float th , const int ORBdist);

    用关键帧pKF的地图点投影匹配当前帧的特征点:

    // For Relocalization
    //
    // 1. 获取pKF对应的地图点vpMPs,遍历
    //     (1). 若该点为NULL、isBad或者在SearchByBow中已经匹配上(Relocalization中首先会通过SearchByBow匹配一次),抛弃;
    // 2. 通过当前帧的位姿,将世界坐标系下的地图点坐标转换为当前帧坐标系(相机坐标系)下的坐标
    //     (2). 投影点(u,v)不在畸变矫正过的图像范围内,地图点的距离dist3D不在地图点的可观测距离内(根据地图点对应的金字塔层数,
    //           也就是提取特征的neighbourhood尺寸),抛弃
    // 3. 通过地图点的距离dist3D,预测特征对应金字塔层nPredictedLevel,并获取搜索window大小(th*scale),在以上约束的范围内,
    //    搜索得到候选匹配点集合向量vIndices2
    //     const vector<size_t> vIndices2 = CurrentFrame.GetFeaturesInArea(u, v, radius, nPredictedLevel-1, nPredictedLevel+1);
    // 4. 计算地图点的描述子和候选匹配点描述子距离,获得最近距离的最佳匹配,但是也要满足距离<ORBdist。
    // 5. 最后,还需要通过直方图验证描述子的方向是否匹配
    int ORBmatcher::SearchByProjection(Frame &CurrentFrame, KeyFrame *pKF, const set<MapPoint*> &sAlreadyFound, const float th , const int ORBdist)
    {
        int nmatches = 0;
    
        const cv::Mat Rcw = CurrentFrame.mTcw.rowRange(0,3).colRange(0,3);
        const cv::Mat tcw = CurrentFrame.mTcw.rowRange(0,3).col(3);
        const cv::Mat Ow = -Rcw.t()*tcw;
    
        // Rotation Histogram (to check rotation consistency)
        vector<int> rotHist[HISTO_LENGTH];
        for(int i=0;i<HISTO_LENGTH;i++)
            rotHist[i].reserve(500);
        const float factor = 1.0f/HISTO_LENGTH;
    
        const vector<MapPoint*> vpMPs = pKF->GetMapPointMatches();
    
        for(size_t i=0, iend=vpMPs.size(); i<iend; i++)
        {
            MapPoint* pMP = vpMPs[i];
    
            if(pMP)
            {
                // before this, Relocalization has already execute SearchByBoW, those matched was inserted into sAlreadyFound
                if(!pMP->isBad() && !sAlreadyFound.count(pMP))
                {
                    //Project
                    cv::Mat x3Dw = pMP->GetWorldPos();
                    cv::Mat x3Dc = Rcw*x3Dw+tcw;
    
                    const float xc = x3Dc.at<float>(0);
                    const float yc = x3Dc.at<float>(1);
                    const float invzc = 1.0/x3Dc.at<float>(2);
    
                    const float u = CurrentFrame.fx*xc*invzc+CurrentFrame.cx;
                    const float v = CurrentFrame.fy*yc*invzc+CurrentFrame.cy;
                    // u,v是关键帧中地图点在当前帧上的投影点
                    if(u<CurrentFrame.mnMinX || u>CurrentFrame.mnMaxX)
                        continue;
                    if(v<CurrentFrame.mnMinY || v>CurrentFrame.mnMaxY)
                        continue;
    
                    // Compute predicted scale level
                    cv::Mat PO = x3Dw-Ow;
                    float dist3D = cv::norm(PO);
    
                    const float maxDistance = pMP->GetMaxDistanceInvariance();
                    const float minDistance = pMP->GetMinDistanceInvariance();
    
                    // Depth must be inside the scale pyramid of the image
                    if(dist3D<minDistance || dist3D>maxDistance)
                        continue;
    
                    int nPredictedLevel = pMP->PredictScale(dist3D,&CurrentFrame);
    
                    // Search in a window
                    const float radius = th*CurrentFrame.mvScaleFactors[nPredictedLevel];
    
                    const vector<size_t> vIndices2 = CurrentFrame.GetFeaturesInArea(u, v, radius, nPredictedLevel-1, nPredictedLevel+1);
    
                    if(vIndices2.empty())
                        continue;
    
                    const cv::Mat dMP = pMP->GetDescriptor();
    
                    int bestDist = 256;
                    int bestIdx2 = -1;
    
                    for(vector<size_t>::const_iterator vit=vIndices2.begin(); vit!=vIndices2.end(); vit++)
                    {
                        const size_t i2 = *vit;
                        if(CurrentFrame.mvpMapPoints[i2])
                            continue;
    
                        const cv::Mat &d = CurrentFrame.mDescriptors.row(i2);
    
                        const int dist = DescriptorDistance(dMP,d);
    
                        if(dist<bestDist)
                        {
                            bestDist=dist;
                            bestIdx2=i2;
                        }
                    }
    
                    if(bestDist<=ORBdist)
                    {
                        CurrentFrame.mvpMapPoints[bestIdx2]=pMP;
                        nmatches++;
    
                        if(mbCheckOrientation)
                        {
                            float rot = pKF->mvKeysUn[i].angle-CurrentFrame.mvKeysUn[bestIdx2].angle;
                            if(rot<0.0)
                                rot+=360.0f;
                            int bin = round(rot*factor);
                            if(bin==HISTO_LENGTH)
                                bin=0;
                            assert(bin>=0 && bin<HISTO_LENGTH);
                            rotHist[bin].push_back(bestIdx2);
                        }
                    }
    
                }
            }
        }
    
        if(mbCheckOrientation)
        {
            int ind1=-1;
            int ind2=-1;
            int ind3=-1;
    
            ComputeThreeMaxima(rotHist,HISTO_LENGTH,ind1,ind2,ind3);
    
            for(int i=0; i<HISTO_LENGTH; i++)
            {
                if(i!=ind1 && i!=ind2 && i!=ind3)
                {
                    for(size_t j=0, jend=rotHist[i].size(); j<jend; j++)
                    {
                        CurrentFrame.mvpMapPoints[rotHist[i][j]]=NULL;
                        nmatches--;
                    }
                }
            }
        }
    
        return nmatches;
    }

    其中角度直方图是用来剔除不满足两帧之间角度旋转的外点的,也就是所谓的旋转一致性检测

    1. 将关键帧与当前帧匹配点的angle相减,得到rot(0<=rot<360),放入一个直方图中,对于每一对匹配点的角度差,均可以放入一个bin的范围内(360/HISTO_LENGTH)。

    2. 统计直方图最高的三个bin保留,其他范围内的匹配点剔除。另外,若最高的比第二高的高10倍以上,则只保留最高的bin中的匹配点。

    最后该函数会

    1. 为当前帧生成和关键帧匹配上的地图点

    2. 统计通过投影匹配上的点

    CurrentFrame.mvpMapPoints[bestIdx2]=pMP;
    nmatches++;
  • 相关阅读:
    YearsBetween、MonthsBetween ... YearSpan、MonthSpan ... 间隔时间
    SysUtilsFunction
    DateOf、TimeOf、YearOf、MonthOf、WeekOf、DayOf、HourOf、MinuteOf、SecondOf、MilliSecondOf 提取时间成分
    MathFunction
    DateUtilsFunction
    关于发表评论时的“无法验证数据”的错误
    用ISAPI_Rewrite实现反向代理(ReverseProxy)
    [公告]取消了后台管理中的统计功能
    推荐一个不错的SharePoint文档库树形显示WebPart及谈谈写Blog的好处
    [公告]镜像站点可以登录并发表文章
  • 原文地址:https://www.cnblogs.com/shang-slam/p/6431017.html
Copyright © 2020-2023  润新知