ORB-SLAM代码详解之SLAM.TrackMonocular

  • 总述
    • TrackMonocular
    • mpTracker-GrabImageMonocular
      • Frame
        • ExtractORB
        • ComputePyramid
        • ComputeKeyPointsOctTree
        • DistributeOctTree
        • computeDescriptors
        • computeOrientation
      • Track

转载请注明出处:http://blog.csdn.net/c602273091/article/details/54955663

总述

在mono_kitti.cc中运行kitti数据集的时候,初始化完了system之后,就出现了并行的多个线程:追踪、局部地图构建、闭环检测、地图显示等等。这个时候我们就需要喂入数据给整个系统,所以就是:

SLAM.TrackMonocular(im,tframe);

把传感器采集的图片以及时间戳传入,我们就可以更新系统的状态,获取新的数据,更新地图。

这个时候TrackMonocular的流程图为:
ORB-SLAM代码详解之SLAM.TrackMonocular_第1张图片

TrackMonocular

对TrackMonocular进行解释,具体如下:

cv::Mat System::TrackMonocular(const cv::Mat &im, const double &timestamp)
{
    // 传感器不是单目摄像头、退出
    if(mSensor!=MONOCULAR)
    {
        cerr << "ERROR: you called TrackMonocular but input sensor was not set to Monocular." << endl;
        exit(-1);
    }

    // Check mode change
    // 这一部分主要是对局部地图线程进行操作.
    // mbActivateLocalizationMode是是否停止局部地图线程
    // mbDeactivateLocalizationMode是是否清空局部地图.
    {
        // 独占锁,主要是为了mbActivateLocalizationMode和mbDeactivateLocalizationMode
        // 不会发生混乱,没有死锁或者在临界区
        unique_lock<mutex> lock(mMutexMode);
        // mbActivateLocalizationMode为true会关闭局部地图线程
        if(mbActivateLocalizationMode)
        {
            mpLocalMapper->RequestStop();
            // 设置local map的mbStopRequested,mbAbortBA为true.
            // 当这两个为true的时候,那么进行就会去关闭局部地图的线程

            // Wait until Local Mapping has effectively stopped
            // mbStopped为true,说明局部地图线程已经关闭了
            while(!mpLocalMapper->isStopped())
            {
                usleep(1000);
            }
            // 局部地图关闭以后,只进行追踪的线程
            // 只计算相机的位姿,没有对局部地图进行更新
            // 设置mbOnlyTracking为真
            mpTracker->InformOnlyTracking(true);
            // 执行完当前的部分之和把mbActivateLocalizationMode再置回false.
            // 当然这里设置mbActivateLocalizationMode为true的部分应该是没有新的关键帧和点云的时候
            // 关闭线程可以使得别的线程得到更多的资源
            mbActivateLocalizationMode = false;
        }
        // 如果mbDeactivateLocalizationMode是true
        // 设置mbActivateLocalizationMode为false
        // 局部地图线程就被释放, 关键帧从局部地图中删除.
        // mbStopped和mbStopRequested被置为false.
        if(mbDeactivateLocalizationMode)
        {
            mpTracker->InformOnlyTracking(false);
            mpLocalMapper->Release();
            mbDeactivateLocalizationMode = false;
        }
    }

    // Check reset
    // 检查是否需要进行复位重置.
    {
        // 给mbReset加锁,防止被别的线程修改
        unique_lock<mutex> lock(mMutexReset);
        if(mbReset)
        {
            // mpViwer暂停,视图停止更新
            // 局部地图:mpLocalMapper和闭环检测:mpLoopClosing被停止.
            // Bow:mpKeyFrameDB和mpMap被清空
            // 就是把所有资源释放
            mpTracker->Reset();
            mbReset = false;
        }
    }
    // 可以看出上面这两部分都是对于各个线程状态的反馈.
    // 其实可以看做是对上一个状态的反馈.
    // 接下来的部分才是最重要的部分,获取数据,对各个线程的数据进行更新, 对地图重新进行计算.
    return mpTracker->GrabImageMonocular(im,timestamp);
}

mpTracker->GrabImageMonocular

这部分是上一部分的核心。对GrabImageMonocular的解释如下:

cv::Mat Tracking::GrabImageMonocular(const cv::Mat &im, const double ×tamp)
{
    // 把输入的图片设为当前帧.
    mImGray = im;

    // 若图片是三\四通道的,还需要转化为灰度图.
    if(mImGray.channels()==3)
    {
        if(mbRGB)
            cvtColor(mImGray,mImGray,CV_RGB2GRAY);
        else
            cvtColor(mImGray,mImGray,CV_BGR2GRAY);
    }
    else if(mImGray.channels()==4)
    {
        if(mbRGB)
            cvtColor(mImGray,mImGray,CV_RGBA2GRAY);
        else
            cvtColor(mImGray,mImGray,CV_BGRA2GRAY);
    }

    // 若状态是未初始化或者当前获取的图片是第一帧的时候,那么就需要进行初始化.
    // 传入的参数就是当前帧(灰度图)、时间戳、ORBextractor(ORB特征)、DBoW2的字典、标定矩阵、畸变参数、立体视觉的(baseline)x(fx)、可靠点的距离的阈值
    // mCurrentFrame就是对当前帧进行处理. 获取ORB特征、获取尺度信息.
    // 这里需要进行更加深入的研究.
    if(mState==NOT_INITIALIZED || mState==NO_IMAGES_YET)
        mCurrentFrame = Frame(mImGray,timestamp,mpIniORBextractor,mpORBVocabulary,mK,mDistCoef,mbf,mThDepth);
    else
        mCurrentFrame = Frame(mImGray,timestamp,mpORBextractorLeft,mpORBVocabulary,mK,mDistCoef,mbf,mThDepth);

    // 追踪,初始化以及特征匹配.
    // 这里需要更加深入的研究.
    Track();

    // mTcw是位姿  
    // clone是cv::Mat特有的方法.
    // 实现的就是完全拷贝, 把数据完全拷贝而不共享数据.
    return mCurrentFrame.mTcw.clone();
}

Frame

对于这一部分,需要特别研究一下:

// 若状态是未初始化或者当前获取的图片是第一帧的时候,那么就需要进行初始化.
// 传入的参数就是当前帧(灰度图)、时间戳、ORBextractor(ORB特征)、DBoW2的字典、标定矩阵、畸变参数、立体视觉的(baseline)x(fx)、可靠点的距离的阈值
// mCurrentFrame就是对当前帧进行处理. 获取ORB特征、获取尺度信息.
// 这里需要进行更加深入的研究.
if(mState==NOT_INITIALIZED || mState==NO_IMAGES_YET)
        mCurrentFrame = Frame(mImGray,timestamp,mpIniORBextractor,mpORBVocabulary,mK,mDistCoef,mbf,mThDepth);
    else
        mCurrentFrame = Frame(mImGray,timestamp,mpORBextractorLeft,mpORBVocabulary,mK,mDistCoef,mbf,mThDepth);

ORB-SLAM代码详解之SLAM.TrackMonocular_第2张图片

整个Frame的流程图如上所示,比较清晰。

对Frame部分进行注释得到:

Frame::Frame(const cv::Mat &imGray, const double &timeStamp, ORBextractor* extractor,ORBVocabulary* voc, cv::Mat &K, cv::Mat &distCoef, const float &bf, const float &thDepth)
    :mpORBvocabulary(voc),mpORBextractorLeft(extractor),mpORBextractorRight(static_cast(NULL)),
     mTimeStamp(timeStamp), mK(K.clone()),mDistCoef(distCoef.clone()), mbf(bf), mThDepth(thDepth)
{
    // Frame ID
    // 当前帧的ID
    // 每一帧都有唯一的一个ID做标识
    // 从0开始
    mnId=nNextId++;

    // Scale Level Info
    // 获取尺度信息,目前还不了解这些是做什么的?
    // 这些是属于Frame的变量.
    mnScaleLevels = mpORBextractorLeft->GetLevels();
    mfScaleFactor = mpORBextractorLeft->GetScaleFactor();
    mfLogScaleFactor = log(mfScaleFactor);
    mvScaleFactors = mpORBextractorLeft->GetScaleFactors();
    mvInvScaleFactors = mpORBextractorLeft->GetInverseScaleFactors();
    mvLevelSigma2 = mpORBextractorLeft->GetScaleSigmaSquares();
    mvInvLevelSigma2 = mpORBextractorLeft->GetInverseScaleSigmaSquares();

    // ORB extraction
    // 提取ORB特征.
    // 可以继续研究.
    ExtractORB(0,imGray);

    // KeyPoint (float x, float y, float _size, float _angle=-1, float _response=0, int _octave=0, int _class_id=-1)
    // 进行ORB特征提取以后可以得到关键点的数量. 存储在关键点的向量内.
    N = mvKeys.size();

    // 没有找到关键点,返回.
    if(mvKeys.empty())
        return;
    // 进行畸变校正,找到关键点实际应该在普通摄像头中的位置.
    UndistortKeyPoints();

    // Set no stereo information
    // 把立体信息部分设置为-1.
    mvuRight = vector<float>(N,-1);
    mvDepth = vector<float>(N,-1);

    // 初始化点和各个点是否是outlier的状态.
    mvpMapPoints = vector(N,static_cast(NULL));
    mvbOutlier = vector<bool>(N,false);

    // This is done only for the first Frame (or after a change in the calibration)
    // 第一帧或者是标定矩阵发生了变化以后
    if(mbInitialComputations)
    {
        // 计算畸变矫正之后的边界.
        ComputeImageBounds(imGray);

        // mnMax(Min)X(Y)是畸变矫正以后的边界.    
        mfGridElementWidthInv=static_cast<float>(FRAME_GRID_COLS)/static_cast<float>(mnMaxX-mnMinX);
        mfGridElementHeightInv=static_cast<float>(FRAME_GRID_ROWS)/static_cast<float>(mnMaxY-mnMinY);

        // 从配置文件中读取数据赋给相应元素.
        fx = K.at<float>(0,0);
        fy = K.at<float>(1,1);
        cx = K.at<float>(0,2);
        cy = K.at<float>(1,2);
        invfx = 1.0f/fx;
        invfy = 1.0f/fy;

        mbInitialComputations=false;
    }
    // Stereo baseline in meters.
    // 计算立体匹配的时候的baseline.
    // mbf来自与配置文件.
    mb = mbf/fx;

    // 把每一帧分割成48x64个网格
    // 根据关键点的畸变矫正后的位置分在不同的网格里面.
    AssignFeaturesToGrid();
}

ExtractORB

ExtractORB(0,imGray);

这里提取的就是左边的摄像头的数据。

void Frame::ExtractORB(int flag, const cv::Mat &im)
{
    if(flag==0)
        (*mpORBextractorLeft)(im,cv::Mat(),mvKeys,mDescriptors);
    else
        (*mpORBextractorRight)(im,cv::Mat(),mvKeysRight,mDescriptorsRight);
}

这个是在ORBExtractor.h的ORBExtractor类里重载的操作符’()’: 这里忽略了mask这个变量。

void operator()( cv::InputArray image, cv::InputArray mask,
                 std::vector& keypoints,
                 cv::OutputArray descriptors);

在ORBextractor.cc里面对这个有详细的介绍。接下来对这个模块进行详细的介绍。

// 输入的变量:
//    _image: 获取的图片像素信息(灰度图)
//    _mask: 掩码,这个部分的位置就不需要计算描述子
//    _keypoints: 关键点的位置
//    _descriptors: 描述子,没有使用引用,看来是没有用到了 
void ORBextractor::operator()( InputArray _image, InputArray _mask, vector& _keypoints,
                      OutputArray _descriptors)
{ 
    // 如果没有获取图片,那么返回
    if(_image.empty())
        return;

    // 获取图片信息赋给Mat类型的image
    Mat image = _image.getMat();
    // 判断通道是否为单通道灰度图 
    assert(image.type() == CV_8UC1 );

    // Pre-compute the scale pyramid
    // 计算尺度的金字塔,可以稍微看一眼
    ComputePyramid(image);

    // 保存所有的关键点
    vector < vector > allKeypoints;
    // 计算关键点,找到FAST关键点,值得看一下
    ComputeKeyPointsOctTree(allKeypoints);
    //ComputeKeyPointsOld(allKeypoints);

    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();
    _keypoints.reserve(nkeypoints);

    int offset = 0;
    for (int level = 0; level < nlevels; ++level)
    {
        vector& keypoints = allKeypoints[level];
        int nkeypointsLevel = (int)keypoints.size();

        if(nkeypointsLevel==0)
            continue;

        // preprocess the resized image
        Mat workingMat = mvImagePyramid[level].clone();
        // 使用高斯模糊为了计算BRIEF的时候去噪
        GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);

        // Compute the descriptors
        // 计算rBRIEF描述子,采用的是高斯分布取点
        Mat desc = descriptors.rowRange(offset, offset + nkeypointsLevel);
        computeDescriptors(workingMat, keypoints, desc, pattern);

        offset += nkeypointsLevel;

        // 对关键点的位置做尺度恢复,恢复到原图的位置
        // Scale keypoint coordinates
        if (level != 0)
        {
            float scale = mvScaleFactor[level]; //getScale(level, firstLevel, scaleFactor);
            for (vector::iterator keypoint = keypoints.begin(),
                 keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
                keypoint->pt *= scale;
        }
        // And add the keypoints to the output
        // 
        _keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());
    }
}

在这里我发现还是要复习一下ORB特征【1】【2】。看【2】会更好,因为在这里我把算法流程讲得还算清楚。

在这里的level和scalefactor我不清楚,我把它打印出来看了一下:
ORB-SLAM代码详解之SLAM.TrackMonocular_第3张图片

图片金字塔一共是8层,随着level越来越大,scale factor越来越大,图片越来越小。当恢复金字塔关键点的原图坐标的时候就把它乘以scale factor。

ComputePyramid()

计算尺度金字塔。

void ORBextractor::ComputePyramid(cv::Mat image)
{
    // 计算nlevel个尺度的图片
    for (int level = 0; level < nlevels; ++level)
    {
        // 获取尺度
        float scale = mvInvScaleFactor[level];
        // 计算当前尺度下图片的大小
        Size sz(cvRound((float)image.cols*scale), cvRound((float)image.rows*scale));
        Size wholeSize(sz.width + EDGE_THRESHOLD*2, sz.height + EDGE_THRESHOLD*2);
        Mat temp(wholeSize, image.type()), masktemp;
        // 图片初始化
        mvImagePyramid[level] = temp(Rect(EDGE_THRESHOLD, EDGE_THRESHOLD, sz.width, sz.height));

        // Compute the resized image
        // 计算图片金字塔在该尺度下的金字塔
        if( level != 0 )
        {
            // 对图片进行尺度变换
            // 在这里我们一般是把更清晰的图片变化更加模糊的图片
            // 从这里可以看出,这里的尺度的值应该是小于1的,所以才会命名为mvIneScale,就是scale的倒数。 
            resize(mvImagePyramid[level-1], mvImagePyramid[level], sz, 0, 0, INTER_LINEAR);
            // 【3】这里主要是为了方便做一些卷积计算来做一些边界补充
            copyMakeBorder(mvImagePyramid[level], temp, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, BORDER_REFLECT_101+BORDER_ISOLATED);            
        }
        else
        {
            copyMakeBorder(image, temp, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, BORDER_REFLECT_101);            
        }
    }
}

ComputeKeyPointsOctTree()

计算关键点。

void ORBextractor::ComputeKeyPointsOctTree(vector<vector >& allKeypoints)
{
    // 一共计算nlevel个尺度的关键点
    allKeypoints.resize(nlevels);

    // 窗口的大小
    const float W = 30;

    // 对每个尺度计算它的关键点
    for (int level = 0; level < nlevels; ++level)
    {
        // 计算边界,在这个边界内计算FAST关键点
        const int minBorderX = EDGE_THRESHOLD-3;
        const int minBorderY = minBorderX;
        const int maxBorderX = mvImagePyramid[level].cols-EDGE_THRESHOLD+3;
        const int maxBorderY = mvImagePyramid[level].rows-EDGE_THRESHOLD+3;

        vector vToDistributeKeys;
        vToDistributeKeys.reserve(nfeatures*10);

        // 计算宽度和高度 
        const float width = (maxBorderX-minBorderX);
        const float height = (maxBorderY-minBorderY);

        // 计算有多少个窗口
        const int nCols = width/W;
        const int nRows = height/W;
        // 计算每个窗口的大小
        const int wCell = ceil(width/nCols);
        const int hCell = ceil(height/nRows);

        // 对每个窗口进行遍历
        for(int i=0; iconst float iniY =minBorderY+i*hCell;
            float maxY = iniY+hCell+6;
            // 已经出了图片的有效区域了
            if(iniY>=maxBorderY-3)
                continue;
            // 超出边界的话就使用计算最宽的边界    
            if(maxY>maxBorderY)
                maxY = maxBorderY;

            for(int j=0; j// *********************************
                // 计算每列的位置
                const float iniX =minBorderX+j*wCell;
                float maxX = iniX+wCell+6;
                if(iniX>=maxBorderX-6)
                    continue;
                if(maxX>maxBorderX)
                    maxX = maxBorderX;
                // 计算FAST关键点
                vector vKeysCell;
                FAST(mvImagePyramid[level].rowRange(iniY,maxY).colRange(iniX,maxX),
                     vKeysCell,iniThFAST,true);

                if(vKeysCell.empty())
                {
                    FAST(mvImagePyramid[level].rowRange(iniY,maxY).colRange(iniX,maxX),
                         vKeysCell,minThFAST,true);
                }
                // 如果找到的关键点不为空,就加入到vToDistributeKeys.
                if(!vKeysCell.empty())
                {
                    for(vector::iterator vit=vKeysCell.begin(); vit!=vKeysCell.end();vit++)
                    {
                        // 计算实际的位置
                        (*vit).pt.x+=j*wCell;
                        (*vit).pt.y+=i*hCell;
                        vToDistributeKeys.push_back(*vit);
                    }
                }
            }
        }

        vector & keypoints = allKeypoints[level];
        // 至少保留n个特征点
        keypoints.reserve(nfeatures);
        // 根据Harris角点的score进行排序,保留正确的
        keypoints = DistributeOctTree(vToDistributeKeys, minBorderX, maxBorderX,
                                      minBorderY, maxBorderY,mnFeaturesPerLevel[level], level);
        // 这里的PATCH_SIZE是31
        const int scaledPatchSize = PATCH_SIZE*mvScaleFactor[level];

        // Add border to coordinates and scale information
        const int nkps = keypoints.size();
        for(int i=0; i// compute orientations
    // 计算nlevels个尺度下各个关键点的方向
    for (int level = 0; level < nlevels; ++level)
        computeOrientation(mvImagePyramid[level], allKeypoints[level], umax);
}

DistributeOctTree()

计算用FAST选出来的特征点是否合格。

vector ORBextractor::DistributeOctTree(const vector& vToDistributeKeys, const int &minX, const int &maxX, const int &minY, const int &maxY, const int &N, const int &level)

这个地方看得不是很懂,这里是计算出每个level的关键点的score,接着进行排序,选出了n个关键点。

Mark 一下,改日回来继续看。

computeDescriptors()

计算rBRIEF描述子。

在这个函数里计算了rBrief的描述子。

static void computeDescriptors(const Mat& image, vector& keypoints, Mat& descriptors,
                               const vector& pattern)
{
    // 初始化描述子 
    descriptors = Mat::zeros((int)keypoints.size(), 32, CV_8UC1);
    for (size_t i = 0; i < keypoints.size(); i++)
        // 计算每个关键点的描述子
        computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr((int)i));
}

计算描述子如下:

计算256维的描述子。

const float factorPI = (float)(CV_PI/180.f);
static void computeOrbDescriptor(const KeyPoint& kpt,
                                 const Mat& img, const Point* pattern,
                                 uchar* desc)
{
    float angle = (float)kpt.angle*factorPI;
    float a = (float)cos(angle), b = (float)sin(angle);

    const uchar* center = &img.at(cvRound(kpt.pt.y), cvRound(kpt.pt.x));
    const int step = (int)img.step;

    #define GET_VALUE(idx) \
        center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + \
               cvRound(pattern[idx].x*a - pattern[idx].y*b)]


    for (int i = 0; i < 32; ++i, pattern += 16)
    {
        int t0, t1, val;
        t0 = GET_VALUE(0); t1 = GET_VALUE(1);
        val = t0 < t1;
        t0 = GET_VALUE(2); t1 = GET_VALUE(3);
        val |= (t0 < t1) << 1;
        t0 = GET_VALUE(4); t1 = GET_VALUE(5);
        val |= (t0 < t1) << 2;
        t0 = GET_VALUE(6); t1 = GET_VALUE(7);
        val |= (t0 < t1) << 3;
        t0 = GET_VALUE(8); t1 = GET_VALUE(9);
        val |= (t0 < t1) << 4;
        t0 = GET_VALUE(10); t1 = GET_VALUE(11);
        val |= (t0 < t1) << 5;
        t0 = GET_VALUE(12); t1 = GET_VALUE(13);
        val |= (t0 < t1) << 6;
        t0 = GET_VALUE(14); t1 = GET_VALUE(15);
        val |= (t0 < t1) << 7;

        desc[i] = (uchar)val;
    }

    #undef GET_VALUE
}

描述子是采用了高斯分布采样得到的。在里面定义为:

static int bit_pattern_31_[256*4] =
{
    8,-3, 9,5/*mean (0), correlation (0)*/,
    4,2, 7,-12/*mean (1.12461e-05), correlation (0.0437584)*/,
    -11,9, -8,2/*mean (3.37382e-05), correlation (0.0617409)*/,
    7,-12, 12,-13/*mean (5.62303e-05), correlation (0.0636977)*/,
    2,-13, 2,12/*mean (0.000134953), correlation (0.085099)*/,
    1,-7, 1,6/*mean (0.000528565), correlation (0.0857175)*/,
    -2,-10, -2,-4/*mean (0.0188821), correlation (0.0985774)*/,
    -13,-13, -11,-8/*mean (0.0363135), correlation (0.0899616)*/,
    -13,-3, -12,-9/*mean (0.121806), correlation (0.099849)*/,
    10,4, 11,9/*mean (0.122065), correlation (0.093285)*/,
    -13,-8, -8,-9/*mean (0.162787), correlation (0.0942748)*/,
    -11,7, -9,12/*mean (0.21561), correlation (0.0974438)*/,
    7,7, 12,6/*mean (0.160583), correlation (0.130064)*/,
    ......
}    

computeOrientation()

在computeOctKeypointTree里调用了这个函数,调用为:

computeOrientation(mvImagePyramid[level], allKeypoints[level], umax);

static void computeOrientation(const Mat& image, vector& keypoints, const vector<int>& umax)
{
    for (vector::iterator keypoint = keypoints.begin(),
         keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
    {
        // 计算nlevel这个尺度的图片的关键点的方向
        keypoint->angle = IC_Angle(image, keypoint->pt, umax);
    }
}

IC_Angle: 这个部分对照我介绍的ORB特征的那篇博客,里面有详细介绍【2】。里面有详细的介绍。

static float IC_Angle(const Mat& image, Point2f pt,  const vector<int> & u_max)
{
    int m_01 = 0, m_10 = 0;

    const uchar* center = &image.at (cvRound(pt.y), cvRound(pt.x));

    // Treat the center line differently, v=0
    for (int u = -HALF_PATCH_SIZE; u <= HALF_PATCH_SIZE; ++u)
        m_10 += u * center[u];

    // Go line by line in the circuI853lar patch
    int step = (int)image.step1();
    for (int v = 1; v <= HALF_PATCH_SIZE; ++v)
    {
        // Proceed over the two lines
        int v_sum = 0;
        int d = u_max[v];
        for (int u = -d; u <= d; ++u)
        {
            int val_plus = center[u + v*step], val_minus = center[u - v*step];
            v_sum += (val_plus - val_minus);
            m_10 += u * (val_plus + val_minus);
        }
        m_01 += v * v_sum;
    }

    return fastAtan2((float)m_01, (float)m_10);
}

Track();

在上面对一帧的关键提取出来以后,就需要进行追踪了。

关键帧提取出了需要的关键点和描述子,接下来就是进行追踪了。

在trcking.cc里面,有void Tracking::Track()

这里面的东西还需要继续挖掘~ 已经Mark

参考:
【1】ORB:an efficient alternative to SIFT or SURF
【2】ORB特征讲解: http://blog.csdn.net/c602273091/article/details/56008370
【3】copyMakeBorder:http://blog.csdn.net/viewcode/article/details/8287599
【4】图像金字塔:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/pyramids/pyramids.html
【5】OpenCV resize:http://www.cnblogs.com/korbin/p/5612427.html
【6】OpenCV Copyboarder: http://blog.csdn.net/viewcode/article/details/8287599
【7】OpenCV PI: http://www.zybang.com/question/5768af44d5df172aedf5d9d6c9dc7c1e.html
【8】Fastatan2: http://blog.csdn.net/mingzhentanwo/article/details/45155307
【9】OpenCV FAST 源码分析:http://blog.csdn.net/zhaocj/article/details/40301561
【10】OpenCV FAST使用: http://www.bkjia.com/ASPjc/976906.html
【11】FAST API:http://opencv.jp/opencv-2.2_org/cpp/features2d_feature_detection_and_description.html?highlight=fast#cv-fast

你可能感兴趣的:(RL,&,DL,&,SLAM,Autonomous,Driving)