视觉SLAM十四讲中的知识

本文主要记录视觉SLAM十四讲中的相关知识点,此记。

C++知识点

智能指针

 几乎每一个有分量的程序都需要“在相同时间的多处地点处理或使用对象”的能力。为此,我们必须在程序的多个地点指向(refer to)同一对象。虽然C++语言提供引用(reference)和指针(pointer),还是不够,因为我们往往必须确保当“指向对象”的最末一个引用被删除时该对象本身也被删除,毕竟对象被删除时析构函数可以要求某些操作,例如释放内存或归还资源等等。

  所以我们需要“当对象再也不被使用时就被清理”的语义。Class shared_ptr提供了这样的共享式拥有语义。也就是说,多个shared_ptr可以共享(或说拥有)同一对象。对象的最末一个拥有者有责任销毁对象,并清理与该对象相关的所有资源。

  shared_ptr的目标就是,在其所指向的对象不再被使用之后(而非之前),自动释放与对象相关的资源。

使用Mat::ptr模板函数

1
2
3
ushort d = depth_.ptr<ushort>(y)[x];
//这个还是.ptr模板函数定位像素值的方法,记住用法
//返回的是depth_的第y行数据的第x个的深度值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int main()
{
Mat m(400, 400, CV_8UC3, Scalar(226, 46, 166));
imshow("Before", m);

for (int row = 0; row < m.rows; row++)
{
if (row % 5 == 0)
{
// data 是 uchar* 类型的, m.ptr<uchar>(row) 返回第 row 行数据的首地址
// 需要注意的是该行数据是按顺序存放的,也就是对于一个 3 通道的 Mat, 一个像素有
// 有 3 个通道值, [B,G,R][B,G,R][B,G,R]... 所以一行长度为:
// sizeof(uchar) * m.cols * m.channels() 个字节
uchar* data = m.ptr<uchar>(row);
for (int col = 0; col < m.cols; col++)
{
data[col * 3] = 102; //第row行的第col个像素点的第一个通道值 Blue
data[col * 3 + 1] = 217; // Green
data[col * 3 + 2] = 239; // Red
}
}
}

imshow("After", m);
cout << (int)m.at<Vec3b>(0, 0)[0] << ','; //利用 Fn 1 介绍的方法输出一下像素值到控制台
cout << (int)m.at<Vec3b>(0, 0)[1] << ',';
cout << (int)m.at<Vec3b>(0, 0)[2] << endl;

cvWaitKey();
return 0;
}

单件模式(Singleton)

定义:单件模式确保一个类只有一个实例,并提供一个全局访问点。
把Config写成单件模式(Singleton),它只有一个全局对象,当我们设置参数文件时,创建该对象并读取参数,随后就可以在任意地方访问参数值,最后在程序结束时自动销毁。

std::min_element和std::max_element

Returns an iterator pointing to the element with the smallest(largest) value in the range [first,last).
这就是书中使用了std::min_element和lambda表达式的程序,这段程序具体实现的功能是找到matches这个用来存放匹配的关键点的描述子中描述子的最小的距离,然后赋值给min_dis,matches是存放Dmatch这个描述子类的容器,所有最后会有->distance表示赋给min_dis的是距离。

返回容器或者数组中最大值和最小值。max/min_element(first,end,cmp);其中cmp为可选择参数

1
2
3
4
[] ( const cv::DMatch& m1, const cv::DMatch& m2 ) 
{
return m1.distance < m2.distance;
}

这里的lamda函数相当于:

1
2
3
4
bool cmp(const cv::DMatch& m1, const cv::DMatch& m2)
{
return m1.distance < m2.distance;
}

实际的功能就是将matches中的所有描述子进行了个排序,然后min_element取了第一个及最小的那个的distance赋给了min_dis

c++ 11特性:auto关键字

auto可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型,类似的关键字还有decltype。举个例子:
用于代替冗长复杂、变量使用范围专一的变量声明。

想象一下在没有auto的时候,我们操作标准库时经常需要这样:

1
2
3
4
5
6
7
8
9
10
#include<string>
#include<vector>
int main()
{
std::vector<std::string> vs;
for (std::vector<std::string>::iterator i = vs.begin(); i != vs.end(); i++)
{
//...
}
}

这样看代码写代码实在烦得很。有人可能会说为何不直接使用using namespace std,这样代码可以短一点。实际上这不是该建议的方法(C++Primer对此有相关叙述)。使用auto能简化代码:

1
2
3
4
5
6
7
8
9
10
#include<string>
#include<vector>
int main()
{
std::vector<std::string> vs;
for (auto i = vs.begin(); i != vs.end(); i++)
{
//..
}
}

for循环中的i将在编译时自动推导其类型,而不用我们显式去定义那长长的一串。
在定义模板函数时,用于声明依赖模板参数的变量类型。

1
2
3
4
5
6
template <typename _Tx,typename _Ty>
void Multiply(_Tx x, _Ty y)
{
auto v = x*y;
std::cout << v;
}

若不使用auto变量来声明v,那这个函数就难定义啦,不到编译的时候,谁知道x*y的真正类型是什么呢?
模板函数依赖于模板参数的返回值

1
2
3
4
5
template <typename _Tx, typename _Ty>
auto multiply(_Tx x, _Ty y)->decltype(_Tx*_Ty)
{
return x*y;
}

当模板函数的返回值依赖于模板的参数时,我们依旧无法在编译代码前确定模板参数的类型,故也无从知道返回值的类型,这时我们可以使用auto。格式如上所示。

图像知识

OpenCV函数

函数cvRound,cvFloor,cvCeil 都是用一种舍入的方法将输入浮点数转换成整数:

cvRound:返回跟参数最接近的整数值; cvFloor:返回不大于参数的最大整数值;
*cvCeil:返回不小于参数的最小整数值。

opencv软件

opencv2和opencv3共存
opencv2默认安装,安装路径一般是“usr/local”,在安装opencv3时,先下载3的源码,解压后

1
2
mkdir build
cd build

更改安装目录(先在/usr/local下新建文件夹opencv3),

1
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local/opencv3 ..

然后编译opencv3即可,

1
2
make
sudo make install

这样就把3安装在opencv3文件夹了(bin lib share include)

build过程中可能报错, ICV: Failed to download ICV package: ippicv_linux_20151201.tgz.
Status=7;”Couldn’t connect to server”,解决方法是手动下载ippicv_linux_20151201.tgz,并替换掉opencv-3.1.0/3rdparty/ippicv/downloads/linux-*下的相应文件即可,可以直接从该地址下载:ippicv_linux_20151201.tgz

在使用的时候,在CMakelists.txt中,如要用3,在find_package前指明路径:

1
2
set(OpenCV_DIR /usr/local/opencv3/share/OpenCV)
find_package(OpenCV 3 REQUIRED)

ORB特征构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static Ptr<ORB> cv::ORB::create ( int nfeatures = 500, 
float scaleFactor = 1.2f,
int nlevels = 8,
int edgeThreshold = 31,
int firstLevel = 0,
int WTA_K = 2,
int scoreType = ORB::HARRIS_SCORE,
int patchSize = 31,
int fastThreshold = 20 )
nfeatures:需要的特征点总数;
scaleFactor:尺度因子;
nlevels:金字塔层数;
edgeThreshold:边界阈值;
firstLevel:起始层;
WTA_K:描述子形成方法,WTA_K=2表示,采用两两比较;
scoreType:角点响应函数,可以选择Harris或者Fast的方法;
patchSize:特征点邻域大小;
如果我的文章对你有所帮助,那么不妨?