在ORB-SLAM中的特征提取和描述子计算,调用Opencv库函数完成,在脱离Opencv的过程中,遇到的BUG和问题简单的记录一下。
首先定义自己的Mat
和Keypoint
以及在测试中所需要的转换工具。
typedef unsigned char uchar;
typedef vector<vector<uchar>> VecMat;
struct mPoint2f
{
float x;
float y;
};
typedef struct _mKeyPoint
{
mPoint2f pt;
float size;
float angle;
float response;
int octave;
}mKeyPoint;
以上则为Mat
和Keypoint
的替代品,对整个特征提取和描述子计算重写。
Mat Vec2Mat(VecMat data)
{
int row = data.size();
int col = data[0].size();
Mat img(row ,col, CV_8UC1);
uchar *ptmp = NULL;
for (int i = 0; i <row; ++i)
{
ptmp = img.ptr<uchar>(i);
for (int j = 0; j < col; ++j)
{
ptmp[j] = data[i][j];
}
}
return img;
}
VecMat Mat2Vec(Mat img)
{
int row = img.rows;
int col = img.cols;
VecMat data(row,vector<uchar>(col, 0));
for (int i = 0; i < row; ++i)
{
for (int j = 0; j < col; ++j)
{
data[i][j] = img.at<uchar>(i, j);
}
}
return data;
}
vector<KeyPoint> mKey2Key(vector<mKeyPoint> mkeypoints)
{
vector<KeyPoint> keypoints;
for (int i = 0; i < mkeypoints.size(); ++i)
{
float x = mkeypoints[i].pt.x;
float y = mkeypoints[i].pt.y;
float size = mkeypoints[i].size;
float angle = mkeypoints[i].angle;
float response = mkeypoints[i].response;
float octave = mkeypoints[i].octave;
keypoints.push_back(KeyPoint((float)x, (float)y,size, angle, response, (float)octave));
}
return keypoints;
}
vector<mKeyPoint> Key2mKey(vector<KeyPoint> keypoints)
{
vector<mKeyPoint> mKeyPoints;
for (int i = 0; i < keypoints.size(); ++i)
{
mKeyPoint mKeyPoint;
mKeyPoint.pt.x = keypoints[i].pt.x;
mKeyPoint.pt.y = keypoints[i].pt.y;
mKeyPoint.size = keypoints[i].size;
mKeyPoint.angle = keypoints[i].angle;
mKeyPoint.response = keypoints[i].response;
mKeyPoint.octave = keypoints[i].octave;
mKeyPoints.push_back(mKeyPoint);
}
return mKeyPoints;
}
然后定义了转换工具,记录一下!其他代码内容将不在这里详细介绍了,只是根据ORB-SLAM的原理重写了一遍。下面记录以下在重写过程中遇到的BUG以及问题。
在ORB-SLAM3的特征提取中,有几个较为重要的函数,也是重写过程中比较麻烦的几个函数:
FAST() //角点提取
resize() //下采样
GaussianBlur() //高斯滤波
copyMakeBorder() //边界扩充
问题1:
在重写的过程中,使用二维数组vector
替代cv::Mat
,在提取FAST
角点的过程中,使用指针遍历周围邻域,在cv::Mat
中可通过加上其步长step
去访问上下相邻的元素,如下:
//step:矩阵每一行所占据的字节数。
const uchar* ptr = img.ptr<uchar>(i) + 3;
uchar data = ptr(img.step);
在cv::Mat
中,每一行都是紧跟在上一行地址后,因此可通过直接对指针相加,访问相邻行的元素。
但是通过二维vector
替代cv::Mat
时,其包含的一维vector
的数据地址并不是连续的,因为其存放的是一个vector
类,还包含成员函数,所以并不能通过累加指针其访问其元素。
因此需要将其数据放到一个一维vector
中再去遍历。
vector<uchar> ptrimg;
for(int r = 0;r<row;r++){
ptrimg.insert(ptrimg.end(),Vimg[r].begin(),Vimg[r].end());
}
const uchar* ptr = ptrimg.data()+i*col+3;
问题2:
在ORB中定义了描述子模板,需要将其模板转成point
类型的点对,这里的mPoint2f
是自己定义的点类型,通过std::copy
将其拷贝到pattern
是无效的,即注释部分。opencv
里应该是设置了构造函数。
std::vector<mPoint2f> pattern;
const int npoints = 512;
// const mPoint2f* pattern0 = (const mPoint2f*)bit_pattern_31_;
// std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));
for(int i=0;i<256*4;i=i+2){
mPoint2f temp_pattern;
temp_pattern.x = bit_pattern_31_[i];
temp_pattern.y = bit_pattern_31_[i+1];
pattern.push_back(temp_pattern);
}
问题3:在Opencv中容易搞混的还有行列的索引,在通过点对去访问周围元素时,访问的坐标点x
对应的是二维数组的列,即想访问相同位置需要通过image[y][x]
去访问。之前有的地方写反了就会出现越界的问题,当索引改写正确的情况下,是不会出现任何越界,所以也不需要进行越界判断。
其他涉及到ORB算法的问题就不记录啦!