ORB-SLAM2学习(原理):MapPoint.cc

ORB-SLAM2学习(原理):MapPoint.cc

详细中文源码解读:链接:https://pan.baidu.com/s/1LWfowy5wbUdXamEGE1STcA 提取码:t796
PS:该代码从“计算机视觉life”客服处免费获得,感觉确实挺详细的,就标明一下出处,侵权则删。


MapPoint::UpdateNormalAndDepth()函数

/**
 * @brief 更新地图点的平均观测方向、观测距离范围
 *
 */
void MapPoint::UpdateNormalAndDepth()
{
    // Step 1 获得观测到该地图点的所有关键帧、坐标等信息
    map<KeyFrame*,size_t> observations;
    KeyFrame* pRefKF;
    cv::Mat Pos;
    {//锁的作用域
        unique_lock<mutex> lock1(mMutexFeatures);
        unique_lock<mutex> lock2(mMutexPos);
        if(mbBad)
            return;

        observations=mObservations; // 获得观测到该地图点的所有关键帧
        pRefKF=mpRefKF;             // 观测到该点的参考关键帧(第一次创建时的关键帧)
        Pos = mWorldPos.clone();    // 地图点在世界坐标系中的位置
    }

    if(observations.empty())
        return;

    // Step 2 计算该地图点的平均观测方向
    // 能观测到该地图点的所有关键帧,对该点的观测方向归一化为单位向量,然后进行求和得到该地图点的朝向
    // 初始值为0向量,累加为归一化向量,最后除以总数n
    cv::Mat normal = cv::Mat::zeros(3,1,CV_32F);
    int n=0;
    for(map<KeyFrame*,size_t>::iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++)
    {
        KeyFrame* pKF = mit->first;
        cv::Mat Owi = pKF->GetCameraCenter();//相机光心
        // 获得地图点和观测到它关键帧的向量并归一化
        cv::Mat normali = mWorldPos - Owi;
        normal = normal + normali/cv::norm(normali);                       
        n++;
    } 

    cv::Mat PC = Pos - pRefKF->GetCameraCenter();                           // 参考关键帧相机指向地图点的向量(在世界坐标系下的表示)
    const float dist = cv::norm(PC);                                        // 该点到参考关键帧相机的距离
    const int level = pRefKF->mvKeysUn[observations[pRefKF]].octave;        // 观测到该地图点的当前帧的特征点在金字塔的第几层
    const float levelScaleFactor =  pRefKF->mvScaleFactors[level];          // 当前金字塔层对应的尺度因子,scale^n,scale=1.2,n为层数
    const int nLevels = pRefKF->mnScaleLevels;                              // 金字塔总层数,默认为8

    {
        unique_lock<mutex> lock3(mMutexPos);
        // 使用方法见PredictScale函数前的注释
        mfMaxDistance = dist*levelScaleFactor;                              // 观测到该点的距离上限
        mfMinDistance = mfMaxDistance/pRefKF->mvScaleFactors[nLevels-1];    // 观测到该点的距离下限
        mNormalVector = normal/n;                                           // 获得地图点平均的观测方向
    }
}

注意一下cv::Mat normali = mWorldPos - Owi;和 cv::Mat PC = Pos - pRefKF->GetCameraCenter(); 的不同。
线程锁的理解
{//锁的作用域
unique_lock lock1(mMutexFeatures);
unique_lock lock2(mMutexPos);
if(mbBad)
return;

    observations=mObservations; // 获得观测到该地图点的所有关键帧
    pRefKF=mpRefKF;             // 观测到该点的参考关键帧(第一次创建时的关键帧)
    Pos = mWorldPos.clone();    // 地图点在世界坐标系中的位置
}

MapPoint::ComputeDistinctiveDescriptors()函数

/**
 * @brief 计算地图点最具代表性的描述子
 *
 * 由于一个地图点会被许多相机观测到,因此在插入关键帧后,需要判断是否更新代表当前点的描述子 
 * 先获得当前点的所有描述子,然后计算描述子之间的两两距离,最好的描述子与其他描述子应该具有最小的距离中值
 */
void MapPoint::ComputeDistinctiveDescriptors()
{
    // Retrieve all observed descriptors
    vector<cv::Mat> vDescriptors;//所有描述子

    map<KeyFrame*,size_t> observations;//所有观测 

    // Step 1 获取该地图点所有有效的观测关键帧信息
    {
        unique_lock<mutex> lock1(mMutexFeatures);
        if(mbBad)
            return;
        observations=mObservations;
    }

    if(observations.empty())
        return;

    vDescriptors.reserve(observations.size());

    // Step 2 遍历观测到该地图点的所有关键帧,对应的orb描述子,放到向量vDescriptors中
    for(map<KeyFrame*,size_t>::iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++)
    {
        // mit->first取观测到该地图点的关键帧
        // mit->second取该地图点在关键帧中的索引
        KeyFrame* pKF = mit->first;

        if(!pKF->isBad())        
            // 取对应的描述子向量                                               
            vDescriptors.push_back(pKF->mDescriptors.row(mit->second));     
    }

    if(vDescriptors.empty())
        return;

    // Compute distances between them
    // Step 3 计算这些描述子两两之间的距离
    // N表示为一共多少个描述子
    const size_t N = vDescriptors.size();
	
    // 将Distances表述成一个对称的矩阵
    // float Distances[N][N];
	std::vector<std::vector<float> > Distances;
	Distances.resize(N, vector<float>(N, 0));
	for (size_t i = 0; i<N; i++)
    {
        // 和自己的距离当然是0
        Distances[i][i]=0;
        // 计算并记录不同描述子距离
        for(size_t j=i+1;j<N;j++)
        {
            int distij = ORBmatcher::DescriptorDistance(vDescriptors[i],vDescriptors[j]);
            Distances[i][j]=distij;
            Distances[j][i]=distij;
        }
    }

    // Take the descriptor with least median distance to the rest
    // Step 4 选择最有代表性的描述子,它与其他描述子应该具有最小的距离中值
    int BestMedian = INT_MAX;   // 记录最小的中值
    int BestIdx = 0;            // 最小中值对应的索引
    for(size_t i=0;i<N;i++)
    {
        // 第i个描述子到其它所有描述子之间的距离
        // vector<int> vDists(Distances[i],Distances[i]+N);
		vector<int> vDists(Distances[i].begin(), Distances[i].end());
		sort(vDists.begin(), vDists.end());

        // 获得中值
        int median = vDists[0.5*(N-1)];
        
        // 寻找最小的中值
        if(median<BestMedian)
        {
            BestMedian = median;
            BestIdx = i;
        }
    }

    {
        unique_lock<mutex> lock(mMutexFeatures);
        mDescriptor = vDescriptors[BestIdx].clone();       
    }
}

{
unique_lock lock(mMutexFeatures);
mDescriptor = vDescriptors[BestIdx].clone();
}
这个线程锁如何理解

MapPoint::GetMaxDistanceInvariance()函数

float MapPoint::GetMinDistanceInvariance()
{
    unique_lock<mutex> lock(mMutexPos);
    return 0.8f*mfMinDistance;
}

float MapPoint::GetMaxDistanceInvariance()
{
    unique_lock<mutex> lock(mMutexPos);
    return 1.2f*mfMaxDistance;
}

// 下图中横线的大小表示不同图层图像上的一个像素表示的真实物理空间中的大小
//              ____
// Nearer      /____\     level:n-1 --> dmin
//            /______\                       d/dmin = 1.2^(n-1-m)
//           /________\   level:m   --> d
//          /__________\                     dmax/d = 1.2^m
// Farther /____________\ level:0   --> dmax
//
//           log(dmax/d)
// m = ceil(------------)
//            log(1.2)
// 这个函数的作用:
// 在进行投影匹配的时候会给定特征点的搜索范围,考虑到处于不同尺度(也就是距离相机远近,位于图像金字塔中不同图层)的特征点受到相机旋转的影响不同,
// 因此会希望距离相机近的点的搜索范围更大一点,距离相机更远的点的搜索范围更小一点,所以要在这里,根据点到关键帧/帧的距离来估计它在当前的关键帧/帧中,
// 会大概处于哪个尺度
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ORB-SLAM2是一种基于二维图像的实时单目视觉SLAM系统,可以在没有先验地图的情况下,从单个摄像头的输入中实时定位和建立环境模型。为了更好地理解ORB-SLAM2的原理和代码实现,我们需要逐行分析其核心算法。 ORB-SLAM2的主要原理是通过特征提取,特征匹配和位姿估计来实现定位和建图。在代码中,我们可以看到一些关键的数据结构和函数调用,这些都是实现这些原理的关键。 首先,ORB-SLAM2使用FAST特征检测器在图像中检测关键点。这些关键点代表图像中的有趣区域。然后,使用ORB描述符对关键点进行描述。ORB描述符使用二进制位串来表示关键点周围的特征。 然后,ORB-SLAM2使用词袋法(Bag-of-Words)模型来进行特征匹配。它首先通过建立一个词典来表示所有关键点的描述符。然后,使用词袋模型来计算图像之间的相似度,从而找到匹配的关键点。 接下来,ORB-SLAM2使用RANSAC算法来估计两个图像之间的相对位姿。RANSAC算法通过迭代随机采样的方式来筛选出最佳的匹配关系,从而得到相对位姿估计。 最后,ORB-SLAM2使用优化算法(如g2o)来进行位姿图优化,从而更精确地估计相机的位姿。通过优化,ORB-SLAM2能够减少位置漂移,并在动态环境下更好地跟踪相机的位置。 总的来说,ORB-SLAM2通过特征提取、特征匹配和位姿估计实现实时单目视觉SLAM。核心代码实现了特征检测、描述符提取、特征匹配、RANSAC算法和图优化等关键步骤。了解这些原理和代码实现,可以帮助我们更好地理解ORB-SLAM2系统背后的工作原理

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值