这次是Frame类,Frame类应该可以说是SLAM系统中处理的一个基本单元,它将一副(或双目)图像包装成一个类,给他增加基本的信息,如这幅图的位姿、编号、特征点、对应的参考帧等重要信息,还包含一些设置参数、获取参数的方法。主要在吴博代码注释上进一步加上自己的理解。
Frame类
Frame.h
#ifndef FRAME_H
#define FRAME_H
#include
#include "MapPoint.h"
#include "Thirdparty/DBoW2/DBoW2/BowVector.h"
#include "Thirdparty/DBoW2/DBoW2/FeatureVector.h"
#include "ORBVocabulary.h"
#include "KeyFrame.h"
#include "ORBextractor.h"
#include
namespace ORB_SLAM2
{
#define FRAME_GRID_ROWS 48
#define FRAME_GRID_COLS 64
class MapPoint;
class KeyFrame;
class Frame
{
public:
Frame();
// Copy constructor.
Frame(const Frame &frame);
// Constructor for stereo cameras.
Frame(const cv::Mat &imLeft, const cv::Mat &imRight, const double &timeStamp, ORBextractor* extractorLeft, ORBextractor* extractorRight, ORBVocabulary* voc, cv::Mat &K, cv::Mat &distCoef, const float &bf, const float &thDepth);
// Constructor for RGB-D cameras.
Frame(const cv::Mat &imGray, const cv::Mat &imDepth, const double &timeStamp, ORBextractor* extractor,ORBVocabulary* voc, cv::Mat &K, cv::Mat &distCoef, const float &bf, const float &thDepth);
// Constructor for Monocular cameras.
Frame(const cv::Mat &imGray, const double &timeStamp, ORBextractor* extractor,ORBVocabulary* voc, cv::Mat &K, cv::Mat &distCoef, const float &bf, const float &thDepth);
// Extract ORB on the image. 0 for left image and 1 for right image.
// 提取的关键点存放在mvKeys和mDescriptors中
// ORB是直接调orbExtractor提取的
void ExtractORB(int flag, const cv::Mat &im);
// Compute Bag of Words representation.
// 存放在mBowVec中
void ComputeBoW();
// Set the camera pose.
// 用Tcw更新mTcw
void SetPose(cv::Mat Tcw);
// Computes rotation, translation and camera center matrices from the camera pose.
void UpdatePoseMatrices();
// Returns the camera center.
inline cv::Mat GetCameraCenter()
{
return mOw.clone();
}
// Returns inverse of rotation
inline cv::Mat GetRotationInverse()
{
return mRwc.clone();
}
// Check if a MapPoint is in the frustum of the camera
// and fill variables of the MapPoint to be used by the tracking
// 判断路标点是否在视野中
bool isInFrustum(MapPoint* pMP, float viewingCosLimit);
// Compute the cell of a keypoint (return false if outside the grid)
bool PosInGrid(const cv::KeyPoint &kp, int &posX, int &posY);
vector GetFeaturesInArea(const float &x, const float &y, const float &r, const int minLevel=-1, const int maxLevel=-1) const;
// Search a match for each keypoint in the left image to a keypoint in the right image.
// If there is a match, depth is computed and the right coordinate associated to the left keypoint is stored.
void ComputeStereoMatches();
// Associate a "right" coordinate to a keypoint if there is valid depth in the depthmap.
void ComputeStereoFromRGBD(const cv::Mat &imDepth);
// Backprojects a keypoint (if stereo/depth info available) into 3D world coordinates.
cv::Mat UnprojectStereo(const int &i);
public:
// Vocabulary used for relocalization.
ORBVocabulary* mpORBvocabulary;
// Feature extractor. The right is used only in the stereo case.
ORBextractor* mpORBextractorLeft, *mpORBextractorRight;
// Frame timestamp.
double mTimeStamp;
// Calibration matrix and OpenCV distortion parameters.
cv::Mat mK;
static float fx;
static float fy;
static float cx;
static float cy;
static float invfx;
static float invfy;
cv::Mat mDistCoef;
// Stereo baseline multiplied by fx.
float mbf;
// Stereo baseline in meters.
float mb;
// Threshold close/far points. Close points are inserted from 1 view.
// Far points are inserted as in the monocular case from 2 views.
float mThDepth;
// Number of KeyPoints.
int N; ///< KeyPoints数量
// Vector of keypoints (original for visualization) and undistorted (actually used by the system).
// In the stereo case, mvKeysUn is redundant as images must be rectified.
// In the RGB-D case, RGB images can be distorted.
// mvKeys:原始左图像提取出的特征点(未校正)
// mvKeysRight:原始右图像提取出的特征点(未校正)
// mvKeysUn:校正mvKeys后的特征点,对于双目摄像头,一般得到的图像都是校正好的,再校正一次有点多余
std::vector mvKeys, mvKeysRight;
std::vector mvKeysUn;
// Corresponding stereo coordinate and depth for each keypoint.
// "Monocular" keypoints have a negative value.
// 对于双目,mvuRight存储了左目像素点在右目中的对应点的横坐标
// mvDepth对应的深度
// 单目摄像头,这两个容器中存的都是-1
std::vector mvuRight;
std::vector mvDepth;
// Bag of Words Vector structures.
DBoW2::BowVector mBowVec;
DBoW2::FeatureVector mFeatVec;
// ORB descriptor, each row associated to a keypoint.
// 左目摄像头和右目摄像头特征点对应的描述子
cv::Mat mDescriptors, mDescriptorsRight;
// MapPoints associated to keypoints, NULL pointer if no association.
// 每个特征点对应的MapPoint
std::vector mvpMapPoints;
// Flag to identify outlier associations.
// 观测不到Map中的3D点
std::vector mvbOutlier;
// Keypoints are assigned to cells in a grid to reduce matching complexity when projecting MapPoints.
// 坐标乘以mfGridElementWidthInv和mfGridElementHeightInv就可以确定在哪个格子
static float mfGridElementWidthInv;
static float mfGridElementHeightInv;
// 每个格子分配的特征点数,将图像分成格子,保证提取的特征点比较均匀
// FRAME_GRID_ROWS 48
// FRAME_GRID_COLS 64
std::vector mGrid[FRAME_GRID_COLS][FRAME_GRID_ROWS];
// Camera pose.
cv::Mat mTcw; ///< 相机姿态 世界坐标系到相机坐标坐标系的变换矩阵
// Current and Next Frame id.
static long unsigned int nNextId; ///< Next Frame id.
long unsigned int mnId; ///< Current Frame id.
// Reference Keyframe.
KeyFrame* mpReferenceKF;//指针,指向参考关键帧
// Scale pyramid info.
int mnScaleLevels;//图像提金字塔的层数
float mfScaleFactor;//图像提金字塔的尺度因子
float mfLogScaleFactor;//
vector mvScaleFactors;
vector mvInvScaleFactors;
vector mvLevelSigma2;
vector mvInvLevelSigma2;
// Undistorted Image Bounds (computed once).
// 用于确定画格子时的边界
static float mnMinX;
static float mnMaxX;
static float mnMinY;
static float mnMaxY;
static bool mbInitialComputations;
private:
// Undistort keypoints given OpenCV distortion parameters.
// Only for the RGB-D case. Stereo must be already rectified!
// (called in the constructor).
void UndistortKeyPoints();
// Computes image bounds for the undistorted image (called in the constructor).
void ComputeImageBounds(const cv::Mat &imLeft);
// Assign keypoints to the grid for speed up feature matching (called in the constructor).
void AssignFeaturesToGrid();
// Rotation, translation and camera center
cv::Mat mRcw; ///< Rotation from world to camera
cv::Mat mtcw; ///< Translation from world to camera
cv::Mat mRwc; ///< Rotation from camera to world
cv::Mat mOw; ///< mtwc,Translation from camera to world
};
}// namespace ORB_SLAM
#endif // FRAME_H
Frame.cpp
/**
* This file is part of ORB-SLAM2.
*
* Copyright (C) 2014-2016 Raúl Mur-Artal (University of Zaragoza)
* For more information see
*
* ORB-SLAM2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ORB-SLAM2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ORB-SLAM2. If not, see .
*/
#include "Frame.h"
#include "Converter.h"
#include "ORBmatcher.h"
#include
namespace ORB_SLAM2
{
long unsigned int Frame::nNextId=0;
bool Frame::mbInitialComputations=true;
float Frame::cx, Frame::cy, Frame::fx, Frame::fy, Frame::invfx, Frame::invfy;
float Frame::mnMinX, Frame::mnMinY, Frame::mnMaxX, Frame::mnMaxY;
float Frame::mfGridElementWidthInv, Frame::mfGridElementHeightInv;
Frame::Frame()
{}
/**
* @brief Copy constructor
*
* 复制构造函数, mLastFrame = Frame(mCurrentFrame)
*/
Frame::Frame(const Frame &frame)
:mpORBvocabulary(frame.mpORBvocabulary), mpORBextractorLeft(frame.mpORBextractorLeft), mpORBextractorRight(frame.mpORBextractorRight),
mTimeStamp(frame.mTimeStamp), mK(frame.mK.clone()), mDistCoef(frame.mDistCoef.clone()),
mbf(frame.mbf), mb(frame.mb), mThDepth(frame.mThDepth), N(frame.N), mvKeys(frame.mvKeys),
mvKeysRight(frame.mvKeysRight), mvKeysUn(frame.mvKeysUn), mvuRight(frame.mvuRight),
mvDepth(frame.mvDepth), mBowVec(frame.mBowVec), mFeatVec(frame.mFeatVec),
mDescriptors(frame.mDescriptors.clone()), mDescriptorsRight(frame.mDescriptorsRight.clone()),
mvpMapPoints(frame.mvpMapPoints), mvbOutlier(frame.mvbOutlier), mnId(frame.mnId),
mpReferenceKF(frame.mpReferenceKF), mnScaleLevels(frame.mnScaleLevels),
mfScaleFactor(frame.mfScaleFactor), mfLogScaleFactor(frame.mfLogScaleFactor),
mvScaleFactors(frame.mvScaleFactors), mvInvScaleFactors(frame.mvInvScaleFactors),
mvLevelSigma2(frame.mvLevelSigma2), mvInvLevelSigma2(frame.mvInvLevelSigma2)
{
for(int i=0;i(NULL))
{
// Frame ID
mnId=nNextId++;
// Scale Level Info
mnScaleLevels = mpORBextractorLeft->GetLevels();
mfScaleFactor = mpORBextractorLeft->GetScaleFactor();
mfLogScaleFactor = log(mfScaleFactor);
mvScaleFactors = mpORBextractorLeft->GetScaleFactors();
mvInvScaleFactors = mpORBextractorLeft->GetInverseScaleFactors();
mvLevelSigma2 = mpORBextractorLeft->GetScaleSigmaSquares();
mvInvLevelSigma2 = mpORBextractorLeft->GetInverseScaleSigmaSquares();
// ORB extraction
// 同时对左右目提特征
thread threadLeft(&Frame::ExtractORB,this,0,imLeft);
thread threadRight(&Frame::ExtractORB,this,1,imRight);
threadLeft.join();
threadRight.join();
N = mvKeys.size();
if(mvKeys.empty())
return;
// Undistort特征点,这里没有对双目进行校正,因为要求输入的图像已经进行极线校正
UndistortKeyPoints();
// 计算双目间的匹配, 匹配成功的特征点会计算其深度
// 深度存放在mvuRight 和 mvDepth 中
ComputeStereoMatches();
// 对应的mappoints
mvpMapPoints = vector(N,static_cast(NULL));
mvbOutlier = vector(N,false);
// This is done only for the first Frame (or after a change in the calibration)
if(mbInitialComputations)
{
ComputeImageBounds(imLeft);
mfGridElementWidthInv=static_cast(FRAME_GRID_COLS)/(mnMaxX-mnMinX);
mfGridElementHeightInv=static_cast(FRAME_GRID_ROWS)/(mnMaxY-mnMinY);
fx = K.at(0,0);
fy = K.at(1,1);
cx = K.at(0,2);
cy = K.at(1,2);
invfx = 1.0f/fx;
invfy = 1.0f/fy;
mbInitialComputations=false;
}
mb = mbf/fx;
AssignFeaturesToGrid();
}
// RGBD初始化
Frame::Frame(const cv::Mat &imGray, const cv::Mat &imDepth, 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
mnId=nNextId++;
// Scale Level Info
mnScaleLevels = mpORBextractorLeft->GetLevels();
mfScaleFactor = mpORBextractorLeft->GetScaleFactor();
mfLogScaleFactor = log(mfScaleFactor);
mvScaleFactors = mpORBextractorLeft->GetScaleFactors();
mvInvScaleFactors = mpORBextractorLeft->GetInverseScaleFactors();
mvLevelSigma2 = mpORBextractorLeft->GetScaleSigmaSquares();
mvInvLevelSigma2 = mpORBextractorLeft->GetInverseScaleSigmaSquares();
// ORB extraction
ExtractORB(0,imGray);
N = mvKeys.size();
if(mvKeys.empty())
return;
UndistortKeyPoints();
ComputeStereoFromRGBD(imDepth);
mvpMapPoints = vector(N,static_cast(NULL));
mvbOutlier = vector(N,false);
// This is done only for the first Frame (or after a change in the calibration)
if(mbInitialComputations)
{
ComputeImageBounds(imGray);
mfGridElementWidthInv=static_cast(FRAME_GRID_COLS)/static_cast(mnMaxX-mnMinX);
mfGridElementHeightInv=static_cast(FRAME_GRID_ROWS)/static_cast(mnMaxY-mnMinY);
fx = K.at(0,0);
fy = K.at(1,1);
cx = K.at(0,2);
cy = K.at(1,2);
invfx = 1.0f/fx;
invfy = 1.0f/fy;
mbInitialComputations=false;
}
mb = mbf/fx;
AssignFeaturesToGrid();
}
// 单目初始化
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
mnId=nNextId++;
// Scale Level Info
mnScaleLevels = mpORBextractorLeft->GetLevels();
mfScaleFactor = mpORBextractorLeft->GetScaleFactor();
mfLogScaleFactor = log(mfScaleFactor);
mvScaleFactors = mpORBextractorLeft->GetScaleFactors();
mvInvScaleFactors = mpORBextractorLeft->GetInverseScaleFactors();
mvLevelSigma2 = mpORBextractorLeft->GetScaleSigmaSquares();
mvInvLevelSigma2 = mpORBextractorLeft->GetInverseScaleSigmaSquares();
// ORB extraction
ExtractORB(0,imGray);
N = mvKeys.size();
if(mvKeys.empty())
return;
// 调用OpenCV的矫正函数矫正orb提取的特征点
UndistortKeyPoints();
// Set no stereo information
mvuRight = vector(N,-1);
mvDepth = vector(N,-1);
mvpMapPoints = vector(N,static_cast(NULL));
mvbOutlier = vector(N,false);
// This is done only for the first Frame (or after a change in the calibration)
if(mbInitialComputations)
{
ComputeImageBounds(imGray);
mfGridElementWidthInv=static_cast(FRAME_GRID_COLS)/static_cast(mnMaxX-mnMinX);
mfGridElementHeightInv=static_cast(FRAME_GRID_ROWS)/static_cast(mnMaxY-mnMinY);
fx = K.at(0,0);
fy = K.at(1,1);
cx = K.at(0,2);
cy = K.at(1,2);
invfx = 1.0f/fx;
invfy = 1.0f/fy;
mbInitialComputations=false;
}
mb = mbf/fx;
AssignFeaturesToGrid();
}
void Frame::AssignFeaturesToGrid()
{
int nReserve = 0.5f*N/(FRAME_GRID_COLS*FRAME_GRID_ROWS);
for(unsigned int i=0; imbTrackInView = false;
// 3D in absolute coordinates
cv::Mat P = pMP->GetWorldPos();
// 3D in camera coordinates
// 3D点P在相机坐标系下的坐标
const cv::Mat Pc = mRcw*P+mtcw; // 这里的Rt是经过初步的优化后的
const float &PcX = Pc.at(0);
const float &PcY = Pc.at(1);
const float &PcZ = Pc.at(2);
// Check positive depth
if(PcZ<0.0f)
return false;
// Project in image and check it is not outside
// V-D 1) 将MapPoint投影到当前帧, 并判断是否在图像内
const float invz = 1.0f/PcZ;
const float u=fx*PcX*invz+cx;
const float v=fy*PcY*invz+cy;
if(umnMaxX)
return false;
if(vmnMaxY)
return false;
// Check distance is in the scale invariance region of the MapPoint
// V-D 3) 计算MapPoint到相机中心的距离, 并判断是否在尺度变化的距离内
const float maxDistance = pMP->GetMaxDistanceInvariance();
const float minDistance = pMP->GetMinDistanceInvariance();
// 世界坐标系下,相机到3D点P的向量, 向量方向由相机指向3D点P
const cv::Mat PO = P-mOw;
const float dist = cv::norm(PO);
if(distmaxDistance)
return false;
// Check viewing angle
// V-D 2) 计算当前视角和平均视角夹角的余弦值, 若小于cos(60), 即夹角大于60度则返回
cv::Mat Pn = pMP->GetNormal();
const float viewCos = PO.dot(Pn)/dist;
if(viewCosPredictScale(dist,this);
// Data used by the tracking
// 标记该点将来要被投影
pMP->mbTrackInView = true;
pMP->mTrackProjX = u;
pMP->mTrackProjXR = u - mbf*invz; //该3D点投影到双目右侧相机上的横坐标
pMP->mTrackProjY = v;
pMP->mnTrackScaleLevel = nPredictedLevel;
pMP->mTrackViewCos = viewCos;
return true;
}
/**
* @brief 找到在 以x,y为中心,边长为2r的方形内且在[minLevel, maxLevel]的特征点
* @param x 图像坐标u
* @param y 图像坐标v
* @param r 边长
* @param minLevel 最小尺度
* @param maxLevel 最大尺度
* @return 满足条件的特征点的序号
*/
vector Frame::GetFeaturesInArea(const float &x, const float &y, const float &r, const int minLevel, const int maxLevel) const
{
vector vIndices;
vIndices.reserve(N);
const int nMinCellX = max(0,(int)floor((x-mnMinX-r)*mfGridElementWidthInv));
if(nMinCellX>=FRAME_GRID_COLS)
return vIndices;
const int nMaxCellX = min((int)FRAME_GRID_COLS-1,(int)ceil((x-mnMinX+r)*mfGridElementWidthInv));
if(nMaxCellX<0)
return vIndices;
const int nMinCellY = max(0,(int)floor((y-mnMinY-r)*mfGridElementHeightInv));
if(nMinCellY>=FRAME_GRID_ROWS)
return vIndices;
const int nMaxCellY = min((int)FRAME_GRID_ROWS-1,(int)ceil((y-mnMinY+r)*mfGridElementHeightInv));
if(nMaxCellY<0)
return vIndices;
const bool bCheckLevels = (minLevel>0) || (maxLevel>=0);
for(int ix = nMinCellX; ix<=nMaxCellX; ix++)
{
for(int iy = nMinCellY; iy<=nMaxCellY; iy++)
{
const vector vCell = mGrid[ix][iy];
if(vCell.empty())
continue;
for(size_t j=0, jend=vCell.size(); j=0)
if(kpUn.octave>maxLevel)
continue;
}
const float distx = kpUn.pt.x-x;
const float disty = kpUn.pt.y-y;
if(fabs(distx)=FRAME_GRID_COLS || posY<0 || posY>=FRAME_GRID_ROWS)
return false;
return true;
}
/**
* @brief Bag of Words Representation
*
* 计算词包mBowVec和mFeatVec,其中mFeatVec记录了属于第i个node(在第4层)的ni个描述子
* @see CreateInitialMapMonocular() TrackReferenceKeyFrame() Relocalization()
*/
void Frame::ComputeBoW()
{
if(mBowVec.empty())
{
vector vCurrentDesc = Converter::toDescriptorVector(mDescriptors);
mpORBvocabulary->transform(vCurrentDesc,mBowVec,mFeatVec,4);
}
}
// 调用OpenCV的矫正函数矫正orb提取的特征点
void Frame::UndistortKeyPoints()
{
// 如果没有图像是矫正过的,没有失真
if(mDistCoef.at(0)==0.0)
{
mvKeysUn=mvKeys;
return;
}
// Fill matrix with points
// N为提取的特征点数量,将N个特征点保存在N*2的mat中
cv::Mat mat(N,2,CV_32F);
for(int i=0; i(i,0)=mvKeys[i].pt.x;
mat.at(i,1)=mvKeys[i].pt.y;
}
// Undistort points
// 调整mat的通道为2,矩阵的行列形状不变
mat=mat.reshape(2);
cv::undistortPoints(mat,mat,mK,mDistCoef,cv::Mat(),mK); // 用cv的函数进行失真校正
mat=mat.reshape(1);
// Fill undistorted keypoint vector
// 存储校正后的特征点
mvKeysUn.resize(N);
for(int i=0; i(i,0);
kp.pt.y=mat.at(i,1);
mvKeysUn[i]=kp;
}
}
void Frame::ComputeImageBounds(const cv::Mat &imLeft)
{
if(mDistCoef.at(0)!=0.0)
{
// 矫正前四个边界点:(0,0) (cols,0) (0,rows) (cols,rows)
cv::Mat mat(4,2,CV_32F);
mat.at(0,0)=0.0; //左上
mat.at(0,1)=0.0;
mat.at(1,0)=imLeft.cols; //右上
mat.at(1,1)=0.0;
mat.at(2,0)=0.0; //左下
mat.at(2,1)=imLeft.rows;
mat.at(3,0)=imLeft.cols; //右下
mat.at(3,1)=imLeft.rows;
// Undistort corners
mat=mat.reshape(2);
cv::undistortPoints(mat,mat,mK,mDistCoef,cv::Mat(),mK);
mat=mat.reshape(1);
mnMinX = min(mat.at(0,0),mat.at(2,0));//左上和左下横坐标最小的
mnMaxX = max(mat.at(1,0),mat.at(3,0));//右上和右下横坐标最大的
mnMinY = min(mat.at(0,1),mat.at(1,1));//左上和右上纵坐标最小的
mnMaxY = max(mat.at(2,1),mat.at(3,1));//左下和右下纵坐标最小的
}
else
{
mnMinX = 0.0f;
mnMaxX = imLeft.cols;
mnMinY = 0.0f;
mnMaxY = imLeft.rows;
}
}
/**
* @brief 双目匹配
*
* 为左图的每一个特征点在右图中找到匹配点 \n
* 根据基线(有冗余范围)上描述子距离找到匹配, 再进行SAD精确定位 \n
* 最后对所有SAD的值进行排序, 剔除SAD值较大的匹配对,然后利用抛物线拟合得到亚像素精度的匹配 \n
* 匹配成功后会更新 mvuRight(ur) 和 mvDepth(Z)
*/
void Frame::ComputeStereoMatches()
{
mvuRight = vector(N,-1.0f);
mvDepth = vector(N,-1.0f);
const int thOrbDist = (ORBmatcher::TH_HIGH+ORBmatcher::TH_LOW)/2;
const int nRows = mpORBextractorLeft->mvImagePyramid[0].rows;
//Assign keypoints to row table
// 步骤1:建立特征点搜索范围对应表,一个特征点在一个带状区域内搜索匹配特征点
// 匹配搜索的时候,不仅仅是在一条横线上搜索,而是在一条横向搜索带上搜索,简而言之,原本每个特征点的纵坐标为1,这里把特征点体积放大,纵坐标占好几行
// 例如左目图像某个特征点的纵坐标为20,那么在右侧图像上搜索时是在纵坐标为18到22这条带上搜索,搜索带宽度为正负2,搜索带的宽度和特征点所在金字塔层数有关
// 简单来说,如果纵坐标是20,特征点在图像第20行,那么认为18 19 20 21 22行都有这个特征点
// vRowIndices[18]、vRowIndices[19]、vRowIndices[20]、vRowIndices[21]、vRowIndices[22]都有这个特征点编号
vector > vRowIndices(nRows,vector());
for(int i=0; i > vDistIdx;
vDistIdx.reserve(N);
// 步骤2:对左目相机每个特征点,通过描述子在右目带状搜索区域找到匹配点, 再通过SAD做亚像素匹配
// 注意:这里是校正前的mvKeys,而不是校正后的mvKeysUn
// KeyFrame::UnprojectStereo和Frame::UnprojectStereo函数中不一致
// 这里是不是应该对校正后特征点求深度呢?(wubo???)
for(int iL=0; iL &vCandidates = vRowIndices[vL];
if(vCandidates.empty())
continue;
const float minU = uL-maxD; // 最小匹配范围
const float maxU = uL-minD; // 最大匹配范围
if(maxU<0)
continue;
int bestDist = ORBmatcher::TH_HIGH;
size_t bestIdxR = 0;
// 每个特征点描述子占一行,建立一个指针指向iL特征点对应的描述子
const cv::Mat &dL = mDescriptors.row(iL);
// Compare descriptor to right keypoints
// 步骤2.1:遍历右目所有可能的匹配点,找出最佳匹配点(描述子距离最小)
for(size_t iC=0; iClevelL+1)
continue;
const float &uR = kpR.pt.x;
if(uR>=minU && uR<=maxU)
{
const cv::Mat &dR = mDescriptorsRight.row(iR);
const int dist = ORBmatcher::DescriptorDistance(dL,dR);
if(distmvImagePyramid[kpL.octave].rowRange(scaledvL-w,scaledvL+w+1).colRange(scaleduL-w,scaleduL+w+1);
IL.convertTo(IL,CV_32F);
IL = IL - IL.at(w,w) * cv::Mat::ones(IL.rows,IL.cols,CV_32F);//窗口中的每个元素减去正中心的那个元素,简单归一化,减小光照强度影响
int bestDist = INT_MAX;
int bestincR = 0;
const int L = 5;
vector vDists;
vDists.resize(2*L+1); // 11
// 滑动窗口的滑动范围为(-L, L),提前判断滑动窗口滑动过程中是否会越界
const float iniu = scaleduR0+L-w; //这个地方是否应该是scaleduR0-L-w (wubo???)
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(w,w) * cv::Mat::ones(IR.rows,IR.cols,CV_32F);//窗口中的每个元素减去正中心的那个元素,简单归一化,减小光照强度影响
float dist = cv::norm(IL,IR,cv::NORM_L1); // 一范数,计算差的绝对值
if(dist1)
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(bestDist,iL)); // 该特征点SAD匹配最小匹配偏差
}
}
}
// 步骤3:剔除SAD匹配偏差较大的匹配特征点
// 前面SAD匹配只判断滑动窗口中是否有局部最小值,这里通过对比剔除SAD匹配偏差比较大的特征点的深度
sort(vDistIdx.begin(),vDistIdx.end()); // 根据所有匹配对的SAD偏差进行排序, 距离由小到大
const float median = vDistIdx[vDistIdx.size()/2].first;
const float thDist = 1.5f*1.4f*median; // 计算自适应距离, 大于此距离的匹配对将剔除
for(int i=vDistIdx.size()-1;i>=0;i--)
{
if(vDistIdx[i].first(N,-1);
mvDepth = vector(N,-1);
for(int i=0; i(v,u);
if(d>0)
{
mvDepth[i] = d;
mvuRight[i] = kpU.pt.x-mbf/d;
}
}
}
/**
* @brief Backprojects a keypoint (if stereo/depth info available) into 3D world coordinates.
* @param i 第i个keypoint
* @return 3D点(相对于世界坐标系)
*/
cv::Mat Frame::UnprojectStereo(const int &i)
{
// KeyFrame::UnprojectStereo
// mvDepth是在ComputeStereoMatches函数中求取的
// mvDepth对应的校正前的特征点,可这里却是对校正后特征点反投影
// KeyFrame::UnprojectStereo中是对校正前的特征点mvKeys反投影
// 在ComputeStereoMatches函数中应该对校正后的特征点求深度?? (wubo???)
const float z = mvDepth[i];
if(z>0)
{
const float u = mvKeysUn[i].pt.x;
const float v = mvKeysUn[i].pt.y;
const float x = (u-cx)*z*invfx;
const float y = (v-cy)*z*invfy;
cv::Mat x3Dc = (cv::Mat_(3,1) << x, y, z);
return mRwc*x3Dc+mOw;
}
else
return cv::Mat();
}
} //namespace ORB_SLAM
参考:
https://blog.csdn.net/u012936940/article/details/81139647