/该类负责特征点与特征点之间,地图点与特征点之间通过投影关系、词袋模型或者Sim3位姿匹配
class ORBmatcher
{
public:
/**
* 找到在 以x, y为中心,边长为2r的方形内且尺度在[minLevel, maxLevel]的特征点
* @param nnratio 匹配特征点时,确定时候最好匹配与次好匹配差距的阈值。其值越小,其匹配越精确
* @param checkOri 是否开启匹配点的方向分类
*/
ORBmatcher(float nnratio=0.6, bool checkOri=true);
// Computes the Hamming distance between two ORB descriptors
static int DescriptorDistance(const cv::Mat &a, const cv::Mat &b);
// Search matches between Frame keypoints and projected MapPoints. Returns number of matches
// Used to track the local map (Tracking)
//将F里的特征值与vpMapPoints进行匹配,通过投影加速
//返回通过此函数匹配成功的数量
int SearchByProjection(Frame &F, const std::vector<MapPoint*> &vpMapPoints, const float th=3);
// Project MapPoints tracked in last frame into the current frame and search matches.
// Used to track from previous frame (Tracking)
/**根据上一帧LastFrame的特征点以及所对应的mappoint信息,寻找当前帧的哪些特征点与哪些mappoint的匹配联系
* 根据上一帧特征点对应的3D点投影的位置缩小特征点匹配范围
* @param CurrentFrame 当前帧
* @param LastFrame 上一帧
* @param th 控制特征搜索框的大小阈值
* @param bMono 是否为单目
* @return 成功匹配的数量
*/
int SearchByProjection(Frame &CurrentFrame, const Frame &LastFrame, const float th, const bool bMono);
// Project MapPoints seen in KeyFrame into the Frame and search matches.
// Used in relocalisation (Tracking)
/**在Tracking里的relocalisation中使用
* CurrentFrame中特征点已经匹配好一些mappoint在sAlreadyFound中,通过此函数将pKF悉数投影到CurrentFrame再就近搜索特征点进行匹配
* 也就是说CurrentFrame想通过这个函数在pKF的mappoint集合中匹配上更多的mappoint点
* @param CurrentFrame 当前帧
* @param pKF
* @param sAlreadyFound CurrentFrame已经匹配上的mappoint
* @param th 控制特征搜索框的大小阈值
* @param ORBdist pKF中的mappoint是否能和CurrentFrame匹配成功的描述子距离的阈值
* @return 成功匹配的数量
*/
int SearchByProjection(Frame &CurrentFrame, KeyFrame* pKF, const std::set<MapPoint*> &sAlreadyFound, const float th, const int ORBdist);
// Project MapPoints using a Similarity Transformation and search matches.
// Used in loop detection (Loop Closing)
int SearchByProjection(KeyFrame* pKF, cv::Mat Scw, const std::vector<MapPoint*> &vpPoints, std::vector<MapPoint*> &vpMatched, int th);
// Search matches between MapPoints in a KeyFrame and ORB in a Frame.
// Brute force constrained to ORB that belong to the same vocabulary node (at a certain level)
// Used in Relocalisation and Loop Detection
/**
* 针对pKF中有对应mappoint的那些特征点,和F中的特征点进行匹配
* 利用dbow进行匹配加速
* 通过距离阈值、比例阈值和角度投票进行剔除误匹配
* @param pKF KeyFrame
* @param F Current Frame
* @param vpMapPointMatches 大小为F特征点数量,输出F中MapPoints对应的匹配,NULL表示未匹配
* @return 成功匹配的数量
*/
int SearchByBoW(KeyFrame *pKF, Frame &F, std::vector<MapPoint*> &vpMapPointMatches);
// 匹配pKF1与pKF2之间的特征点并通过bow加速,vpMatches12是pKF1匹配特征点对应的MapPoints
int SearchByBoW(KeyFrame *pKF1, KeyFrame* pKF2, std::vector<MapPoint*> &vpMatches12);
// Matching for the Map Initialization (only used in the monocular case)
/**
* 搜索F1和F2之间的匹配点放在vnMatches12。如果mbCheckOrientation,则将F1中匹配成功的
* 特征点按照其角度分类在rotHist中。
* 计算出rotHist,vnMatches12
* @param F1 参考帧
* @param F2 当前帧
* @param vbPrevMatched F1中待匹配的特征点
* @param vnMatches12 输出F1中特征点匹配情况,大小是F1的特征点数量。其中-1表示未匹配,大于0表示匹配的特征点在F2中的序号
* @param windowSize 加速匹配时用到的方形边长
* @return 成功匹配的数量
*/
int SearchForInitialization(Frame &F1, Frame &F2, std::vector<cv::Point2f> &vbPrevMatched, std::vector<int> &vnMatches12, int windowSize=10);
// Matching to triangulate new MapPoints. Check Epipolar Constraint.
// 匹配pKF1与pKF2之间的未被匹配的特征点并通过bow加速,并校验是否符合对级约束。vMatchedPairs匹配成功的特征点在各自关键帧中的id。
int SearchForTriangulation(KeyFrame *pKF1, KeyFrame* pKF2, cv::Mat F12,
std::vector<pair<size_t, size_t> > &vMatchedPairs, const bool bOnlyStereo);
// Search matches between MapPoints seen in KF1 and KF2 transforming by a Sim3 [s12*R12|t12]
// In the stereo and RGB-D case, s12=1
int SearchBySim3(KeyFrame* pKF1, KeyFrame* pKF2, std::vector<MapPoint *> &vpMatches12, const float &s12, const cv::Mat &R12, const cv::Mat &t12, const float th);
// Project MapPoints into KeyFrame and search for duplicated MapPoints.
/**
* 将vpMapPoints中的mappoint与pKF的特征点进行匹配,若匹配的特征点已有mappoint与其匹配,
* 则选择其一与此特征点匹配,并抹去没有选择的那个mappoint,此为mappoint的融合
* @param radius 为在vpMapPoints投影到pKF搜索待匹配的特征点时的方框边长
* @return 融合的mappoint的数量
*/
int Fuse(KeyFrame* pKF, const vector<MapPoint *> &vpMapPoints, const float th=3.0);
// Project MapPoints into KeyFrame using a given Sim3 and search for duplicated MapPoints.
//vpPoints通过Scw投影到pKF,与pKF中的特征点匹配。如果匹配的pKF中的特征点本身有就的匹配mappoint,就用vpPoints替代它。
//vpReplacePoint大小与vpPoints一致,储存着被替换下来的mappoint
int Fuse(KeyFrame* pKF, cv::Mat Scw, const std::vector<MapPoint*> &vpPoints, float th, vector<MapPoint *> &vpReplacePoint);
public:
//匹配特征点时,描述子距离的阈值。特征点间描述子小于此值才考虑匹配
static const int TH_LOW;
static const int TH_HIGH;
//按照匹配特征点之间的角度分类匹配特征点的数量
static const int HISTO_LENGTH;
protected:
//判断kp1,与kp2在基础矩阵F12下是否复合对极约束
bool CheckDistEpipolarLine(const cv::KeyPoint &kp1, const cv::KeyPoint &kp2, const cv::Mat &F12, const KeyFrame *pKF);
//根据观测角的cos值确定搜索区域的半径
float RadiusByViewingCos(const float &viewCos);
//找出数组histo中,vector.size()数量最大的前三位。也就是角度范围最多的前三位。
void ComputeThreeMaxima(std::vector<int>* histo, const int L, int &ind1, int &ind2, int &ind3);
//匹配特征点时,确定时候最好匹配与次好匹配差距的阈值。其值越小,其匹配越精确
float mfNNratio;
//是否开启匹配点角度差与其他大多数匹配点角度差差异较大的匹配点
bool mbCheckOrientation;
};
int ORBmatcher::SearchByProjection(Frame &F, const vector<MapPoint*> &vpMapPoints, const float th)
{
int nmatches=0;
const bool bFactor = th!=1.0;
//遍历vpMapPoints
for(size_t iMP=0; iMP<vpMapPoints.size(); iMP++)
{
MapPoint* pMP = vpMapPoints[iMP];
if(!pMP->mbTrackInView)
continue;
if(pMP->isBad())
continue;
const int &nPredictedLevel = pMP->mnTrackScaleLevel;
// The size of the window will depend on the viewing direction
//根据观测角的cos值确定搜索区域的半径
float r = RadiusByViewingCos(pMP->mTrackViewCos);
if(bFactor)
r*=th;
//pMP通过区域搜索得到F中特征点集合
const vector<size_t> vIndices =
F.GetFeaturesInArea(pMP->mTrackProjX,pMP->mTrackProjY,r*F.mvScaleFactors[nPredictedLevel],nPredictedLevel-1,nPredictedLevel);
if(vIndices.empty())
continue;
const cv::Mat MPdescriptor = pMP->GetDescriptor();
int bestDist=256;
int bestLevel= -1;
int bestDist2=256;
int bestLevel2 = -1;
int bestIdx =-1 ;
// Get best and second matches with near keypoints
//遍历通过区域搜索得到的特征点集合
for(vector<size_t>::const_iterator vit=vIndices.begin(), vend=vIndices.end(); vit!=vend; vit++)
{
const size_t idx = *vit;
//如果这个特征点有对应的mappoint,且其对应的mappoint有被keyframe看到
if(F.mvpMapPoints[idx])
if(F.mvpMapPoints[idx]->Observations()>0)
continue;
if(F.mvuRight[idx]>0)
{
const float er = fabs(pMP->mTrackProjXR-F.mvuRight[idx]);
if(er>r*F.mvScaleFactors[nPredictedLevel])
continue;
}
const cv::Mat &d = F.mDescriptors.row(idx);
const int dist = DescriptorDistance(MPdescriptor,d);
if(dist<bestDist)
{
bestDist2=bestDist;
bestDist=dist;
bestLevel2 = bestLevel;
bestLevel = F.mvKeysUn[idx].octave;
bestIdx=idx;
}
else if(dist<bestDist2)
{
bestLevel2 = F.mvKeysUn[idx].octave;
bestDist2=dist;
}
}
// Apply ratio to second match (only if best and second are in the same scale level)
if(bestDist<=TH_HIGH)
{
if(bestLevel==bestLevel2 && bestDist>mfNNratio*bestDist2)
continue;
F.mvpMapPoints[bestIdx]=pMP;
nmatches++;
}
}
return nmatches;
}
/**根据上一帧LastFrame的特征点以及所对应的mappoint信息,寻找当前帧的哪些特征点与哪些mappoint的匹配联系
* 根据上一帧特征点对应的3D点投影的位置缩小特征点匹配范围
* @param CurrentFrame 当前帧
* @param LastFrame 上一帧
* @param th 控制特征搜索框的大小阈值
* @param bMono 是否为单目
* @return 成功匹配的数量
*/
int ORBmatcher::SearchByProjection(Frame &CurrentFrame, const Frame &LastFrame, const float th, const bool bMono)
{
int nmatches = 0;
//这里ORBmatcher::SearchForInitialization()里一致,具体看那里的注释,这里就不再重复了
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 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 twc = -Rcw.t()*tcw;
const cv::Mat Rlw = LastFrame.mTcw.rowRange(0,3).colRange(0,3);
const cv::Mat tlw = LastFrame.mTcw.rowRange(0,3).col(3);
const cv::Mat tlc = Rlw*twc+tlw;
const bool bForward = tlc.at<float>(2)>CurrentFrame.mb && !bMono;
const bool bBackward = -tlc.at<float>(2)>CurrentFrame.mb && !bMono;
//遍历上一帧LastFrame可以看到的所有特征点
for(int i=0; i<LastFrame.N; i++)
{
MapPoint* pMP = LastFrame.mvpMapPoints[i];
//如果在上一帧的这个特征点对应有mappoint
if(pMP)
{
if(!LastFrame.mvbOutlier[i])
{
// Project
//针对上一帧的mappoint,将它投影当前帧,然后在投影后的位置进行特征点的搜索匹配
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);
if(invzc<0)
continue;
float u = CurrentFrame.fx*xc*invzc+CurrentFrame.cx;
float v = CurrentFrame.fy*yc*invzc+CurrentFrame.cy;
if(u<CurrentFrame.mnMinX || u>CurrentFrame.mnMaxX)
continue;
if(v<CurrentFrame.mnMinY || v>CurrentFrame.mnMaxY)
continue;
int nLastOctave = LastFrame.mvKeys[i].octave;
// Search in a window. Size depends on scale
// NOTE 尺度越大,图像越小
// 以下可以这么理解,例如一个有一定面积的圆点,在某个尺度n下它是一个特征点
// 当前进时,圆点的面积增大,在某个尺度m下它是一个特征点,由于面积增大,则需要在更高的尺度下才能检测出来
// 因此m>=n,对应前进的情况,nCurOctave>=nLastOctave。后退的情况可以类推
float radius = th*CurrentFrame.mvScaleFactors[nLastOctave];
//可能与pMP匹配的当前帧特征点
vector<size_t> vIndices2;
// 前进,则上一帧兴趣点在所在的尺度nLastOctave<=nCurOctave
if(bForward)
vIndices2 = CurrentFrame.GetFeaturesInArea(u,v, radius, nLastOctave);
// 后退,则上一帧兴趣点在所在的尺度0<=nCurOctave<=nLastOctave
else if(bBackward)
vIndices2 = CurrentFrame.GetFeaturesInArea(u,v, radius, 0, nLastOctave);
// 在[nLastOctave-1, nLastOctave+1]中搜索
else
vIndices2 = CurrentFrame.GetFeaturesInArea(u,v, radius, nLastOctave-1, nLastOctave+1);
if(vIndices2.empty())
continue;
const cv::Mat dMP = pMP->GetDescriptor();
int bestDist = 256;
int bestIdx2 = -1;
//遍历vIndices2,筛选出与pMP最匹配的特征点
for(vector<size_t>::const_iterator vit=vIndices2.begin(), vend=vIndices2.end(); vit!=vend; vit++)
{
const size_t i2 = *vit;
if(CurrentFrame.mvpMapPoints[i2])
if(CurrentFrame.mvpMapPoints[i2]->Observations()>0)
continue;
if(CurrentFrame.mvuRight[i2]>0)
{
const float ur = u - CurrentFrame.mbf*invzc;
const float er = fabs(ur - CurrentFrame.mvuRight[i2]);
if(er>radius)
continue;
}
const cv::Mat &d = CurrentFrame.mDescriptors.row(i2);
const int dist = DescriptorDistance(dMP,d);
if(dist<bestDist)
{
bestDist=dist;
bestIdx2=i2;
}
}
//bestDist<=TH_HIGH则匹配成功
if(bestDist<=TH_HIGH)
{
CurrentFrame.mvpMapPoints[bestIdx2]=pMP;
nmatches++;
if(mbCheckOrientation)
{
float rot = LastFrame.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);
}
}
}
}
}
//Apply rotation consistency
//剔除方向不符合的匹配点
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]]=static_cast<MapPoint*>(NULL);
nmatches--;
}
}
}
}
return nmatches;
}
/**
* 针对pKF中有对应mappoint的那些特征点,和F中的特征点进行匹配
* 利用dbow进行匹配加速
* 通过距离阈值、比例阈值和角度投票进行剔除误匹配
* @param pKF KeyFrame
* @param F Current Frame
* @param vpMapPointMatches 大小为F特征点数量,输出F中MapPoints对应的匹配,NULL表示未匹配
* @return 成功匹配的数量
*/
int ORBmatcher::SearchByBoW(KeyFrame* pKF,Frame &F, vector<MapPoint*> &vpMapPointMatches)
{
// pKF可以看到的mappoint
const vector<MapPoint*> vpMapPointsKF = pKF->GetMapPointMatches();
vpMapPointMatches = vector<MapPoint*>(F.N,static_cast<MapPoint*>(NULL));
const DBoW2::FeatureVector &vFeatVecKF = pKF->mFeatVec;
int nmatches=0;
vector<int> rotHist[HISTO_LENGTH];
for(int i=0;i<HISTO_LENGTH;i++)
rotHist[i].reserve(500);
const float factor = 1.0f/HISTO_LENGTH;
// We perform the matching over ORB that belong to the same vocabulary node (at a certain level)
DBoW2::FeatureVector::const_iterator KFit = vFeatVecKF.begin();
DBoW2::FeatureVector::const_iterator Fit = F.mFeatVec.begin();
DBoW2::FeatureVector::const_iterator KFend = vFeatVecKF.end();
DBoW2::FeatureVector::const_iterator Fend = F.mFeatVec.end();
//遍历vFeatVecKF的每个结点
while(KFit != KFend && Fit != Fend)
{
//步骤1:分别取出属于同一node的ORB特征点(只有属于同一node,才有可能是匹配点)
if(KFit->first == Fit->first)
{
const vector<unsigned int> vIndicesKF = KFit->second;
const vector<unsigned int> vIndicesF = Fit->second;
//遍历pKF中属于该node的特征点
for(size_t iKF=0; iKF<vIndicesKF.size(); iKF++)
{
const unsigned int realIdxKF = vIndicesKF[iKF];
//获得此特征点对应的mappoint
MapPoint* pMP = vpMapPointsKF[realIdxKF];
//如果此特征点没有对应mappoint
if(!pMP)
continue;
//如果此特征点对应的mappoint是坏的
if(pMP->isBad())
continue;
//获得此特征点对应的描述子
const cv::Mat &dKF= pKF->mDescriptors.row(realIdxKF);
int bestDist1=256;
int bestIdxF =-1 ;
int bestDist2=256;
//遍历F中属于该node的特征点,找到最佳匹配点
for(size_t iF=0; iF<vIndicesF.size(); iF++)
{
const unsigned int realIdxF = vIndicesF[iF];
// 表明这个点已经被匹配过了,不再匹配,加快速度
if(vpMapPointMatches[realIdxF])
continue;
const cv::Mat &dF = F.mDescriptors.row(realIdxF);
const int dist = DescriptorDistance(dKF,dF);
//更新bestDist1 bestDist2
//bestDist1表示最佳匹配,bestDist2表示次佳匹配
if(dist<bestDist1)
{
bestDist2=bestDist1;
bestDist1=dist;
bestIdxF=realIdxF;
}
else if(dist<bestDist2)
{
bestDist2=dist;
}
}
//如果最佳匹配达到阈值,也就是距离阈值剔除
if(bestDist1<=TH_LOW)
{
//如果最佳匹配和次佳匹配差距较大,那么就剔除这个匹配。也就是比例剔除
if(static_cast<float>(bestDist1)<mfNNratio*static_cast<float>(bestDist2))
{
vpMapPointMatches[bestIdxF]=pMP;
const cv::KeyPoint &kp = pKF->mvKeysUn[realIdxKF];
//如果开启了角度剔除
if(mbCheckOrientation)
{
float rot = kp.angle-F.mvKeys[bestIdxF].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(bestIdxF);
}
nmatches++;
}
}
}
KFit++;
Fit++;
}
else if(KFit->first < Fit->first)
{
KFit = vFeatVecKF.lower_bound(Fit->first);
}
else
{
Fit = F.mFeatVec.lower_bound(KFit->first);
}
}
if(mbCheckOrientation)
{
//ind1,ind2,ind3表示主要的旋转方向
int ind1=-1;
int ind2=-1;
int ind3=-1;
//计算ind1,ind2,ind3
ComputeThreeMaxima(rotHist,HISTO_LENGTH,ind1,ind2,ind3);
for(int i=0; i<HISTO_LENGTH; i++)
{
if(i==ind1 || i==ind2 || i==ind3)
continue;
//对于不在ind1,ind2,ind3方向上的匹配,进行剔除
for(size_t j=0, jend=rotHist[i].size(); j<jend; j++)
{
vpMapPointMatches[rotHist[i][j]]=static_cast<MapPoint*>(NULL);
nmatches--;
}
}
}
return nmatches;
}
* 搜索F1和F2之间的匹配点放在vnMatches12。如果mbCheckOrientation,则将F1中匹配成功的
* 特征点按照其角度分类在rotHist中。
* 计算出rotHist,vnMatches12
* @param F1 参考帧
* @param F2 当前帧
* @param vbPrevMatched F1中待匹配的特征点
* @param vnMatches12 输出F1中特征点匹配情况,大小是F1的特征点数量。其中-1表示未匹配,大于0表示匹配的特征点在F2中的序号
* @param windowSize 加速匹配时用到的方形边长
* @return 成功匹配的数量
*/
int ORBmatcher::SearchForInitialization(Frame &F1, Frame &F2, vector<cv::Point2f> &vbPrevMatched, vector<int> &vnMatches12, int windowSize)
{
int nmatches=0;
//储存F1中匹配成功的点在F2中的序号
vnMatches12 = vector<int>(F1.mvKeysUn.size(),-1);
//如果mbCheckOrientation为真,之后匹配点匹配按照匹配点的角度差划分到rotHist。分类数量为HISTO_LENGTH
//是类型为vector,大小为HISTO_LENGTH的数组
//假设HISTO_LENGTH=8,那么rotHist存放着匹配点角度差为0~45,45~90...315~360度的F1特征点序号
vector<int> rotHist[HISTO_LENGTH];
for(int i=0;i<HISTO_LENGTH;i++)
rotHist[i].reserve(500);
const float factor = 1.0f/HISTO_LENGTH;
//储存匹配成功的特征点的描述子之间的距离
vector<int> vMatchedDistance(F2.mvKeysUn.size(),INT_MAX);
//储存F2中匹配成功的点在F1中的序号
vector<int> vnMatches21(F2.mvKeysUn.size(),-1);
//遍历F1中畸变纠正后的特征点
for(size_t i1=0, iend1=F1.mvKeysUn.size(); i1<iend1; i1++)
{
cv::KeyPoint kp1 = F1.mvKeysUn[i1];
int level1 = kp1.octave;
//只处理第0层的特征点
if(level1>0)
continue;
//搜索F2中,以vbPrevMatched[i1]为中心,边长为2*windowSize的方形内,尺度为level1的特征点。注意返回的特征点集合是F2中特征点序号集合
//这些F2中的特征点是最有可能和F1中il匹配上的
vector<size_t> vIndices2 = F2.GetFeaturesInArea(vbPrevMatched[i1].x,vbPrevMatched[i1].y, windowSize,level1,level1);
if(vIndices2.empty())
continue;
//获得F1中il的描述子
cv::Mat d1 = F1.mDescriptors.row(i1);
//初始化描述子间最小距离
int bestDist = INT_MAX;
//初始化描述子间次小距离
int bestDist2 = INT_MAX;
//描述子间最小距离对应在F2中特征点序号
int bestIdx2 = -1;
//遍历vIndices2
//在vIndices2中找出和i1距离最小的点,也就是最匹配的点,并更新bestDist,bestDist2
for(vector<size_t>::iterator vit=vIndices2.begin(); vit!=vIndices2.end(); vit++)
{
size_t i2 = *vit;
cv::Mat d2 = F2.mDescriptors.row(i2);
int dist = DescriptorDistance(d1,d2);
if(vMatchedDistance[i2]<=dist)
continue;
//更新bestDist,bestDist2
if(dist<bestDist)
{
bestDist2=bestDist;
bestDist=dist;
bestIdx2=i2;
}
else if(dist<bestDist2)
{
bestDist2=dist;
}
}
//描述*子距离小于等于TH_LOW才考虑匹配
if(bestDist<=TH_LOW)
{
//如果最小距离bestDist,与次小距离相距足够远(用mfNNratio描述其阈值),则匹配成功,否则放弃
if(bestDist<(float)bestDist2*mfNNratio)
{
//其他的F1中的特征点已经和bestIdx2匹配上了,形成冲突,于是删除之前的匹配,然后代替它
if(vnMatches21[bestIdx2]>=0)
{
vnMatches12[vnMatches21[bestIdx2]]=-1;
nmatches--;
}
vnMatches12[i1]=bestIdx2;
vnMatches21[bestIdx2]=i1;
vMatchedDistance[bestIdx2]=bestDist;
nmatches++;
//将i1按照匹配点角度差范围划分到rotHist中
if(mbCheckOrientation)
{
//计算匹配点的角度差
float rot = F1.mvKeysUn[i1].angle-F2.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(i1);
}
}
}
}
//其实这里的意思是,F1与F2之间匹配点的角度差应该差不多是一致的
//于是我们就将匹配点,按照匹配点间的角度差来分成HISTO_LENGTH类,放在rotHist
//然后剔除,那些角度差和其他大多数匹配点角度差差异较大的点
//具体就是,先找出前3多的角度差范围,然后剔除那些不在这些角度差范围的匹配点
if(mbCheckOrientation)
{
int ind1=-1;
int ind2=-1;
int ind3=-1;
//找出数组rotHist中,数量最多的前三位
ComputeThreeMaxima(rotHist,HISTO_LENGTH,ind1,ind2,ind3);
for(int i=0; i<HISTO_LENGTH; i++)
{
if(i==ind1 || i==ind2 || i==ind3)
continue;
for(size_t j=0, jend=rotHist[i].size(); j<jend; j++)
{
int idx1 = rotHist[i][j];
if(vnMatches12[idx1]>=0)
{
vnMatches12[idx1]=-1;
nmatches--;
}
}
}
}
//Update prev matched
for(size_t i1=0, iend1=vnMatches12.size(); i1<iend1; i1++)
if(vnMatches12[i1]>=0)
vbPrevMatched[i1]=F2.mvKeysUn[vnMatches12[i1]].pt;
return nmatches;
}
// 匹配pKF1与pKF2之间的未被匹配的特征点并通过bow加速,并校验是否符合对级约束。vMatchedPairs匹配成功的特征点在各自关键帧中的id。
int ORBmatcher::SearchForTriangulation(KeyFrame *pKF1, KeyFrame *pKF2, cv::Mat F12,
vector<pair<size_t, size_t> > &vMatchedPairs, const bool bOnlyStereo)
{
const DBoW2::FeatureVector &vFeatVec1 = pKF1->mFeatVec;
const DBoW2::FeatureVector &vFeatVec2 = pKF2->mFeatVec;
//Compute epipole in second image
//pKF1光心在世界坐标系中的位姿
cv::Mat Cw = pKF1->GetCameraCenter();
cv::Mat R2w = pKF2->GetRotation();
cv::Mat t2w = pKF2->GetTranslation();
//pKF1光心在相机2坐标系中的位姿
cv::Mat C2 = R2w*Cw+t2w;
const float invz = 1.0f/C2.at<float>(2);
const float ex =pKF2->fx*C2.at<float>(0)*invz+pKF2->cx;
const float ey =pKF2->fy*C2.at<float>(1)*invz+pKF2->cy;
// Find matches between not tracked keypoints
// Matching speed-up by ORB Vocabulary
// Compare only ORB that share the same node
int nmatches=0;
vector<bool> vbMatched2(pKF2->N,false);
vector<int> vMatches12(pKF1->N,-1);
vector<int> rotHist[HISTO_LENGTH];
for(int i=0;i<HISTO_LENGTH;i++)
rotHist[i].reserve(500);
const float factor = 1.0f/HISTO_LENGTH;
DBoW2::FeatureVector::const_iterator f1it = vFeatVec1.begin();
DBoW2::FeatureVector::const_iterator f2it = vFeatVec2.begin();
DBoW2::FeatureVector::const_iterator f1end = vFeatVec1.end();
DBoW2::FeatureVector::const_iterator f2end = vFeatVec2.end();
//遍历vFeatVec1,vFeatVec2中的节点
while(f1it!=f1end && f2it!=f2end)
{
if(f1it->first == f2it->first)
{
for(size_t i1=0, iend1=f1it->second.size(); i1<iend1; i1++)
{
const size_t idx1 = f1it->second[i1];
MapPoint* pMP1 = pKF1->GetMapPoint(idx1);
// If there is already a MapPoint skip
if(pMP1)
continue;
const bool bStereo1 = pKF1->mvuRight[idx1]>=0;
if(bOnlyStereo)
if(!bStereo1)
continue;
const cv::KeyPoint &kp1 = pKF1->mvKeysUn[idx1];
const cv::Mat &d1 = pKF1->mDescriptors.row(idx1);
int bestDist = TH_LOW;
int bestIdx2 = -1;
//在pk2中相同的节点中寻找匹配的特征点
for(size_t i2=0, iend2=f2it->second.size(); i2<iend2; i2++)
{
size_t idx2 = f2it->second[i2];
MapPoint* pMP2 = pKF2->GetMapPoint(idx2);
// If we have already matched or there is a MapPoint skip
//已经匹配上了,或者已经有了mappoint
if(vbMatched2[idx2] || pMP2)
continue;
const bool bStereo2 = pKF2->mvuRight[idx2]>=0;
if(bOnlyStereo)
if(!bStereo2)
continue;
const cv::Mat &d2 = pKF2->mDescriptors.row(idx2);
const int dist = DescriptorDistance(d1,d2);
if(dist>TH_LOW || dist>bestDist)
continue;
const cv::KeyPoint &kp2 = pKF2->mvKeysUn[idx2];
if(!bStereo1 && !bStereo2)
{
const float distex = ex-kp2.pt.x;
const float distey = ey-kp2.pt.y;
if(distex*distex+distey*distey<100*pKF2->mvScaleFactors[kp2.octave])
continue;
}
//如果kp1,与kp2在基础矩阵F12下是否复合对极约束
if(CheckDistEpipolarLine(kp1,kp2,F12,pKF2))
{
//更新最好点和次好点的id
bestIdx2 = idx2;
bestDist = dist;
}
}
if(bestIdx2>=0)
{
const cv::KeyPoint &kp2 = pKF2->mvKeysUn[bestIdx2];
vMatches12[idx1]=bestIdx2;
nmatches++;
if(mbCheckOrientation)
{
float rot = kp1.angle-kp2.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(idx1);
}
}
}
f1it++;
f2it++;
}
else if(f1it->first < f2it->first)
{
f1it = vFeatVec1.lower_bound(f2it->first);
}
else
{
f2it = vFeatVec2.lower_bound(f1it->first);
}
}
//剔除方向不符合的匹配点
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)
continue;
for(size_t j=0, jend=rotHist[i].size(); j<jend; j++)
{
vMatches12[rotHist[i][j]]=-1;
nmatches--;
}
}
}
vMatchedPairs.clear();
vMatchedPairs.reserve(nmatches);
for(size_t i=0, iend=vMatches12.size(); i<iend; i++)
{
if(vMatches12[i]<0)
continue;
vMatchedPairs.push_back(make_pair(i,vMatches12[i]));
}
return nmatches;
}
/**
* 将vpMapPoints中的mappoint与pKF的特征点进行匹配,若匹配的特征点已有mappoint与其匹配,
* 则选择其一与此特征点匹配,并抹去没有选择的那个mappoint,此为mappoint的融合
* @param radius 为在vpMapPoints投影到pKF搜索待匹配的特征点时的方框边长
* @return 融合的mappoint的数量
*/
int ORBmatcher::Fuse(KeyFrame *pKF, const vector<MapPoint *> &vpMapPoints, const float th)
{
cv::Mat Rcw = pKF->GetRotation();
cv::Mat tcw = pKF->GetTranslation();
const float &fx = pKF->fx;
const float &fy = pKF->fy;
const float &cx = pKF->cx;
const float &cy = pKF->cy;
const float &bf = pKF->mbf;
cv::Mat Ow = pKF->GetCameraCenter();
int nFused=0;
const int nMPs = vpMapPoints.size();
//遍历vpMapPoints中的mappoint
for(int i=0; i<nMPs; i++)
{
MapPoint* pMP = vpMapPoints[i];
if(!pMP)
continue;
//pMP->IsInKeyFrame(pKF)表示pMP已经被pKF观测了,有了对应了特征点,也就不用融合了
if(pMP->isBad() || pMP->IsInKeyFrame(pKF))
continue;
cv::Mat p3Dw = pMP->GetWorldPos();
cv::Mat p3Dc = Rcw*p3Dw + tcw;
// Depth must be positive
//如果深度为负
if(p3Dc.at<float>(2)<0.0f)
continue;
//尺度归一化
const float invz = 1/p3Dc.at<float>(2);
const float x = p3Dc.at<float>(0)*invz;
const float y = p3Dc.at<float>(1)*invz;
const float u = fx*x+cx;
const float v = fy*y+cy;
// Point must be inside the image
//uv是pMP投影到pKF的像素坐标
//如果pMP不在pKF图像中
if(!pKF->IsInImage(u,v))
continue;
const float ur = u-bf*invz;
const float maxDistance = pMP->GetMaxDistanceInvariance();
const float minDistance = pMP->GetMinDistanceInvariance();
cv::Mat PO = p3Dw-Ow;
const float dist3D = cv::norm(PO);
// Depth must be inside the scale pyramid of the image
//深度必须在尺度金字塔范围内
if(dist3D<minDistance || dist3D>maxDistance )
continue;
// Viewing angle must be less than 60 deg
cv::Mat Pn = pMP->GetNormal();
//如果PO和Pn的夹角大于60度
if(PO.dot(Pn)<0.5*dist3D)
continue;
int nPredictedLevel = pMP->PredictScale(dist3D,pKF);
// Search in a radius
//求出radius
const float radius = th*pKF->mvScaleFactors[nPredictedLevel];
//获得pKF在uv附近的特征点
const vector<size_t> vIndices = pKF->GetFeaturesInArea(u,v,radius);
if(vIndices.empty())
continue;
// Match to the most similar keypoint in the radius
const cv::Mat dMP = pMP->GetDescriptor();
int bestDist = 256;
int bestIdx = -1;
//遍历vIndices,找出在vIndices中与pMP的最佳匹配
for(vector<size_t>::const_iterator vit=vIndices.begin(), vend=vIndices.end(); vit!=vend; vit++)
{
const size_t idx = *vit;
const cv::KeyPoint &kp = pKF->mvKeysUn[idx];
const int &kpLevel= kp.octave;
if(kpLevel<nPredictedLevel-1 || kpLevel>nPredictedLevel)
continue;
if(pKF->mvuRight[idx]>=0)
{
// Check reprojection error in stereo
const float &kpx = kp.pt.x;
const float &kpy = kp.pt.y;
const float &kpr = pKF->mvuRight[idx];
const float ex = u-kpx;
const float ey = v-kpy;
const float er = ur-kpr;
const float e2 = ex*ex+ey*ey+er*er;
if(e2*pKF->mvInvLevelSigma2[kpLevel]>7.8)
continue;
}
else
{
const float &kpx = kp.pt.x;
const float &kpy = kp.pt.y;
const float ex = u-kpx;
const float ey = v-kpy;
const float e2 = ex*ex+ey*ey;
if(e2*pKF->mvInvLevelSigma2[kpLevel]>5.99)
continue;
}
const cv::Mat &dKF = pKF->mDescriptors.row(idx);
const int dist = DescriptorDistance(dMP,dKF);
if(dist<bestDist)
{
bestDist = dist;
bestIdx = idx;
}
}
// If there is already a MapPoint replace otherwise add new measurement
if(bestDist<=TH_LOW)
{
MapPoint* pMPinKF = pKF->GetMapPoint(bestIdx);
if(pMPinKF)
{
if(!pMPinKF->isBad())
{
if(pMPinKF->Observations()>pMP->Observations())
pMP->Replace(pMPinKF);
else
pMPinKF->Replace(pMP);
}
}
else
{
pMP->AddObservation(pKF,bestIdx);
pKF->AddMapPoint(pMP,bestIdx);
}
nFused++;
}
}
return nFused;
}