ORB_SLAM2原理及代码解析:Tracking::MonocularInitialization() 函数

目录

[1 作用](#1 作用)

[2 参数及含义说明](#2 参数及含义说明)

[3 代码逻辑](#3 代码逻辑)

[4 代码](#4 代码)

[5 代码解析](#5 代码解析)

[5.1 检查是否有初始化器](#5.1 检查是否有初始化器)

[5.1.1 随机采样一致性算法:RANdom SAmple Consensus(RANSAC)](#5.1.1 随机采样一致性算法:RANdom SAmple Consensus(RANSAC))

(1)作用

(2)原理

[5.2 初始化](#5.2 初始化)

[5.3 当前帧和参考帧中找匹配点](#5.3 当前帧和参考帧中找匹配点)

[5.3.1 最近邻比值阈值(nnratio)](#5.3.1 最近邻比值阈值(nnratio))

[5.3.2 参数说明](#5.3.2 参数说明)

[5.4 调用初始化器估计R、t,三角化](#5.4 调用初始化器估计R、t,三角化)

[5.5 设置位姿、建立初始化地图](#5.5 设置位姿、建立初始化地图)

1 作用

单目相机初始化:

(1)找到合适的两帧(参考帧 + 当前帧)、估计相机的初始运动(R, t);

(2)三角化生成第一批 3D 地图点;

(3)构建初始地图。

2 参数及含义说明

3 代码逻辑

(1)第一次检测到足够特征点(>100) → 保存为 mInitialFrame,并构造初始化器Initializer

(2)后续帧进入初始化阶段

ORBmatchermInitialFramemCurrentFrame 之间找匹配点。

如果匹配数不足 100 → 初始化失败,清空。

如果匹配足够 → 用 Initializer->Initialize() 估计 Rcwtcw,并三角化得到初始 3D 点。

设置初始帧和当前帧的位姿,调用 CreateInitialMapMonocular() 建图。

4 代码

cpp 复制代码
void Tracking::MonocularInitialization()
{

    if(!mpInitializer)
    {
        // Set Reference Frame
        if(mCurrentFrame.mvKeys.size()>100)
        {
            mInitialFrame = Frame(mCurrentFrame);
            mLastFrame = Frame(mCurrentFrame);
            mvbPrevMatched.resize(mCurrentFrame.mvKeysUn.size());
            for(size_t i=0; i<mCurrentFrame.mvKeysUn.size(); i++)
                mvbPrevMatched[i]=mCurrentFrame.mvKeysUn[i].pt;

            if(mpInitializer)
                delete mpInitializer;

            mpInitializer =  new Initializer(mCurrentFrame,1.0,200);

            fill(mvIniMatches.begin(),mvIniMatches.end(),-1);

            return;
        }
    }
    else
    {
        // Try to initialize
        if((int)mCurrentFrame.mvKeys.size()<=100)
        {
            delete mpInitializer;
            mpInitializer = static_cast<Initializer*>(NULL);
            fill(mvIniMatches.begin(),mvIniMatches.end(),-1);
            return;
        }

        // Find correspondences
        ORBmatcher matcher(0.9,true);
        int nmatches = matcher.SearchForInitialization(mInitialFrame,mCurrentFrame,mvbPrevMatched,mvIniMatches,100);

        // Check if there are enough correspondences
        if(nmatches<100)
        {
            delete mpInitializer;
            mpInitializer = static_cast<Initializer*>(NULL);
            return;
        }

        cv::Mat Rcw; // Current Camera Rotation
        cv::Mat tcw; // Current Camera Translation
        vector<bool> vbTriangulated; // Triangulated Correspondences (mvIniMatches)

        if(mpInitializer->Initialize(mCurrentFrame, mvIniMatches, Rcw, tcw, mvIniP3D, vbTriangulated))
        {
            for(size_t i=0, iend=mvIniMatches.size(); i<iend;i++)
            {
                if(mvIniMatches[i]>=0 && !vbTriangulated[i])
                {
                    mvIniMatches[i]=-1;
                    nmatches--;
                }
            }

            // Set Frame Poses
            mInitialFrame.SetPose(cv::Mat::eye(4,4,CV_32F));
            cv::Mat Tcw = cv::Mat::eye(4,4,CV_32F);
            Rcw.copyTo(Tcw.rowRange(0,3).colRange(0,3));
            tcw.copyTo(Tcw.rowRange(0,3).col(3));
            mCurrentFrame.SetPose(Tcw);

            CreateInitialMapMonocular();
        }
    }
}

5 代码解析

5.1 检查是否有初始化器
cpp 复制代码
    if(!mpInitializer)
    {
        // Set Reference Frame
        if(mCurrentFrame.mvKeys.size()>100)
        {
            mInitialFrame = Frame(mCurrentFrame);
            mLastFrame = Frame(mCurrentFrame);
            //分配存储空间,另mvbPrevMatched空间大小等于当前帧去畸变后的特征点数量
            mvbPrevMatched.resize(mCurrentFrame.mvKeysUn.size());
            for(size_t i=0; i<mCurrentFrame.mvKeysUn.size(); i++)
            //.pt二维像素坐标,将当前帧归一化去畸变后的像素坐标存储在mvbPrevMatched
                mvbPrevMatched[i]=mCurrentFrame.mvKeysUn[i].pt;

            if(mpInitializer)
                delete mpInitializer;
            
            //RANSAC 的噪声阈值 (1.0 像素) ,迭代次数 (200)
            mpInitializer =  new Initializer(mCurrentFrame,1.0,200);

            //将mvIniMatches中所有元素全部置为-1
            fill(mvIniMatches.begin(),mvIniMatches.end(),-1);

            return;
        }
    }
5.1.1 随机采样一致性算法:RANdom SAmple Consensus(RANSAC)
(1)作用

从数据里"挖出"一组可靠的内点(inliers),用它们来拟合正确的模型,并自动剔除外点。

(2)原理

求解对极几何E矩阵为例:

随机采样,

从匹配点集中随机选取最小数量的点(比如 8 点法 → 取 8 对点);

拟合模型,

用这组点计算一个候选的E矩阵;

验证一致性,

用这个E矩阵检查所有匹配点,看有多少点满足约束(误差 < 阈值)。符合约束的点称为内点 ;

记录最佳结果,

如果内点数比之前的最好结果还多 → 更新最佳模型;

迭代多次,

重复 1~4(比如 200 次),最终选出包含最多内点的模型。

5.2 初始化
cpp 复制代码
    else
    {
        // Try to initialize
        if((int)mCurrentFrame.mvKeys.size()<=100)
        {
            delete mpInitializer;
            mpInitializer = static_cast<Initializer*>(NULL);
            fill(mvIniMatches.begin(),mvIniMatches.end(),-1);
            return;
        }

5.1、5.2综合起来看,如果没有初始化器则找到一帧特征点大于100的作为初始化器的建立,如果有初始化器但当前帧特征点数小于100,则删除重新找。

5.3 当前帧和参考帧中找匹配点
cpp 复制代码
        // Find correspondences
        //根据ORB描述子在两帧图像之间寻找对应点,最近邻比值阈值0.9,检查特征点方向是否一致
        ORBmatcher matcher(0.9,true);
        int nmatches = matcher.SearchForInitialization(mInitialFrame,mCurrentFrame,mvbPrevMatched,mvIniMatches,100);

        // Check if there are enough correspondences
        if(nmatches<100)
        {
            delete mpInitializer;
            mpInitializer = static_cast<Initializer*>(NULL);
            return;
        }
5.3.1 最近邻比值阈值(nnratio)

(1)对一个特征点找出在另一帧中的最近邻(距离 d1)和次近邻(距离 d2),该距离即汉明距离(Hamming Distance)

(2)如果 d1 < nnratio * d2,就认为匹配有效。

5.3.2 参数说明

(1)mInitialFrame

参考帧,保存了参考帧的关键点、ORB描述子

(2)mCurrentFrame

当前帧,要和参考帧匹配

(3)mvbPrevMatched

参考帧中特征点在上一帧的位置,初始化时将当前帧的关键点位置给mvbPrevMatched,连续跟踪mvbPrevMatched是参考帧的关键点在当前帧中的预测位置,由运动模型计算。

(4)mvIniMatches

参考帧在当前帧中的匹配情况

(5)100

匹配的范围,在附近 100 像素范围内搜索可能的匹配点

PS.SearchForInitialization() 函数详见:

https://blog.csdn.net/weixin_45728280/article/details/152455350?sharetype=blogdetail&sharerId=152455350&sharerefer=PC&sharesource=weixin_45728280&spm=1011.2480.3001.8118

5.4 调用初始化器估计R、t,三角化
cpp 复制代码
        cv::Mat Rcw; // Current Camera Rotation
        cv::Mat tcw; // Current Camera Translation
        vector<bool> vbTriangulated; // Triangulated Correspondences (mvIniMatches)

        //用参考帧 + 当前帧 → 估计相机运动(R,t)并恢复初始 3D 点云
        if(mpInitializer->Initialize(mCurrentFrame, mvIniMatches, Rcw, tcw, mvIniP3D, vbTriangulated))
        {
            for(size_t i=0, iend=mvIniMatches.size(); i<iend;i++)
            {
                if(mvIniMatches[i]>=0 && !vbTriangulated[i])
                {
                    mvIniMatches[i]=-1;
                    nmatches--;
                }
            }

PS.Initialize() 函数详见:

https://blog.csdn.net/weixin_45728280/article/details/152455699?sharetype=blogdetail&sharerId=152455699&sharerefer=PC&sharesource=weixin_45728280&spm=1011.2480.3001.8118

5.5 设置位姿、建立初始化地图
cpp 复制代码
            // Set Frame Poses
            mInitialFrame.SetPose(cv::Mat::eye(4,4,CV_32F));
            cv::Mat Tcw = cv::Mat::eye(4,4,CV_32F);
            Rcw.copyTo(Tcw.rowRange(0,3).colRange(0,3));
            tcw.copyTo(Tcw.rowRange(0,3).col(3));
            mCurrentFrame.SetPose(Tcw);

            CreateInitialMapMonocular();
        }
    }

CreateInitialMapMonocular() 函数详见:

https://blog.csdn.net/weixin_45728280/article/details/152511800?sharetype=blogdetail&sharerId=152511800&sharerefer=PC&sharesource=weixin_45728280&spm=1011.2480.3001.8118

相关推荐
NAGNIP9 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab10 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab10 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP14 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年14 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼14 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS15 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区16 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈16 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang16 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx