欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

orb-slam系列 Tracking线程 匹配(四)

程序员文章站 2024-03-25 18:56:16
...

ComputeStereoMatches() 函数

1)在同一行,设定一个阈值,找到大致的位置
2)在范围内所有的目标关键点进行甄别,需要满足条件在合理的视差范围内,并且金字塔层数不能超过一

    const float minZ = mb;   //人为设置最小深度
    const float minD = 0;    //最小视差
    const float maxD = mbf/minZ;    //最大视差

3)计算符合的描述子距离,选择最佳描述子。
4)SAD匹配提高像素匹配修正量bestincR

// 通过SAD匹配提高像素匹配修正量bestincR
        if(bestDist<thOrbDist)
        {
            // coordinates in image pyramid at keypoint scale
            // kpL.pt.x对应金字塔最底层坐标,将最佳匹配的特征点对尺度变换到尺度对应层 (scaleduL, scaledvL) (scaleduR0, )
            const float uR0 = mvKeysRight[bestIdxR].pt.x;
            const float scaleFactor = mvInvScaleFactors[kpL.octave];
            const float scaleduL = round(kpL.pt.x*scaleFactor);
            const float scaledvL = round(kpL.pt.y*scaleFactor);
            const float scaleduR0 = round(uR0*scaleFactor);
 
            // sliding window search
            const int w = 5; 
            cv::Mat IL = mpORBextractorLeft->mvImagePyramid[kpL.octave].rowRange(scaledvL-w,scaledvL+w+1).colRange(scaleduL-w,scaleduL+w+1);
            IL.convertTo(IL,CV_32F);
            IL = IL - IL.at<float>(w,w) * cv::Mat::ones(IL.rows,IL.cols,CV_32F);//简单归一化,减小光照强度影响
 
            int bestDist = INT_MAX;
            int bestincR = 0;
            const int L = 5;
            vector<float> vDists;
            vDists.resize(2*L+1); // 11
 
            // 滑动窗口的滑动范围为(-L, L),提前判断滑动窗口滑动过程中是否会越界
            const float iniu = scaleduR0+L-w; 
            const float endu = scaleduR0+L+w+1;
            if(iniu<0 || endu >= mpORBextractorRight->mvImagePyramid[kpL.octave].cols)
                continue;
 
            for(int incR=-L; incR<=+L; incR++)
            {
                // 横向滑动窗口
                cv::Mat IR = mpORBextractorRight->mvImagePyramid[kpL.octave].rowRange(scaledvL-w,scaledvL+w+1).colRange(scaleduR0+incR-w,scaleduR0+incR+w+1);
                IR.convertTo(IR,CV_32F);
                IR = IR - IR.at<float>(w,w) * cv::Mat::ones(IR.rows,IR.cols,CV_32F); 
                float dist = cv::norm(IL,IR,cv::NORM_L1); // 一范数,计算差的绝对值
                if(dist<bestDist)
                {
                    bestDist =  dist;// SAD匹配目前最小匹配偏差
                    bestincR = incR; // SAD匹配目前最佳的修正量
                }
 
                vDists[L+incR] = dist; // 正常情况下,这里面的数据应该以抛物线形式变化
            }
 
            if(bestincR==-L || bestincR==L) // SAD匹配失败,同时放弃求该特征点的深度
                continue;
 
            // Sub-pixel match (Parabola fitting)
            // 做抛物线拟合找谷底得到亚像素匹配deltaR
            // (bestincR,dist) (bestincR-1,dist) (bestincR+1,dist)三个点拟合出抛物线
            // bestincR+deltaR就是抛物线谷底的位置,相对SAD匹配出的最小值bestincR的修正量为deltaR
            const float dist1 = vDists[L+bestincR-1];
            const float dist2 = vDists[L+bestincR];
            const float dist3 = vDists[L+bestincR+1];
 
            const float deltaR = (dist1-dist3)/(2.0f*(dist1+dist3-2.0f*dist2));
 
            // 抛物线拟合得到的修正量不能超过一个像素,否则放弃求该特征点的深度
            if(deltaR<-1 || deltaR>1)
                continue;
 
            // Re-scaled coordinate
            // 通过描述子匹配得到匹配点位置为scaleduR0
            // 通过SAD匹配找到修正量bestincR
            // 通过抛物线拟合找到亚像素修正量deltaR
            float bestuR = mvScaleFactors[kpL.octave]*((float)scaleduR0+(float)bestincR+deltaR);
 
            // 这里是disparity,根据它算出depth
            float disparity = (uL-bestuR);
 
            if(disparity>=minD && disparity<maxD) // 最后判断视差是否在范围内
            {
                if(disparity<=0)
                {
                    disparity=0.01;
                    bestuR = uL-0.01;
                }
                // depth 是在这里计算的
                // depth=baseline*fx/disparity
                mvDepth[iL]=mbf/disparity;   // 深度
                mvuRight[iL] = bestuR;       // 匹配对在右图的横坐标
                vDistIdx.push_back(pair<int,int>(bestDist,iL)); // 该特征点SAD匹配最小匹配偏差
            }

特征点选取完毕

ComputeImageBounds()

/*****************************************************************
 *     函数属性:类Frame的成员函数ComputeImageBounds(const cv::Mat &imLeft)
 *     函数功能:
 *                 函数分为两部分,一部分是当图片需要矫正时:图像的边界为矫正后的图像边界
 *                 第二部分是函数不需要矫正时 图像的边界就是原图像的边界
 *                 此函数的最终结果为:将图形的边界赋值即mnMinX、mnMaxX、mnMinY、mnMaxY
 *     函数参数介绍:
 *                         imLeft:图像彩色图对应的灰度图
 *     备注:计算图像边界(在初始化时调用)
 ******************************************************************/
void Frame::ComputeImageBounds(const cv::Mat &imLeft)
{
    if(mDistCoef.at<float>(0)!=0.0)  //如果图片需要失真矫正
    {
        // 矫正前四个边界点:(0,0) (cols,0) (0,rows) (cols,rows)
        cv::Mat mat(4,2,CV_32F);
        mat.at<float>(0,0)=0.0; mat.at<float>(0,1)=0.0;
        mat.at<float>(1,0)=imLeft.cols; mat.at<float>(1,1)=0.0;
        mat.at<float>(2,0)=0.0; mat.at<float>(2,1)=imLeft.rows;
        mat.at<float>(3,0)=imLeft.cols; mat.at<float>(3,1)=imLeft.rows;

        // Undistort corners  对rgb图进行失真矫正
        mat=mat.reshape(2);
        cv::undistortPoints(mat,mat,mK,mDistCoef,cv::Mat(),mK);   //将矫正后的图像放入mat中
        mat=mat.reshape(1);

        mnMinX = min(mat.at<float>(0,0),mat.at<float>(2,0));  //左上和左下横坐标最小的
        mnMaxX = max(mat.at<float>(1,0),mat.at<float>(3,0)); //右上和右下横坐标最大的
        mnMinY = min(mat.at<float>(0,1),mat.at<float>(1,1));  //左上和右上纵坐标最小的
        mnMaxY = max(mat.at<float>(2,1),mat.at<float>(3,1)); //左下和右下纵坐标最小的

    }
    else          //如果图片不需要失真矫正
    {
        mnMinX = 0.0f;
        mnMaxX = imLeft.cols;
        mnMinY = 0.0f;
        mnMaxY = imLeft.rows;
    }
}

AssignFeaturesToGrid()

/*****************************************************************
*      函数属性:类Frame的成员函数AssignFeaturesToGrid()
 *     函数功能:
 *                 将整张图片分为64×48的网格
 *                 并将每个特征点的id加入到该网格中,即mGrid容器存储的是特征点的id
 *     函数参数介绍:NULL
 *     备注:分配特征点到各个网格,加速特征匹配
 ******************************************************************/
void Frame::AssignFeaturesToGrid()
{
    int nReserve = 0.5f*N/(FRAME_GRID_COLS*FRAME_GRID_ROWS);
    for(unsigned int i=0; i<FRAME_GRID_COLS;i++)
        for (unsigned int j=0; j<FRAME_GRID_ROWS;j++)
            mGrid[i][j].reserve(nReserve);   //给每个网格预留下空间,为什么要预留这些?
    //将特征点分配到这些网格中
    for(int i=0;i<N;i++)
    {
        const cv::KeyPoint &kp = mvKeysUn[i];

        int nGridPosX, nGridPosY;  //存储网格位置,证明第(nGridPosX,nGridPosY)个网格
        if(PosInGrid(kp,nGridPosX,nGridPosY))    //如果第i个特征点位置在第(nGridPosX,nGridPosY)个网格中,就将该特征点的id存入该网格中
            mGrid[nGridPosX][nGridPosY].push_back(i);
    }
}