Skip to content

Commit 21a1191

Browse files
authored
Make it work without vocabulary files (#636)
1 parent 42636fd commit 21a1191

File tree

13 files changed

+378
-186
lines changed

13 files changed

+378
-186
lines changed

src/stella_vslam/data/keyframe.cc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,9 @@ std::shared_ptr<keyframe> keyframe::from_stmt(sqlite3_stmt* stmt,
240240
// Construct frame_observation
241241
frame_observation frm_obs{descriptors, undist_keypts, bearings, stereo_x_right, depths};
242242
// Compute BoW
243-
data::bow_vocabulary_util::compute_bow(bow_vocab, descriptors, bow_vec, bow_feat_vec);
243+
if (bow_vocab) {
244+
data::bow_vocabulary_util::compute_bow(bow_vocab, descriptors, bow_vec, bow_feat_vec);
245+
}
244246
// NOTE: 3D marker info will be filled in later based on loaded markers
245247
auto keyfrm = data::keyframe::make_keyframe(
246248
id + next_keyframe_id, timestamp, pose_cw, camera, orb_params,
@@ -660,7 +662,9 @@ void keyframe::prepare_for_erasing(map_database* map_db, bow_database* bow_db) {
660662
// 4. remove myself from the databased
661663

662664
map_db->erase_keyframe(shared_from_this());
663-
bow_db->erase_keyframe(shared_from_this());
665+
if (bow_db) {
666+
bow_db->erase_keyframe(shared_from_this());
667+
}
664668
}
665669

666670
bool keyframe::will_be_erased() {

src/stella_vslam/data/map_database.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,9 @@ void map_database::register_keyframe(camera_database* cam_db, orb_params_databas
395395
// Construct frame_observation
396396
frame_observation frm_obs{descriptors, undist_keypts, bearings, stereo_x_right, depths};
397397
// Compute BoW
398-
data::bow_vocabulary_util::compute_bow(bow_vocab, descriptors, bow_vec, bow_feat_vec);
398+
if (bow_vocab) {
399+
data::bow_vocabulary_util::compute_bow(bow_vocab, descriptors, bow_vec, bow_feat_vec);
400+
}
399401
auto keyfrm = data::keyframe::make_keyframe(
400402
id, timestamp, pose_cw, camera, orb_params,
401403
frm_obs, bow_vec, bow_feat_vec);

src/stella_vslam/io/map_database_io_sqlite3.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ bool map_database_io_sqlite3::load(const std::string& path,
5353
data::bow_database* bow_db,
5454
data::bow_vocabulary* bow_vocab) {
5555
std::lock_guard<std::mutex> lock(data::map_database::mtx_database_);
56-
assert(cam_db && map_db && bow_db && bow_vocab);
56+
assert(cam_db && map_db);
5757

5858
// Open database
5959
sqlite3* db = nullptr;
@@ -69,7 +69,7 @@ bool map_database_io_sqlite3::load(const std::string& path,
6969
ok = ok && load_stats(db, map_db);
7070

7171
// update bow database
72-
if (ok) {
72+
if (ok && bow_db) {
7373
const auto keyfrms = map_db->get_all_keyframes();
7474
for (const auto& keyfrm : keyfrms) {
7575
bow_db->add_keyframe(keyfrm);

src/stella_vslam/mapping_module.cc

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ void mapping_module::run() {
110110
// create and extend the map with the new keyframe
111111
mapping_with_new_keyframe();
112112
// send the new keyframe to the global optimization module
113-
if (!cur_keyfrm_->graph_node_->is_spanning_root()) {
113+
if (global_optimizer_ && !cur_keyfrm_->graph_node_->is_spanning_root()) {
114114
global_optimizer_->queue_keyframe(cur_keyfrm_);
115115
}
116116
}
@@ -247,7 +247,7 @@ void mapping_module::mapping_with_new_keyframe() {
247247

248248
void mapping_module::store_new_keyframe() {
249249
// compute BoW feature vector
250-
if (!cur_keyfrm_->bow_is_available()) {
250+
if (bow_vocab_ && !cur_keyfrm_->bow_is_available()) {
251251
cur_keyfrm_->compute_bow(bow_vocab_);
252252
}
253253

@@ -277,6 +277,7 @@ void mapping_module::create_new_landmarks(std::atomic<bool>& abort_create_new_la
277277
// in order to triangulate landmarks between `cur_keyfrm_` and each of the covisibilities
278278
const auto cur_covisibilities = cur_keyfrm_->graph_node_->get_top_n_covisibilities(num_covisibilities_for_landmark_generation_);
279279

280+
match::bow_tree bow_tree_matcher(0.95, false);
280281
match::robust robust_matcher(0.95, false);
281282

282283
// camera center of the current keyframe
@@ -327,7 +328,12 @@ void mapping_module::create_new_landmarks(std::atomic<bool>& abort_create_new_la
327328

328329
// vector of matches (idx in the current, idx in the neighbor)
329330
std::vector<std::pair<unsigned int, unsigned int>> matches;
330-
robust_matcher.match_for_triangulation(cur_keyfrm_, ngh_keyfrm, E_ngh_to_cur, matches, residual_rad_thr_);
331+
if (bow_db_ && bow_vocab_) {
332+
bow_tree_matcher.match_for_triangulation(cur_keyfrm_, ngh_keyfrm, E_ngh_to_cur, matches, residual_rad_thr_);
333+
}
334+
else {
335+
robust_matcher.match_for_triangulation(cur_keyfrm_, ngh_keyfrm, E_ngh_to_cur, matches, residual_rad_thr_);
336+
}
331337

332338
// triangulation
333339
triangulate_with_two_keyframes(cur_keyfrm_, ngh_keyfrm, matches);

src/stella_vslam/match/base.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef STELLA_VSLAM_MATCH_BASE_H
22
#define STELLA_VSLAM_MATCH_BASE_H
33

4+
#include "stella_vslam/type.h"
5+
46
#include <array>
57
#include <algorithm>
68
#include <numeric>
@@ -62,6 +64,20 @@ inline unsigned int compute_descriptor_distance_64(const cv::Mat& desc_1, const
6264
return dist;
6365
}
6466

67+
inline bool check_epipolar_constraint(const Vec3_t& bearing_1, const Vec3_t& bearing_2,
68+
const Mat33_t& E_12, float residual_rad_thr,
69+
const float bearing_1_scale_factor) {
70+
// Normal vector of the epipolar plane on keyframe 1
71+
const Vec3_t epiplane_in_1 = E_12 * bearing_2;
72+
73+
// Acquire the angle formed by the normal vector and the bearing
74+
const auto cos_residual = std::min(1.0, std::max(-1.0, epiplane_in_1.dot(bearing_1) / epiplane_in_1.norm()));
75+
const auto residual_rad = std::abs(M_PI / 2.0 - std::acos(cos_residual));
76+
77+
// The larger keypoint scale permits less constraints
78+
return residual_rad < residual_rad_thr * bearing_1_scale_factor;
79+
}
80+
6581
class base {
6682
public:
6783
base(const float lowe_ratio, const bool check_orientation)

src/stella_vslam/match/bow_tree.cc

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,164 @@
88
namespace stella_vslam {
99
namespace match {
1010

11+
unsigned int bow_tree::match_for_triangulation(const std::shared_ptr<data::keyframe>& keyfrm_1,
12+
const std::shared_ptr<data::keyframe>& keyfrm_2,
13+
const Mat33_t& E_12,
14+
std::vector<std::pair<unsigned int, unsigned int>>& matched_idx_pairs,
15+
const float residual_rad_thr) const {
16+
unsigned int num_matches = 0;
17+
18+
// Project the center of keyframe 1 to keyframe 2
19+
// to acquire the epipole coordinates of the candidate keyframe
20+
const Vec3_t cam_center_1 = keyfrm_1->get_trans_wc();
21+
const Mat33_t rot_2w = keyfrm_2->get_rot_cw();
22+
const Vec3_t trans_2w = keyfrm_2->get_trans_cw();
23+
Vec3_t epiplane_in_keyfrm_2;
24+
const bool valid_epiplane = keyfrm_2->camera_->reproject_to_bearing(rot_2w, trans_2w, cam_center_1, epiplane_in_keyfrm_2);
25+
26+
// Acquire the 3D point information of the keframes
27+
const auto assoc_lms_in_keyfrm_1 = keyfrm_1->get_landmarks();
28+
const auto assoc_lms_in_keyfrm_2 = keyfrm_2->get_landmarks();
29+
30+
// Save the matching information
31+
// Discard the already matched keypoints in keyframe 2
32+
// to acquire a unique association to each keypoint in keyframe 1
33+
std::vector<bool> is_already_matched_in_keyfrm_2(keyfrm_2->frm_obs_.undist_keypts_.size(), false);
34+
// Save the keypoint idx in keyframe 2 which is already associated to the keypoint idx in keyframe 1
35+
std::vector<int> matched_indices_2_in_keyfrm_1(keyfrm_1->frm_obs_.undist_keypts_.size(), -1);
36+
37+
data::bow_feature_vector::const_iterator itr_1 = keyfrm_1->bow_feat_vec_.begin();
38+
data::bow_feature_vector::const_iterator itr_2 = keyfrm_2->bow_feat_vec_.begin();
39+
const data::bow_feature_vector::const_iterator itr_1_end = keyfrm_1->bow_feat_vec_.end();
40+
const data::bow_feature_vector::const_iterator itr_2_end = keyfrm_2->bow_feat_vec_.end();
41+
42+
while (itr_1 != itr_1_end && itr_2 != itr_2_end) {
43+
// Check if the node numbers of BoW tree match
44+
if (itr_1->first == itr_2->first) {
45+
// If the node numbers of BoW tree match,
46+
// Check in practice if matches exist between keyframes
47+
const auto& keyfrm_1_indices = itr_1->second;
48+
const auto& keyfrm_2_indices = itr_2->second;
49+
50+
for (const auto idx_1 : keyfrm_1_indices) {
51+
const auto& lm_1 = assoc_lms_in_keyfrm_1.at(idx_1);
52+
// Ignore if the keypoint of keyframe is associated any 3D points
53+
if (lm_1) {
54+
continue;
55+
}
56+
57+
// Check if it's a stereo keypoint or not
58+
const bool is_stereo_keypt_1 = !keyfrm_1->frm_obs_.stereo_x_right_.empty() && 0 <= keyfrm_1->frm_obs_.stereo_x_right_.at(idx_1);
59+
60+
// Acquire the keypoints and ORB feature vectors
61+
const auto& keypt_1 = keyfrm_1->frm_obs_.undist_keypts_.at(idx_1);
62+
const Vec3_t& bearing_1 = keyfrm_1->frm_obs_.bearings_.at(idx_1);
63+
const auto& desc_1 = keyfrm_1->frm_obs_.descriptors_.row(idx_1);
64+
65+
// Find a keypoint in keyframe 2 that has the minimum hamming distance
66+
unsigned int best_hamm_dist = HAMMING_DIST_THR_LOW;
67+
int best_idx_2 = -1;
68+
unsigned int second_best_hamm_dist = MAX_HAMMING_DIST;
69+
70+
for (const auto idx_2 : keyfrm_2_indices) {
71+
// Ignore if the keypoint is associated any 3D points
72+
// (because this function is used for triangulation)
73+
const auto& lm_2 = assoc_lms_in_keyfrm_2.at(idx_2);
74+
if (lm_2) {
75+
continue;
76+
}
77+
78+
// Ignore if matches are already aquired
79+
if (is_already_matched_in_keyfrm_2.at(idx_2)) {
80+
continue;
81+
}
82+
83+
if (check_orientation_ && std::abs(util::angle::diff(keypt_1.angle, keyfrm_2->frm_obs_.undist_keypts_.at(idx_2).angle)) > 30.0) {
84+
continue;
85+
}
86+
87+
// Check if it's a stereo keypoint or not
88+
const bool is_stereo_keypt_2 = !keyfrm_2->frm_obs_.stereo_x_right_.empty() && 0 <= keyfrm_2->frm_obs_.stereo_x_right_.at(idx_2);
89+
90+
// Acquire the keypoints and ORB feature vectors
91+
const Vec3_t& bearing_2 = keyfrm_2->frm_obs_.bearings_.at(idx_2);
92+
const auto& desc_2 = keyfrm_2->frm_obs_.descriptors_.row(idx_2);
93+
94+
// Compute the distance
95+
const auto hamm_dist = compute_descriptor_distance_32(desc_1, desc_2);
96+
97+
if (HAMMING_DIST_THR_LOW < hamm_dist || best_hamm_dist < hamm_dist) {
98+
continue;
99+
}
100+
101+
if (valid_epiplane && !is_stereo_keypt_1 && !is_stereo_keypt_2) {
102+
// Do not use any keypoints near the epipole if both are not stereo keypoints
103+
const auto cos_dist = epiplane_in_keyfrm_2.dot(bearing_2);
104+
// The threshold of the minimum angle formed by the epipole and the bearing vector is 3.0 degree
105+
constexpr double cos_dist_thr = 0.99862953475;
106+
107+
// Do not allow to match if the formed angle is narrower that the threshold value
108+
if (cos_dist_thr < cos_dist) {
109+
continue;
110+
}
111+
}
112+
113+
// Check consistency in Matrix E
114+
const bool is_inlier = check_epipolar_constraint(bearing_1, bearing_2, E_12,
115+
keyfrm_1->orb_params_->scale_factors_.at(keypt_1.octave),
116+
residual_rad_thr);
117+
if (is_inlier) {
118+
if (hamm_dist < best_hamm_dist) {
119+
second_best_hamm_dist = best_hamm_dist;
120+
best_hamm_dist = hamm_dist;
121+
best_idx_2 = idx_2;
122+
}
123+
else if (hamm_dist < second_best_hamm_dist) {
124+
second_best_hamm_dist = hamm_dist;
125+
}
126+
}
127+
}
128+
129+
if (best_idx_2 < 0) {
130+
continue;
131+
}
132+
133+
// Ratio test
134+
if (lowe_ratio_ * second_best_hamm_dist < static_cast<float>(best_hamm_dist)) {
135+
continue;
136+
}
137+
138+
is_already_matched_in_keyfrm_2.at(best_idx_2) = true;
139+
matched_indices_2_in_keyfrm_1.at(idx_1) = best_idx_2;
140+
++num_matches;
141+
}
142+
143+
++itr_1;
144+
++itr_2;
145+
}
146+
else if (itr_1->first < itr_2->first) {
147+
// Since the node number of keyframe 1 is smaller, increment the iterator until the node numbers match
148+
itr_1 = keyfrm_1->bow_feat_vec_.lower_bound(itr_2->first);
149+
}
150+
else {
151+
// Since the node number of keyframe 2 is smaller, increment the iterator until the node numbers match
152+
itr_2 = keyfrm_2->bow_feat_vec_.lower_bound(itr_1->first);
153+
}
154+
}
155+
156+
matched_idx_pairs.clear();
157+
matched_idx_pairs.reserve(num_matches);
158+
159+
for (unsigned int idx_1 = 0; idx_1 < matched_indices_2_in_keyfrm_1.size(); ++idx_1) {
160+
if (matched_indices_2_in_keyfrm_1.at(idx_1) < 0) {
161+
continue;
162+
}
163+
matched_idx_pairs.emplace_back(std::make_pair(idx_1, matched_indices_2_in_keyfrm_1.at(idx_1)));
164+
}
165+
166+
return num_matches;
167+
}
168+
11169
unsigned int bow_tree::match_frame_and_keyframe(const std::shared_ptr<data::keyframe>& keyfrm, data::frame& frm, std::vector<std::shared_ptr<data::landmark>>& matched_lms_in_frm) const {
12170
unsigned int num_matches = 0;
13171

src/stella_vslam/match/bow_tree.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,22 @@ class bow_tree final : public base {
2222

2323
~bow_tree() final = default;
2424

25-
//! frameで観測している特徴点とkeyframeで観測している特徴点の対応を求め,それを元にframeの特徴点と3次元点の対応情報を得る
26-
//! matched_lms_in_frmには,frameの各特徴点に対応する(keyframeで観測された)3次元点が格納される
27-
//! NOTE: matched_lms_in_frm.size()はframeの特徴点数と一致
25+
unsigned int match_for_triangulation(const std::shared_ptr<data::keyframe>& keyfrm_1,
26+
const std::shared_ptr<data::keyframe>& keyfrm_2,
27+
const Mat33_t& E_12,
28+
std::vector<std::pair<unsigned int, unsigned int>>& matched_idx_pairs,
29+
const float residual_rad_thr) const;
30+
31+
//! Find the correspondence between the feature points observed in the frame and the feature points observed in the keyframe,
32+
//! and obtain the correspondence between the feature points in the frame and the 3D points.
33+
//! Matched_lms_in_frm stores the 3D points (observed in the keyframe) corresponding to each feature point in the frame.
34+
//! NOTE: matched_lms_in_frm.size() is equal to the number of feature points in the frame
2835
unsigned int match_frame_and_keyframe(const std::shared_ptr<data::keyframe>& keyfrm, data::frame& frm, std::vector<std::shared_ptr<data::landmark>>& matched_lms_in_frm) const;
2936

30-
//! keyframe1で観測している特徴点とkeyframe2で観測している特徴点の対応を求め,それを元にkeyframe1の特徴点と3次元点の対応情報を得る
31-
//! matched_lms_in_keyfrm_1には,keyframe1の各特徴点に対応する(keyframe2で観測された)3次元点が格納される
32-
//! NOTE: matched_lms_in_keyfrm_1.size()はkeyframe1の特徴点数と一致
37+
//! Find the correspondence between the feature point observed in keyframe1 and the feature point observed in keyframe2,
38+
//! and obtain the correspondence between the feature point in keyframe1 and the 3D points.
39+
//! Matched_lms_in_keyfrm_1 stores the 3D points (observed in keyframe2) corresponding to each feature point in keyframe1
40+
//! NOTE: matched_lms_in_keyfrm_1.size() matches the number of feature points in keyframe1
3341
unsigned int match_keyframes(const std::shared_ptr<data::keyframe>& keyfrm_1, const std::shared_ptr<data::keyframe>& keyfrm_2, std::vector<std::shared_ptr<data::landmark>>& matched_lms_in_keyfrm_1) const;
3442
};
3543

0 commit comments

Comments
 (0)