37 #include "SiftGPU/SiftGPU.h"
48 void PrintElapsedTime(
const Timer& timer) {
52 void IndexImagesInVisualIndex(
const int num_threads,
const int num_checks,
53 const int max_num_features,
54 const std::vector<image_t>& image_ids,
55 Thread* thread, FeatureMatcherCache* cache,
56 retrieval::VisualIndex<>* visual_index) {
57 retrieval::VisualIndex<>::IndexOptions index_options;
58 index_options.num_threads = num_threads;
59 index_options.num_checks = num_checks;
61 for (
size_t i = 0; i < image_ids.size(); ++i) {
62 if (thread->IsStopped()) {
69 std::cout <<
StringPrintf(
"Indexing image [%d/%d]", i + 1, image_ids.size())
72 auto keypoints = *cache->GetKeypoints(image_ids[i]);
73 auto descriptors = *cache->GetDescriptors(image_ids[i]);
74 if (max_num_features > 0 &&
descriptors.rows() > max_num_features) {
78 visual_index->Add(index_options, image_ids[i], keypoints,
descriptors);
80 PrintElapsedTime(timer);
84 visual_index->Prepare();
87 void MatchNearestNeighborsInVisualIndex(
88 const int num_threads,
const int num_images,
const int num_neighbors,
89 const int num_checks,
const int num_images_after_verification,
90 const int max_num_features,
const std::vector<image_t>& image_ids,
91 Thread* thread, FeatureMatcherCache* cache,
92 retrieval::VisualIndex<>* visual_index, SiftFeatureMatcher* matcher) {
95 std::vector<retrieval::ImageScore> image_scores;
99 ThreadPool retrieval_thread_pool(num_threads);
100 JobQueue<Retrieval> retrieval_queue(num_threads);
105 retrieval::VisualIndex<>::QueryOptions query_options;
106 query_options.max_num_images = num_images;
107 query_options.num_neighbors = num_neighbors;
108 query_options.num_checks = num_checks;
109 query_options.num_images_after_verification = num_images_after_verification;
110 auto QueryFunc = [&](
const image_t image_id) {
111 auto keypoints = *cache->GetKeypoints(image_id);
112 auto descriptors = *cache->GetDescriptors(image_id);
113 if (max_num_features > 0 &&
descriptors.rows() > max_num_features) {
118 retrieval.image_id = image_id;
119 visual_index->Query(query_options, keypoints,
descriptors,
120 &retrieval.image_scores);
122 CHECK(retrieval_queue.Push(retrieval));
126 size_t image_idx = 0;
127 const size_t init_num_tasks =
128 std::min(image_ids.size(), 2 * retrieval_thread_pool.NumThreads());
129 for (; image_idx < init_num_tasks; ++image_idx) {
130 retrieval_thread_pool.AddTask(QueryFunc, image_ids[image_idx]);
133 std::vector<std::pair<image_t, image_t>> image_pairs;
136 for (
size_t i = 0; i < image_ids.size(); ++i) {
137 if (thread->IsStopped()) {
138 retrieval_queue.Stop();
145 std::cout <<
StringPrintf(
"Matching image [%d/%d]", i + 1, image_ids.size())
149 if (image_idx < image_ids.size()) {
150 retrieval_thread_pool.AddTask(QueryFunc, image_ids[image_idx]);
155 const auto retrieval = retrieval_queue.Pop();
156 CHECK(retrieval.IsValid());
158 const auto& image_id = retrieval.Data().image_id;
159 const auto& image_scores = retrieval.Data().image_scores;
163 image_pairs.reserve(image_scores.size());
164 for (
const auto image_score : image_scores) {
165 image_pairs.emplace_back(image_id, image_score.image_id);
168 matcher->Match(image_pairs);
170 PrintElapsedTime(timer);
218 : cache_size_(cache_size), database_(database) {
219 CHECK_NOTNULL(database_);
222 void FeatureMatcherCache::MaybeLoadCameras() {
223 std::lock_guard<std::mutex> lock(database_mutex_);
224 if (cameras_cache_) {
229 cameras_cache_ = std::make_unique<std::unordered_map<camera_t, Camera>>();
230 cameras_cache_->reserve(cameras.size());
231 for (Camera& camera : cameras) {
232 cameras_cache_->emplace(camera.CameraId(), std::move(camera));
236 void FeatureMatcherCache::MaybeLoadImages() {
237 std::lock_guard<std::mutex> lock(database_mutex_);
243 images_cache_ = std::make_unique<std::unordered_map<image_t, Image>>();
244 images_cache_->reserve(images.size());
245 for (Image&
image : images) {
246 images_cache_->emplace(
image.ImageId(), std::move(
image));
252 std::make_unique<ThreadSafeLRUCache<image_t, FeatureKeypoints>>(
253 cache_size_, [
this](
const image_t image_id) {
254 std::lock_guard<std::mutex> lock(database_mutex_);
255 return std::make_shared<FeatureKeypoints>(
260 std::make_unique<ThreadSafeLRUCache<image_t, FeatureDescriptors>>(
261 cache_size_, [
this](
const image_t image_id) {
262 std::lock_guard<std::mutex> lock(database_mutex_);
263 return std::make_shared<FeatureDescriptors>(
267 keypoints_exists_cache_ = std::make_unique<ThreadSafeLRUCache<image_t, bool>>(
268 cache_size_, [
this](
const image_t image_id) {
269 std::lock_guard<std::mutex> lock(database_mutex_);
273 descriptors_exists_cache_ =
274 std::make_unique<ThreadSafeLRUCache<image_t, bool>>(
275 cache_size_, [
this](
const image_t image_id) {
276 std::lock_guard<std::mutex> lock(database_mutex_);
277 return std::make_shared<bool>(
284 return cameras_cache_->at(camera_id);
289 return images_cache_->at(image_id);
294 return keypoints_cache_->Get(image_id);
299 return descriptors_cache_->Get(image_id);
304 std::unique_lock<std::mutex> lock(database_mutex_);
305 return database_->
ReadMatches(image_id1, image_id2);
311 std::vector<image_t> image_ids;
312 image_ids.reserve(images_cache_->size());
313 for (
const auto&
image : *images_cache_) {
314 image_ids.push_back(
image.first);
321 return *keypoints_exists_cache_->Get(image_id);
325 return *descriptors_exists_cache_->Get(image_id);
330 std::unique_lock<std::mutex> lock(database_mutex_);
336 std::unique_lock<std::mutex> lock(database_mutex_);
343 std::unique_lock<std::mutex> lock(database_mutex_);
350 std::unique_lock<std::mutex> lock(database_mutex_);
356 std::unique_lock<std::mutex> lock(database_mutex_);
362 std::unique_lock<std::mutex> lock(database_mutex_);
368 : options_(options), cache_(cache) {}
379 input_queue_(input_queue),
380 output_queue_(output_queue) {
393 if (input_job.IsValid()) {
394 auto data = input_job.Data();
416 input_queue_(input_queue),
417 output_queue_(output_queue) {
431 std::cerr <<
"ERROR: OpenGL context is null" <<
std::endl;
442 std::cout <<
"WARNING: SiftGPU not fully supported" <<
std::endl;
455 if (input_job.IsValid()) {
456 auto data = input_job.Data();
470 &sift_match_gpu, &
data.matches);
471 }
catch (
const std::exception&
e) {
472 std::cerr <<
"ERROR: GPU matching failed for image pair ("
473 <<
data.image_id1 <<
", " <<
data.image_id2 <<
"): "
476 data.matches.resize(0);
478 std::cerr <<
"ERROR: Unknown exception in GPU matching for image pair ("
480 data.matches.resize(0);
489 const int index,
const image_t image_id,
494 *descriptors_ptr =
nullptr;
506 input_queue_(input_queue),
507 output_queue_(output_queue) {
511 void GuidedSiftCPUFeatureMatcher::Run() {
519 const auto input_job = input_queue_->Pop();
520 if (input_job.IsValid()) {
521 auto data = input_job.Data();
523 if (
data.two_view_geometry.inlier_matches.size() <
525 CHECK(output_queue_->Push(
data));
533 CHECK(output_queue_->Push(
data));
539 const auto descriptors1 =
541 const auto descriptors2 =
544 *descriptors2, &
data.two_view_geometry);
546 CHECK(output_queue_->Push(
data));
555 input_queue_(input_queue),
556 output_queue_(output_queue) {
567 void GuidedSiftGPUFeatureMatcher::Run() {
569 if (!opengl_context_) {
570 std::cerr <<
"ERROR: OpenGL context is null" <<
std::endl;
574 opengl_context_->MakeCurrent();
581 std::cout <<
"WARNING: SiftGPU not fully supported" <<
std::endl;
593 const auto input_job = input_queue_->Pop();
594 if (input_job.IsValid()) {
595 auto data = input_job.Data();
597 if (
data.two_view_geometry.inlier_matches.size() <
599 CHECK(output_queue_->Push(
data));
607 CHECK(output_queue_->Push(
data));
614 GetFeatureData(0,
data.image_id1, &keypoints1_ptr, &descriptors1_ptr);
617 GetFeatureData(1,
data.image_id2, &keypoints2_ptr, &descriptors2_ptr);
620 descriptors1_ptr, descriptors2_ptr,
621 &sift_match_gpu, &
data.two_view_geometry);
622 }
catch (
const std::exception&
e) {
623 std::cerr <<
"ERROR: Guided GPU matching failed for image pair ("
624 <<
data.image_id1 <<
", " <<
data.image_id2 <<
"): "
626 data.two_view_geometry = TwoViewGeometry();
628 std::cerr <<
"ERROR: Unknown exception in guided GPU matching for image pair ("
630 data.two_view_geometry = TwoViewGeometry();
633 CHECK(output_queue_->Push(
data));
638 void GuidedSiftGPUFeatureMatcher::GetFeatureData(
639 const int index,
const image_t image_id,
644 if (prev_uploaded_image_ids_[index] == image_id) {
645 *keypoints_ptr =
nullptr;
646 *descriptors_ptr =
nullptr;
650 *keypoints_ptr = &prev_uploaded_keypoints_[index];
651 *descriptors_ptr = &prev_uploaded_descriptors_[index];
652 prev_uploaded_image_ids_[index] = image_id;
661 input_queue_(input_queue),
662 output_queue_(output_queue) {
684 if (input_job.IsValid()) {
685 auto data = input_job.Data();
703 data.two_view_geometry.EstimateMultiple(camera1, points1, camera2,
704 points2,
data.matches,
707 data.two_view_geometry.Estimate(camera1, points1, camera2, points2,
711 }
catch (
const std::exception&
e) {
712 std::cerr <<
"ERROR: TwoViewGeometryVerifier failed for image pair ("
713 <<
data.image_id1 <<
", " <<
data.image_id2 <<
"): "
727 : options_(options), database_(database), cache_(cache), is_setup_(false) {
728 CHECK(options_.
Check());
731 CHECK_GT(num_threads, 0);
733 std::vector<int> gpu_indices = CSVToVector<int>(options_.
gpu_index);
734 CHECK_GT(gpu_indices.size(), 0);
737 if (options_.
use_gpu && gpu_indices.size() == 1 && gpu_indices[0] == -1) {
739 if (num_cuda_devices <= 0) {
740 std::cerr <<
"WARNING: No CUDA devices available, falling back to CPU matching" <<
std::endl;
743 gpu_indices.push_back(-1);
745 gpu_indices.resize(num_cuda_devices);
746 std::iota(gpu_indices.begin(), gpu_indices.end(), 0);
752 auto gpu_options = options_;
753 matchers_.reserve(gpu_indices.size());
754 for (
const auto& gpu_index : gpu_indices) {
757 gpu_options, cache, &matcher_queue_, &verifier_queue_));
760 matchers_.reserve(num_threads);
761 for (
int i = 0; i < num_threads; ++i) {
763 options_, cache, &matcher_queue_, &verifier_queue_));
767 verifiers_.reserve(num_threads);
769 for (
int i = 0; i < num_threads; ++i) {
771 options_, cache, &verifier_queue_, &guided_matcher_queue_));
775 auto gpu_options = options_;
776 guided_matchers_.reserve(gpu_indices.size());
777 for (
const auto& gpu_index : gpu_indices) {
780 gpu_options, cache, &guided_matcher_queue_, &output_queue_));
783 guided_matchers_.reserve(num_threads);
784 for (
int i = 0; i < num_threads; ++i) {
786 options_, cache, &guided_matcher_queue_, &output_queue_));
790 for (
int i = 0; i < num_threads; ++i) {
792 options_, cache, &verifier_queue_, &output_queue_));
798 matcher_queue_.Wait();
799 verifier_queue_.Wait();
800 guided_matcher_queue_.Wait();
801 output_queue_.Wait();
803 for (
size_t i = 0; i < matchers_.size(); ++i) {
804 matchers_[i]->Stop();
807 for (
size_t i = 0; i < verifiers_.size(); ++i) {
808 verifiers_[i]->Stop();
811 for (
size_t i = 0; i < guided_matchers_.size(); ++i) {
812 guided_matchers_[i]->Stop();
815 matcher_queue_.Stop();
816 verifier_queue_.Stop();
817 guided_matcher_queue_.Stop();
818 output_queue_.Stop();
820 for (
size_t i = 0; i < matchers_.size(); ++i) {
821 matchers_[i]->Wait();
824 for (
size_t i = 0; i < verifiers_.size(); ++i) {
825 verifiers_[i]->Wait();
828 for (
size_t i = 0; i < guided_matchers_.size(); ++i) {
829 guided_matchers_[i]->Wait();
834 const int max_num_features = CHECK_NOTNULL(database_)->MaxNumDescriptors();
838 for (
auto& matcher : matchers_) {
843 for (
auto& verifier : verifiers_) {
847 for (
auto& guided_matcher : guided_matchers_) {
849 guided_matcher->Start();
852 for (
auto& matcher : matchers_) {
853 if (!matcher->CheckValidSetup()) {
858 for (
auto& guided_matcher : guided_matchers_) {
859 if (!guided_matcher->CheckValidSetup()) {
870 const std::vector<std::pair<image_t, image_t>>& image_pairs) {
871 CHECK_NOTNULL(database_);
872 CHECK_NOTNULL(cache_);
875 if (image_pairs.empty()) {
883 std::unordered_set<image_pair_t> image_pair_ids;
884 image_pair_ids.reserve(image_pairs.size());
886 size_t num_outputs = 0;
887 for (
const auto& image_pair : image_pairs) {
889 if (image_pair.first == image_pair.second) {
896 if (image_pair_ids.count(pair_id) > 0) {
900 image_pair_ids.insert(pair_id);
902 const bool exists_matches =
904 const bool exists_inlier_matches =
907 if (exists_matches && exists_inlier_matches) {
918 if (exists_inlier_matches) {
923 data.image_id1 = image_pair.first;
924 data.image_id2 = image_pair.second;
926 if (exists_matches) {
927 data.matches = cache_->
GetMatches(image_pair.first, image_pair.second);
929 CHECK(verifier_queue_.Push(
data));
931 CHECK(matcher_queue_.Push(
data));
939 for (
size_t i = 0; i < num_outputs; ++i) {
940 const auto output_job = output_queue_.Pop();
941 CHECK(output_job.IsValid());
942 auto output = output_job.Data();
944 if (output.matches.size() <
static_cast<size_t>(options_.
min_num_inliers)) {
948 if (output.two_view_geometry.inlier_matches.size() <
953 cache_->
WriteMatches(output.image_id1, output.image_id2, output.matches);
955 output.two_view_geometry);
958 CHECK_EQ(output_queue_.Size(), 0);
965 match_options_(match_options) {
966 CHECK(options_.
Check());
967 CHECK(match_options_.
Check());
969 database_ = std::make_shared<Database>(database_path);
970 cache_ = std::make_shared<FeatureMatcherCache>(5 * options_.
block_size, database_.get());
971 matcher_ = std::make_shared<SiftFeatureMatcher>(match_options, database_.get(), cache_.get());
974 void ExhaustiveFeatureMatcher::Run() {
977 if (!matcher_->Setup()) {
983 const std::vector<image_t> image_ids = cache_->GetImageIds();
985 const size_t block_size =
static_cast<size_t>(options_.
block_size);
986 const size_t num_blocks =
static_cast<size_t>(
987 std::ceil(
static_cast<double>(image_ids.size()) / block_size));
988 const size_t num_pairs_per_block = block_size * (block_size - 1) / 2;
990 std::vector<std::pair<image_t, image_t>> image_pairs;
991 image_pairs.reserve(num_pairs_per_block);
993 for (
size_t start_idx1 = 0; start_idx1 < image_ids.size();
994 start_idx1 += block_size) {
995 const size_t end_idx1 =
996 std::min(image_ids.size(), start_idx1 + block_size) - 1;
997 for (
size_t start_idx2 = 0; start_idx2 < image_ids.size();
998 start_idx2 += block_size) {
999 const size_t end_idx2 =
1000 std::min(image_ids.size(), start_idx2 + block_size) - 1;
1010 std::cout <<
StringPrintf(
"Matching block [%d/%d, %d/%d]",
1011 start_idx1 / block_size + 1, num_blocks,
1012 start_idx2 / block_size + 1, num_blocks)
1015 image_pairs.clear();
1016 for (
size_t idx1 = start_idx1; idx1 <= end_idx1; ++idx1) {
1017 for (
size_t idx2 = start_idx2; idx2 <= end_idx2; ++idx2) {
1018 const size_t block_id1 = idx1 % block_size;
1019 const size_t block_id2 = idx2 % block_size;
1020 if ((idx1 > idx2 && block_id1 <= block_id2) ||
1022 block_id1 < block_id2)) {
1023 image_pairs.emplace_back(image_ids[idx1], image_ids[idx2]);
1028 DatabaseTransaction database_transaction(database_.get());
1029 matcher_->Match(image_pairs);
1031 PrintElapsedTime(timer);
1041 : options_(options),
1042 match_options_(match_options),
1043 database_(database_path),
1045 5 * options_.overlap), &database_)),
1046 matcher_(match_options, &database_, cache_.get()) {
1047 CHECK(options_.
Check());
1048 CHECK(match_options_.
Check());
1051 void SequentialFeatureMatcher::Run() {
1054 if (!matcher_.
Setup()) {
1060 const std::vector<image_t> ordered_image_ids = GetOrderedImageIds();
1062 RunSequentialMatching(ordered_image_ids);
1064 RunLoopDetection(ordered_image_ids);
1070 std::vector<image_t> SequentialFeatureMatcher::GetOrderedImageIds()
const {
1071 const std::vector<image_t> image_ids = cache_->GetImageIds();
1073 std::vector<Image> ordered_images;
1074 ordered_images.reserve(image_ids.size());
1075 for (
const auto image_id : image_ids) {
1076 ordered_images.push_back(cache_->GetImage(image_id));
1079 std::sort(ordered_images.begin(), ordered_images.end(),
1080 [](
const Image& image1,
const Image& image2) {
1081 return image1.Name() < image2.Name();
1084 std::vector<image_t> ordered_image_ids;
1085 ordered_image_ids.reserve(image_ids.size());
1086 for (
const auto&
image : ordered_images) {
1087 ordered_image_ids.push_back(
image.ImageId());
1090 return ordered_image_ids;
1093 void SequentialFeatureMatcher::RunSequentialMatching(
1094 const std::vector<image_t>& image_ids) {
1095 std::vector<std::pair<image_t, image_t>> image_pairs;
1096 image_pairs.reserve(options_.
overlap);
1098 for (
size_t image_idx1 = 0; image_idx1 < image_ids.size(); ++image_idx1) {
1103 const auto image_id1 = image_ids.at(image_idx1);
1108 std::cout <<
StringPrintf(
"Matching image [%d/%d]", image_idx1 + 1,
1112 image_pairs.clear();
1113 for (
int i = 0; i < options_.
overlap; ++i) {
1114 const size_t image_idx2 = image_idx1 + i;
1115 if (image_idx2 < image_ids.size()) {
1116 image_pairs.emplace_back(image_id1, image_ids.at(image_idx2));
1118 const size_t image_idx2_quadratic = image_idx1 + (1 << i);
1119 if (image_idx2_quadratic < image_ids.size()) {
1120 image_pairs.emplace_back(image_id1,
1121 image_ids.at(image_idx2_quadratic));
1129 DatabaseTransaction database_transaction(&database_);
1130 matcher_.
Match(image_pairs);
1132 PrintElapsedTime(timer);
1136 void SequentialFeatureMatcher::RunLoopDetection(
1137 const std::vector<image_t>& image_ids) {
1140 std::string vocab_tree_path =
1142 retrieval::VisualIndex<> visual_index;
1143 visual_index.Read(vocab_tree_path);
1146 IndexImagesInVisualIndex(match_options_.
num_threads,
1149 this, cache_.get(), &visual_index);
1156 std::vector<image_t> match_image_ids;
1157 for (
size_t i = 0; i < image_ids.size();
1159 match_image_ids.push_back(image_ids[i]);
1162 MatchNearestNeighborsInVisualIndex(
1168 &visual_index, &matcher_);
1174 : options_(options),
1175 match_options_(match_options),
1176 database_(database_path),
1177 cache_(5 * options_.num_images, &database_),
1178 matcher_(match_options, &database_, &cache_) {
1179 CHECK(options_.
Check());
1180 CHECK(match_options_.
Check());
1183 void VocabTreeFeatureMatcher::Run() {
1186 if (!matcher_.
Setup()) {
1194 std::string vocab_tree_path =
1196 retrieval::VisualIndex<> visual_index;
1197 visual_index.Read(vocab_tree_path);
1199 const std::vector<image_t> all_image_ids = cache_.
GetImageIds();
1200 std::vector<image_t> image_ids;
1205 std::unordered_map<std::string, image_t> image_name_to_image_id;
1206 image_name_to_image_id.reserve(all_image_ids.size());
1207 for (
const auto image_id : all_image_ids) {
1209 image_name_to_image_id.emplace(
image.Name(), image_id);
1216 while (std::getline(file, line)) {
1219 if (line.empty() || line[0] ==
'#') {
1223 if (image_name_to_image_id.count(line) == 0) {
1224 std::cerr <<
"ERROR: Image " << line <<
" does not exist." <<
std::endl;
1226 image_ids.push_back(image_name_to_image_id.at(line));
1234 &cache_, &visual_index);
1242 MatchNearestNeighborsInVisualIndex(
1246 image_ids,
this, &cache_, &visual_index, &matcher_);
1254 : options_(options),
1255 match_options_(match_options),
1256 database_(database_path),
1257 cache_(5 * options_.max_num_neighbors, &database_),
1258 matcher_(match_options, &database_, &cache_) {
1259 CHECK(options_.
Check());
1260 CHECK(match_options_.
Check());
1263 void SpatialFeatureMatcher::Run() {
1266 if (!matcher_.
Setup()) {
1272 const std::vector<image_t> image_ids = cache_.
GetImageIds();
1281 std::cout <<
"Indexing images..." << std::flush;
1283 GPSTransform gps_transform;
1285 size_t num_locations = 0;
1286 Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor> location_matrix(
1287 image_ids.size(), 3);
1289 std::vector<size_t> location_idxs;
1290 location_idxs.reserve(image_ids.size());
1292 std::vector<Eigen::Vector3d> ells(1);
1294 for (
size_t i = 0; i < image_ids.size(); ++i) {
1295 const auto image_id = image_ids[i];
1298 if ((
image.TvecPrior(0) == 0 &&
image.TvecPrior(1) == 0 &&
1300 (
image.TvecPrior(0) == 0 &&
image.TvecPrior(1) == 0 &&
1305 location_idxs.push_back(i);
1308 ells[0](0) =
image.TvecPrior(0);
1309 ells[0](1) =
image.TvecPrior(1);
1312 const auto xyzs = gps_transform.EllToXYZ(ells);
1314 location_matrix(num_locations, 0) =
static_cast<float>(xyzs[0](0));
1315 location_matrix(num_locations, 1) =
static_cast<float>(xyzs[0](1));
1316 location_matrix(num_locations, 2) =
static_cast<float>(xyzs[0](2));
1318 location_matrix(num_locations, 0) =
1319 static_cast<float>(
image.TvecPrior(0));
1320 location_matrix(num_locations, 1) =
1321 static_cast<float>(
image.TvecPrior(1));
1322 location_matrix(num_locations, 2) =
1323 static_cast<float>(options_.
ignore_z ? 0 :
image.TvecPrior(2));
1329 PrintElapsedTime(timer);
1331 if (num_locations == 0) {
1332 std::cout <<
" => No images with location data." <<
std::endl;
1343 std::cout <<
"Building search index..." << std::flush;
1345 flann::Matrix<float> locations(location_matrix.data(), num_locations,
1346 location_matrix.cols());
1348 flann::LinearIndexParams index_params;
1349 flann::LinearIndex<flann::L2<float>> search_index(index_params);
1350 search_index.buildIndex(locations);
1352 PrintElapsedTime(timer);
1360 std::cout <<
"Searching for nearest neighbors..." << std::flush;
1364 Eigen::Matrix<size_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
1365 index_matrix(num_locations, knn);
1366 flann::Matrix<size_t> indices(index_matrix.data(), num_locations, knn);
1368 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
1369 distance_matrix(num_locations, knn);
1370 flann::Matrix<float> distances(distance_matrix.data(), num_locations, knn);
1372 flann::SearchParams search_params(flann::FLANN_CHECKS_AUTOTUNED);
1374 search_params.cores = std::thread::hardware_concurrency();
1378 if (search_params.cores <= 0) {
1379 search_params.cores = 1;
1382 search_index.knnSearch(locations, indices, distances, knn, search_params);
1384 PrintElapsedTime(timer);
1390 const float max_distance =
1393 std::vector<std::pair<image_t, image_t>> image_pairs;
1394 image_pairs.reserve(knn);
1396 for (
size_t i = 0; i < num_locations; ++i) {
1404 std::cout <<
StringPrintf(
"Matching image [%d/%d]", i + 1, num_locations)
1407 image_pairs.clear();
1409 for (
int j = 0; j < knn; ++j) {
1411 if (index_matrix(i, j) == i) {
1416 if (distance_matrix(i, j) > max_distance) {
1420 const size_t idx = location_idxs[i];
1421 const image_t image_id = image_ids.at(idx);
1422 const size_t nn_idx = location_idxs.at(index_matrix(i, j));
1423 const image_t nn_image_id = image_ids.at(nn_idx);
1424 image_pairs.emplace_back(image_id, nn_image_id);
1427 DatabaseTransaction database_transaction(&database_);
1428 matcher_.
Match(image_pairs);
1430 PrintElapsedTime(timer);
1439 : options_(options),
1440 match_options_(match_options),
1441 database_(database_path),
1442 cache_(options_.batch_size, &database_),
1443 matcher_(match_options, &database_, &cache_) {
1444 CHECK(options_.
Check());
1445 CHECK(match_options_.
Check());
1448 void TransitiveFeatureMatcher::Run() {
1451 if (!matcher_.
Setup()) {
1457 const std::vector<image_t> image_ids = cache_.
GetImageIds();
1459 std::vector<std::pair<image_t, image_t>> image_pairs;
1460 std::unordered_set<image_pair_t> image_pair_ids;
1462 for (
int iteration = 0; iteration < options_.
num_iterations; ++iteration) {
1471 std::cout <<
StringPrintf(
"Iteration [%d/%d]", iteration + 1,
1475 std::vector<std::pair<image_t, image_t>> existing_image_pairs;
1476 std::vector<int> existing_num_inliers;
1478 &existing_num_inliers);
1480 CHECK_EQ(existing_image_pairs.size(), existing_num_inliers.size());
1482 std::unordered_map<image_t, std::vector<image_t>> adjacency;
1483 for (
const auto& image_pair : existing_image_pairs) {
1484 adjacency[image_pair.first].push_back(image_pair.second);
1485 adjacency[image_pair.second].push_back(image_pair.first);
1488 const size_t batch_size =
static_cast<size_t>(options_.
batch_size);
1490 size_t num_batches = 0;
1491 image_pairs.clear();
1492 image_pair_ids.clear();
1493 for (
const auto&
image : adjacency) {
1494 const auto image_id1 =
image.first;
1495 for (
const auto& image_id2 :
image.second) {
1496 if (adjacency.count(image_id2) > 0) {
1497 for (
const auto& image_id3 : adjacency.at(image_id2)) {
1498 const auto image_pair_id =
1500 if (image_pair_ids.count(image_pair_id) == 0) {
1501 image_pairs.emplace_back(image_id1, image_id3);
1502 image_pair_ids.insert(image_pair_id);
1503 if (image_pairs.size() >= batch_size) {
1507 DatabaseTransaction database_transaction(&database_);
1508 matcher_.
Match(image_pairs);
1509 image_pairs.clear();
1510 PrintElapsedTime(timer);
1525 std::cout <<
StringPrintf(
" Batch %d", num_batches) << std::flush;
1526 DatabaseTransaction database_transaction(&database_);
1527 matcher_.
Match(image_pairs);
1528 PrintElapsedTime(timer);
1537 : options_(options),
1538 match_options_(match_options),
1539 database_(database_path),
1540 cache_(options.block_size, &database_),
1541 matcher_(match_options, &database_, &cache_) {
1542 CHECK(options_.
Check());
1543 CHECK(match_options_.
Check());
1546 void ImagePairsFeatureMatcher::Run() {
1549 if (!matcher_.
Setup()) {
1559 std::unordered_map<std::string, image_t> image_name_to_image_id;
1560 image_name_to_image_id.reserve(cache_.
GetImageIds().size());
1561 for (
const auto image_id : cache_.
GetImageIds()) {
1563 image_name_to_image_id.emplace(
image.Name(), image_id);
1570 std::vector<std::pair<image_t, image_t>> image_pairs;
1571 std::unordered_set<colmap::image_pair_t> image_pairs_set;
1572 while (std::getline(file, line)) {
1575 if (line.empty() || line[0] ==
'#') {
1579 std::stringstream line_stream(line);
1581 std::string image_name1;
1582 std::string image_name2;
1584 std::getline(line_stream, image_name1,
' ');
1586 std::getline(line_stream, image_name2,
' ');
1589 if (image_name_to_image_id.count(image_name1) == 0) {
1590 std::cerr <<
"ERROR: Image " << image_name1 <<
" does not exist."
1594 if (image_name_to_image_id.count(image_name2) == 0) {
1595 std::cerr <<
"ERROR: Image " << image_name2 <<
" does not exist."
1600 const image_t image_id1 = image_name_to_image_id.at(image_name1);
1601 const image_t image_id2 = image_name_to_image_id.at(image_name2);
1604 const bool image_pair_exists = image_pairs_set.insert(image_pair).second;
1605 if (image_pair_exists) {
1606 image_pairs.emplace_back(image_id1, image_id2);
1614 const size_t num_match_blocks = image_pairs.size() / options_.
block_size + 1;
1615 std::vector<std::pair<image_t, image_t>> block_image_pairs;
1616 block_image_pairs.reserve(options_.
block_size);
1618 for (
size_t i = 0; i < image_pairs.size(); i += options_.
block_size) {
1628 i / options_.
block_size + 1, num_match_blocks)
1631 const size_t block_end = i + options_.
block_size <= image_pairs.size()
1633 : image_pairs.size();
1634 std::vector<std::pair<image_t, image_t>> block_image_pairs;
1635 block_image_pairs.reserve(options_.
block_size);
1636 for (
size_t j = i; j < block_end; ++j) {
1637 block_image_pairs.push_back(image_pairs[j]);
1640 DatabaseTransaction database_transaction(&database_);
1641 matcher_.
Match(block_image_pairs);
1643 PrintElapsedTime(timer);
1652 : options_(options),
1653 match_options_(match_options),
1654 database_(database_path),
1655 cache_(kCacheSize, &database_) {
1656 CHECK(options_.
Check());
1657 CHECK(match_options_.
Check());
1660 void FeaturePairsFeatureMatcher::Run() {
1665 std::unordered_map<std::string, const Image*> image_name_to_image;
1666 image_name_to_image.reserve(cache_.
GetImageIds().size());
1667 for (
const auto image_id : cache_.
GetImageIds()) {
1669 image_name_to_image.emplace(
image.Name(), &
image);
1676 while (std::getline(file, line)) {
1687 std::istringstream line_stream(line);
1689 std::string image_name1, image_name2;
1691 line_stream >> image_name1 >> image_name2;
1693 std::cerr <<
"ERROR: Could not read image pair." <<
std::endl;
1697 std::cout <<
StringPrintf(
"%s - %s", image_name1.c_str(),
1698 image_name2.c_str())
1701 if (image_name_to_image.count(image_name1) == 0) {
1702 std::cout <<
StringPrintf(
"SKIP: Image %s not found in database.",
1703 image_name1.c_str())
1707 if (image_name_to_image.count(image_name2) == 0) {
1708 std::cout <<
StringPrintf(
"SKIP: Image %s not found in database.",
1709 image_name2.c_str())
1714 const Image& image1 = *image_name_to_image[image_name1];
1715 const Image& image2 = *image_name_to_image[image_name2];
1717 bool skip_pair =
false;
1719 std::cout <<
"SKIP: Matches for image pair already exist in database."
1725 while (std::getline(file, line)) {
1732 std::istringstream line_stream(line);
1736 line_stream >> match.point2D_idx1 >> match.point2D_idx2;
1738 std::cerr <<
"ERROR: Cannot read feature matches." <<
std::endl;
1742 matches.push_back(match);
1753 database_.
WriteMatches(image1.ImageId(), image2.ImageId(), matches);
1755 const auto keypoints1 = cache_.
GetKeypoints(image1.ImageId());
1756 const auto keypoints2 = cache_.
GetKeypoints(image2.ImageId());
1758 TwoViewGeometry two_view_geometry;
1759 TwoViewGeometry::Options two_view_geometry_options;
1760 two_view_geometry_options.min_num_inliers =
1762 two_view_geometry_options.ransac_options.max_error =
1764 two_view_geometry_options.ransac_options.confidence =
1766 two_view_geometry_options.ransac_options.min_num_trials =
1768 two_view_geometry_options.ransac_options.max_num_trials =
1770 two_view_geometry_options.ransac_options.min_inlier_ratio =
1773 two_view_geometry.Estimate(
1776 two_view_geometry_options);
1781 TwoViewGeometry two_view_geometry;
1783 if (camera1.HasPriorFocalLength() && camera2.HasPriorFocalLength()) {
1789 two_view_geometry.inlier_matches = matches;
std::shared_ptr< core::Tensor > image
FeatureDescriptors ReadDescriptors(const image_t image_id) const
void ReadTwoViewGeometryNumInliers(std::vector< std::pair< image_t, image_t >> *image_pairs, std::vector< int > *num_inliers) const
FeatureMatches ReadMatches(const image_t image_id1, const image_t image_id2) const
void DeleteInlierMatches(const image_t image_id1, const image_t image_id2) const
void WriteMatches(const image_t image_id1, const image_t image_id2, const FeatureMatches &matches) const
bool ExistsMatches(const image_t image_id1, const image_t image_id2) const
FeatureKeypoints ReadKeypoints(const image_t image_id) const
void WriteTwoViewGeometry(const image_t image_id1, const image_t image_id2, const TwoViewGeometry &two_view_geometry) const
static image_pair_t ImagePairToPairId(const image_t image_id1, const image_t image_id2)
bool ExistsKeypoints(const image_t image_id) const
bool ExistsInlierMatches(const image_t image_id1, const image_t image_id2) const
std::vector< Camera > ReadAllCameras() const
void DeleteMatches(const image_t image_id1, const image_t image_id2) const
bool ExistsDescriptors(const image_t image_id) const
std::vector< Image > ReadAllImages() const
ExhaustiveFeatureMatcher(const ExhaustiveMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
const Image & GetImage(const image_t image_id)
FeatureMatches GetMatches(const image_t image_id1, const image_t image_id2)
FeatureMatcherCache(const size_t cache_size, const Database *database)
void WriteMatches(const image_t image_id1, const image_t image_id2, const FeatureMatches &matches)
bool ExistsInlierMatches(const image_t image_id1, const image_t image_id2)
void DeleteInlierMatches(const image_t image_id1, const image_t image_id2)
void WriteTwoViewGeometry(const image_t image_id1, const image_t image_id2, const TwoViewGeometry &two_view_geometry)
std::vector< image_t > GetImageIds()
const Camera & GetCamera(const camera_t camera_id)
bool ExistsDescriptors(const image_t image_id)
void DeleteMatches(const image_t image_id1, const image_t image_id2)
std::shared_ptr< FeatureKeypoints > GetKeypoints(const image_t image_id)
bool ExistsKeypoints(const image_t image_id)
bool ExistsMatches(const image_t image_id1, const image_t image_id2)
std::shared_ptr< FeatureDescriptors > GetDescriptors(const image_t image_id)
void SetMaxNumMatches(const int max_num_matches)
FeatureMatcherThread(const SiftMatchingOptions &options, FeatureMatcherCache *cache)
SiftMatchingOptions options_
FeatureMatcherCache * cache_
FeaturePairsFeatureMatcher(const FeaturePairsMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
GuidedSiftCPUFeatureMatcher(const SiftMatchingOptions &options, FeatureMatcherCache *cache, JobQueue< Input > *input_queue, JobQueue< Output > *output_queue)
GuidedSiftGPUFeatureMatcher(const SiftMatchingOptions &options, FeatureMatcherCache *cache, JobQueue< Input > *input_queue, JobQueue< Output > *output_queue)
ImagePairsFeatureMatcher(const ImagePairsMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
SequentialFeatureMatcher(const SequentialMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
SiftCPUFeatureMatcher(const SiftMatchingOptions &options, FeatureMatcherCache *cache, JobQueue< Input > *input_queue, JobQueue< Output > *output_queue)
JobQueue< Input > * input_queue_
JobQueue< Output > * output_queue_
SiftFeatureMatcher(const SiftMatchingOptions &options, Database *database, FeatureMatcherCache *cache)
void Match(const std::vector< std::pair< image_t, image_t >> &image_pairs)
void GetDescriptorData(const int index, const image_t image_id, const FeatureDescriptors **descriptors_ptr)
JobQueue< Input > * input_queue_
JobQueue< Output > * output_queue_
std::array< image_t, 2 > prev_uploaded_image_ids_
std::array< FeatureDescriptors, 2 > prev_uploaded_descriptors_
SiftGPUFeatureMatcher(const SiftMatchingOptions &options, FeatureMatcherCache *cache, JobQueue< Input > *input_queue, JobQueue< Output > *output_queue)
std::unique_ptr< OpenGLContextManager > opengl_context_
SpatialFeatureMatcher(const SpatialMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
static const int kMaxNumThreads
const Timer & GetTimer() const
void SignalInvalidSetup()
void PrintMinutes() const
TransitiveFeatureMatcher(const TransitiveMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
JobQueue< Output > * output_queue_
FeatureMatcherCache * cache_
TwoViewGeometryVerifier(const SiftMatchingOptions &options, FeatureMatcherCache *cache, JobQueue< Input > *input_queue, JobQueue< Output > *output_queue)
JobQueue< Input > * input_queue_
TwoViewGeometry::Options two_view_geometry_options_
const SiftMatchingOptions options_
VocabTreeFeatureMatcher(const VocabTreeMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
#define CHECK_OPTION_GT(val1, val2)
QTextStream & endl(QTextStream &stream)
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
void MatchGuidedSiftFeaturesGPU(const SiftMatchingOptions &match_options, const FeatureKeypoints *keypoints1, const FeatureKeypoints *keypoints2, const FeatureDescriptors *descriptors1, const FeatureDescriptors *descriptors2, SiftMatchGPU *sift_match_gpu, TwoViewGeometry *two_view_geometry)
void StringTrim(std::string *str)
void MatchSiftFeaturesGPU(const SiftMatchingOptions &match_options, const FeatureDescriptors *descriptors1, const FeatureDescriptors *descriptors2, SiftMatchGPU *sift_match_gpu, FeatureMatches *matches)
void ExtractTopScaleFeatures(FeatureKeypoints *keypoints, FeatureDescriptors *descriptors, const size_t num_features)
bool CreateSiftGPUMatcher(const SiftMatchingOptions &match_options, SiftMatchGPU *sift_match_gpu)
Eigen::Matrix< uint8_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > FeatureDescriptors
void MatchSiftFeaturesCPU(const SiftMatchingOptions &match_options, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, FeatureMatches *matches)
void MatchGuidedSiftFeaturesCPU(const SiftMatchingOptions &match_options, const FeatureKeypoints &keypoints1, const FeatureKeypoints &keypoints2, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, TwoViewGeometry *two_view_geometry)
std::vector< Eigen::Vector2d > FeatureKeypointsToPointsVector(const FeatureKeypoints &keypoints)
void PrintHeading1(const std::string &heading)
std::vector< FeatureKeypoint > FeatureKeypoints
std::string StringPrintf(const char *format,...)
const image_t kInvalidImageId
std::vector< FeatureMatch > FeatureMatches
int GetEffectiveNumThreads(const int num_threads)
std::filesystem::path MaybeDownloadAndCacheFile(const std::string &uri)
std::string to_string(const T &n)
std::string match_list_path
std::string match_list_path
int loop_detection_num_images
std::string vocab_tree_path
int loop_detection_num_nearest_neighbors
int loop_detection_max_num_features
int loop_detection_num_checks
int loop_detection_num_images_after_verification
int loop_detection_period
RANSACOptions ransac_options
int num_images_after_verification
std::string match_list_path
std::string vocab_tree_path
int num_nearest_neighbors