【SLAM十四讲】ch11 回环检测 词袋法实验 得出相似分数后计算PR曲线
【SLAM十四讲】ch11 回环检测 词袋法实验 得出相似分数后计算PR曲线
本文将从DBow3库的安装,再到高博十四讲的第ch11词袋法代码,最后再将词袋法的得分输入PR曲线代码来得到VPR(visual place recognition,视觉场景识别)实验结果。
————————————————————————————————————————————————
之前很长一段时间都是使用WORD来记录实验,今天心血来潮来试试一边做一边写在CSDN上。毕竟最近硬盘不好,不知道什么时候会不会掉盘。
目前手头上有的实验记录,如果兄弟们感兴趣可以在下边留言,我周末的时候抽空整理发在CSDN上(虽然感觉其他人的博客应该也已经有写了)
未整理的实验记录:
ORB3 v1.0 Ubuntu20.04 部署与测试(VMWare16.04版本,WSL2版本,Docker版本)。
Windwos11上的Docker Desktop部署Ubuntu20.04。
Windwos11上的WSL2部署。
Windows CARLA0.8.2,0.9.4部署与坑。
DBow3库安装
对于DBow3库,高博将其放置在了自己仓库的3rdparty中。
如果想要进行下载有以下几种方式
- 下载高博的库,然后使用git submodule下载子模块
git clone https://github.com/gaoxiang12/slambook2
cd slambook2
git submodule init
git submodule update --init --recursive
然后高博设置的Dow3库就会被吓着到3rdparty中,下载链接来记录于仓库内的.gitmodules中。
submodule要求网络对外部网络连接非常良好,下不动的可以把.gitmodules中的链接替换为git镜像站中的链接。
2.直接使用将.gitmomdules文件中记录的URL给clone下来
git clone https://github.com/rmsalinas/DBow3
下载好DBow3库之后,进行编译与安装,DBow3是一个常规的CMAKE工程,所以使用以下命令安装:
cd DBow3
mkdir build
cd build
cmake ..
make -j8
sudo make install
ch11编译
对于ch11的编译,需要对高博的部分代码进行修改。
在ch11/CMakeLists.txt中,高博将libDBoW3库设置为静态库libDBoW3.a,而当前版本的DBow3仓库默认安装之后给出的库文件是动态库.so
所以:
// 将第14行的
set( DBoW3_LIBS "/usr/local/lib/libDBoW3.a" )
// 修改为
set( DBoW3_LIBS "/usr/local/lib/libDBoW3.so" )
而后正常进行编译即可
# 在ch11目录中
mkdir build
cd build
cmake ..
make -j8
参考:https://blog.csdn.net/CSSDCC/article/details/123387255
ch11 词袋法代码运行
ch11中的代码有三个,feature_training.cpp loop_closure.cpp 以及 gen_vocab_large.cpp
feature_training.cpp
feature_training.cpp用于训练词袋法中的词典,训练图像越多效果越好,高博这里只使用了十张图像作为示例。
// feature_training.cpp
int main( int argc, char** argv ) {
// read the image
cout<<"reading images... "<<endl;
vector<Mat> images;
for ( int i=0; i<10; i++ )
{
string path = "./data/"+to_string(i+1)+".png";
images.push_back( imread(path) );
}
// detect ORB features
cout<<"detecting ORB features ... "<<endl;
Ptr< Feature2D > detector = ORB::create();
vector<Mat> descriptors;
for ( Mat& image:images )
{
vector<KeyPoint> keypoints;
Mat descriptor;
detector->detectAndCompute( image, Mat(), keypoints, descriptor );
descriptors.push_back( descriptor );
}
// create vocabulary
cout<<"creating vocabulary ... "<<endl;
DBoW3::Vocabulary vocab;
vocab.create( descriptors );
cout<<"vocabulary info: "<<vocab<<endl;
vocab.save( "vocabulary.yml.gz" );
cout<<"done"<<endl;
return 0;
}
该程序输入为data目录下的所有图像。
输出为vocabulary.yml.gz字典文件
loop_closure.cpp用于计算图像间的相似度,最终给出图像两两间的相似度评分,横向对比机器学习就是给出个分类评分,有这个东西之后PR曲线就好画了。
// loop_closure.cpp
int main(int argc, char **argv) {
// read the images and database
cout << "reading database" << endl;
DBoW3::Vocabulary vocab("./vocabulary.yml.gz");
// DBoW3::Vocabulary vocab("./vocab_larger.yml.gz"); // use large vocab if you want:
if (vocab.empty()) {
cerr << "Vocabulary does not exist." << endl;
return 1;
}
cout << "reading images... " << endl;
vector<Mat> images;
for (int i = 0; i < 10; i++) {
string path = "./data/" + to_string(i + 1) + ".png";
images.push_back(imread(path));
}
// NOTE: in this case we are comparing images with a vocabulary generated by themselves, this may lead to overfit.
// detect ORB features
cout << "detecting ORB features ... " << endl;
Ptr<Feature2D> detector = ORB::create();
vector<Mat> descriptors;
for (Mat &image:images) {
vector<KeyPoint> keypoints;
Mat descriptor;
detector->detectAndCompute(image, Mat(), keypoints, descriptor);
descriptors.push_back(descriptor);
}
// we can compare the images directly or we can compare one image to a database
// images :
cout << "comparing images with images " << endl;
for (int i = 0; i < images.size(); i++) {
DBoW3::BowVector v1;
vocab.transform(descriptors[i], v1);
for (int j = i; j < images.size(); j++) {
DBoW3::BowVector v2;
vocab.transform(descriptors[j], v2);
double score = vocab.score(v1, v2);
cout << "image " << i << " vs image " << j << " : " << score << endl;
}
cout << endl;
}
// or with database
cout << "comparing images with database " << endl;
DBoW3::Database db(vocab, false, 0);
for (int i = 0; i < descriptors.size(); i++)
db.add(descriptors[i]);
cout << "database info: " << db << endl;
for (int i = 0; i < descriptors.size(); i++) {
DBoW3::QueryResults ret;
db.query(descriptors[i], ret, 4); // max result=4
cout << "searching for image " << i << " returns " << ret << endl << endl;
}
cout << "done." << endl;
}
输入词典文件vocabulary.yml.gz,待评分图像(这里设置为data下的所有图像),
最终输入待评分图像间的两两评分,或者是与数据库中图像的评分排行(前四(本质前3,因为有个是自己的)
gen_vocab_large.cpp这个程序用来训练更大的字典,整体结构等同于feature_training。
// gen_vocab_large.cpp
int main( int argc, char** argv )
{
string dataset_dir = argv[1];
ifstream fin ( dataset_dir+"/associate.txt" );
if ( !fin )
{
cout<<"please generate the associate file called associate.txt!"<<endl;
return 1;
}
vector<string> rgb_files, depth_files;
vector<double> rgb_times, depth_times;
while ( !fin.eof() )
{
string rgb_time, rgb_file, depth_time, depth_file;
fin>>rgb_time>>rgb_file>>depth_time>>depth_file;
rgb_times.push_back ( atof ( rgb_time.c_str() ) );
depth_times.push_back ( atof ( depth_time.c_str() ) );
rgb_files.push_back ( dataset_dir+"/"+rgb_file );
depth_files.push_back ( dataset_dir+"/"+depth_file );
if ( fin.good() == false )
break;
}
fin.close();
cout<<"generating features ... "<<endl;
vector<Mat> descriptors;
Ptr< Feature2D > detector = ORB::create();
int index = 1;
for ( string rgb_file:rgb_files )
{
Mat image = imread(rgb_file);
vector<KeyPoint> keypoints;
Mat descriptor;
detector->detectAndCompute( image, Mat(), keypoints, descriptor );
descriptors.push_back( descriptor );
cout<<"extracting features from image " << index++ <<endl;
}
cout<<"extract total "<<descriptors.size()*500<<" features."<<endl;
// create vocabulary
cout<<"creating vocabulary, please wait ... "<<endl;
DBoW3::Vocabulary vocab;
vocab.create( descriptors );
cout<<"vocabulary info: "<<vocab<<endl;
vocab.save( "vocab_larger.yml.gz" );
cout<<"done"<<endl;
return 0;
}
PR曲线代码
# 导入必要的库
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import precision_recall_curve, auc
# 生成一些随机数据
y_true = np.random.randint(0, 2, size=100) # 真实的正负标签
y_score = np.random.rand(100) # 分类评分
# 手动导入数据
'''
y_true = np.array(
[1,0,0,0,0,0,0,0,0,1],
)
y_score = np.array(
[1,
0.0305829,
0.0221928,
0.0308756,
0.0231492,
0.0240249,
0.0240589,
0.0246117,
0.0287788,
0.0542239
]
)
'''
# 计算PR曲线的坐标点
precision, recall, thresholds = precision_recall_curve(y_true, y_score)
# 计算PR曲线下的面积(AUC)
pr_auc = auc(recall, precision)
# 绘制PR曲线
plt.plot(recall, precision, label=f'PR Curve (AUC={pr_auc:.2f})')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.legend()
plt.show()
鸽了,待更新(23-6-10,9:45)