ORB-SLAM2源码分析和学习笔记03:ORB特征提取(2)

本文写于看完《视觉slam十四讲》,orb-slam2代码和论文后,以此来复盘过去几个月所学内容,有理解错误的地方欢迎大家指正批评!有问题也可以在评论区交流。

        关键点和描述子的计算见ORB-SLAM2源码分析和学习笔记03:ORB特征提取(1)

特征点均匀化

        通过计算关键点和描述子可以得到一张图像中的所有特征点,但这些特征点可能在图片纹理丰富的区域过于集中,而在纹理稀疏的地方很少,这就会造成特征点分布不均匀,从而影响后续的特征点跟踪,故会对这些特征点采用四叉树筛选使其分布均匀。

四叉树筛选

        四叉树原理可以参考四叉树

        具体步骤如下:

        代码实现比较复杂,个人认为理解上图即可

/**
 * @brief 八叉树筛选特征点
 * @param vToDistributeKeys 等待进行筛选到特征点
 * @param minX        当前图层的图像的边界,坐标都是在“半径扩充图像”坐标系下的坐标  
 * @param maxX 
 * @param minY 
 * @param maxY 
 * @param N         希望提取出的特征点个数
 * @param level         指定的金字塔图层,并未使用
 * @return          筛选过的特征点容器vector<cv::KeyPoint> vResultKeys;
 */
vector<cv::KeyPoint> ORBextractor::DistributeOctTree(const vector<cv::KeyPoint>& vToDistributeKeys, const int &minX,
                                       const int &maxX, const int &minY, const int &maxY, const int &N, const int &level)
{
    // Compute how many initial nodes   
    const int nIni = round(static_cast<float>(maxX-minX)/(maxY-minY));

    const float hX = static_cast<float>(maxX-minX)/nIni;

    list<ExtractorNode> lNodes;

    vector<ExtractorNode*> vpIniNodes;
    vpIniNodes.resize(nIni);

    for(int i=0; i<nIni; i++)
    {
        ExtractorNode ni;
        ni.UL = cv::Point2i(hX*static_cast<float>(i),0);
        ni.UR = cv::Point2i(hX*static_cast<float>(i+1),0);
        ni.BL = cv::Point2i(ni.UL.x,maxY-minY);
        ni.BR = cv::Point2i(ni.UR.x,maxY-minY);
        ni.vKeys.reserve(vToDistributeKeys.size());

        lNodes.push_back(ni);
        vpIniNodes[i] = &lNodes.back();
    }

    //Associate points to childs
    for(size_t i=0;i<vToDistributeKeys.size();i++)
    {
        const cv::KeyPoint &kp = vToDistributeKeys[i];
        vpIniNodes[kp.pt.x/hX]->vKeys.push_back(kp);
    }

    list<ExtractorNode>::iterator lit = lNodes.begin();

    while(lit!=lNodes.end())
    {
        if(lit->vKeys.size()==1)
        {
            lit->bNoMore=true;
            lit++;
        }
        else if(lit->vKeys.empty())
            lit = lNodes.erase(lit);
        else
            lit++;
    }
    
    …………
    …………
}



特征点提取 

        作者重载了()运算符,使得其他类可以将ORBextractor类型变量当作函数来使用,这个函数调用了ORBextractor.cc中的其他函数,实现了特征点提取。

/**
 * @brief 这个函数重载了() 运算符,使得其他类可以将ORBextractor类型变量当作函数来使用.
 * @param _image    原始图的图像
 * @param _mask     掩膜mask
 * @param _keypoints    存储特征点关键点的向量
 * @param _descriptors  存储特征点描述子的矩阵
 * @note  该类最重要的函数,相当于其它类调用此函数时做了一次完整的特征点提取
 */
void ORBextractor::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,
                      OutputArray _descriptors)
{ 
    //1.检查图像有效性
    if(_image.empty())
        return;

    Mat image = _image.getMat();//获取图像大小
    assert(image.type() == CV_8UC1 );//判断图像的格式是否正确,要求是单通道灰度值

    //2.构建图像金字塔
    ComputePyramid(image);

    vector < vector<KeyPoint> > allKeypoints;
    //3.提取图像特征点并筛选
    ComputeKeyPointsOctTree(allKeypoints);
    //ComputeKeyPointsOld(allKeypoints);

    //4.计算图像的描述子,并储存至descriptors
    Mat descriptors;

    int nkeypoints = 0;
    for (int level = 0; level < nlevels; ++level)
        nkeypoints += (int)allKeypoints[level].size();
    if( nkeypoints == 0 )
        _descriptors.release();
    else
    {
        _descriptors.create(nkeypoints, 32, CV_8U);
        descriptors = _descriptors.getMat();
    }

    _keypoints.clear();//清空用作返回特征点提取结果的vector容器
    _keypoints.reserve(nkeypoints);//预分配正确大小的空间

    int offset = 0;
    //开始遍历每一层图像
    for (int level = 0; level < nlevels; ++level)
    {
        //获取在allKeypoints中当前层特征点容器的句柄
        vector<KeyPoint>& keypoints = allKeypoints[level];
        int nkeypointsLevel = (int)keypoints.size();//本层的特征点数

        if(nkeypointsLevel==0)
            continue;

        
        Mat workingMat = mvImagePyramid[level].clone();// 深拷贝当前金字塔所在层级的图像
        //5.对图像进行高斯模糊
        GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);

        //提取特征点的时候,使用的是清晰的原图像;这里计算描述子的时候,为了避免图像噪声的影响,使用了高斯模糊的图像
        Mat desc = descriptors.rowRange(offset, offset + nkeypointsLevel);
        //6.计算高斯模糊后图像的描述子
        computeDescriptors(workingMat, keypoints, desc, pattern);

        offset += nkeypointsLevel;// 更新偏移量的值 

        //7.对非第0层图像中的特征点的坐标恢复到第0层图像(原图像)的坐标系下
        if (level != 0)
        {
            float scale = mvScaleFactor[level]; //getScale(level, firstLevel, scaleFactor);
            for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
                 keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
                keypoint->pt *= scale;
        }
        //将keypoints中内容插入到_keypoints的末尾
        //输出_keypoints
        _keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());
    }
}

特征提取效果

这里用自己录得一段数据,orbslam做的特征点提取:

orbslam

最近也尝试用卷积网络和深度网络来做特征提取和匹配,这里做个对比:

卷积网络

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值