ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
matching.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
32 #include "feature/matching.h"
33 
34 #include <fstream>
35 #include <numeric>
36 
37 #include "SiftGPU/SiftGPU.h"
38 #include "base/gps.h"
39 #include "feature/utils.h"
40 #include "retrieval/visual_index.h"
41 #include "util/cuda.h"
42 #include "util/download.h"
43 #include "util/misc.h"
44 
45 namespace colmap {
46 namespace {
47 
48 void PrintElapsedTime(const Timer& timer) {
49  std::cout << StringPrintf(" in %.3fs", timer.ElapsedSeconds()) << std::endl;
50 }
51 
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;
60 
61  for (size_t i = 0; i < image_ids.size(); ++i) {
62  if (thread->IsStopped()) {
63  return;
64  }
65 
66  Timer timer;
67  timer.Start();
68 
69  std::cout << StringPrintf("Indexing image [%d/%d]", i + 1, image_ids.size())
70  << std::flush;
71 
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) {
75  ExtractTopScaleFeatures(&keypoints, &descriptors, max_num_features);
76  }
77 
78  visual_index->Add(index_options, image_ids[i], keypoints, descriptors);
79 
80  PrintElapsedTime(timer);
81  }
82 
83  // Compute the TF-IDF weights, etc.
84  visual_index->Prepare();
85 }
86 
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) {
93  struct Retrieval {
94  image_t image_id = kInvalidImageId;
95  std::vector<retrieval::ImageScore> image_scores;
96  };
97 
98  // Create a thread pool to retrieve the nearest neighbors.
99  ThreadPool retrieval_thread_pool(num_threads);
100  JobQueue<Retrieval> retrieval_queue(num_threads);
101 
102  // The retrieval thread kernel function. Note that the descriptors should be
103  // extracted outside of this function sequentially to avoid any concurrent
104  // access to the database causing race conditions.
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) {
114  ExtractTopScaleFeatures(&keypoints, &descriptors, max_num_features);
115  }
116 
117  Retrieval retrieval;
118  retrieval.image_id = image_id;
119  visual_index->Query(query_options, keypoints, descriptors,
120  &retrieval.image_scores);
121 
122  CHECK(retrieval_queue.Push(retrieval));
123  };
124 
125  // Initially, make all retrieval threads busy and continue with the matching.
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]);
131  }
132 
133  std::vector<std::pair<image_t, image_t>> image_pairs;
134 
135  // Pop the finished retrieval results and enqueue them for feature matching.
136  for (size_t i = 0; i < image_ids.size(); ++i) {
137  if (thread->IsStopped()) {
138  retrieval_queue.Stop();
139  return;
140  }
141 
142  Timer timer;
143  timer.Start();
144 
145  std::cout << StringPrintf("Matching image [%d/%d]", i + 1, image_ids.size())
146  << std::flush;
147 
148  // Push the next image to the retrieval queue.
149  if (image_idx < image_ids.size()) {
150  retrieval_thread_pool.AddTask(QueryFunc, image_ids[image_idx]);
151  image_idx += 1;
152  }
153 
154  // Pop the next results from the retrieval queue.
155  const auto retrieval = retrieval_queue.Pop();
156  CHECK(retrieval.IsValid());
157 
158  const auto& image_id = retrieval.Data().image_id;
159  const auto& image_scores = retrieval.Data().image_scores;
160 
161  // Compose the image pairs from the scores.
162  image_pairs.clear();
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);
166  }
167 
168  matcher->Match(image_pairs);
169 
170  PrintElapsedTime(timer);
171  }
172 }
173 
174 } // namespace
175 
178  return true;
179 }
180 
187  return true;
188 }
189 
194  return true;
195 }
196 
200  return true;
201 }
202 
206  return true;
207 }
208 
211  return true;
212 }
213 
214 bool FeaturePairsMatchingOptions::Check() const { return true; }
215 
217  const Database* database)
218  : cache_size_(cache_size), database_(database) {
219  CHECK_NOTNULL(database_);
220 }
221 
222 void FeatureMatcherCache::MaybeLoadCameras() {
223  std::lock_guard<std::mutex> lock(database_mutex_);
224  if (cameras_cache_) {
225  return;
226  }
227 
228  std::vector<Camera> cameras = database_->ReadAllCameras();
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));
233  }
234 }
235 
236 void FeatureMatcherCache::MaybeLoadImages() {
237  std::lock_guard<std::mutex> lock(database_mutex_);
238  if (images_cache_) {
239  return;
240  }
241 
242  std::vector<Image> images = database_->ReadAllImages();
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));
247  }
248 }
249 
251  keypoints_cache_ =
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>(
256  database_->ReadKeypoints(image_id));
257  });
258 
259  descriptors_cache_ =
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>(
264  database_->ReadDescriptors(image_id));
265  });
266 
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_);
270  return std::make_shared<bool>(database_->ExistsKeypoints(image_id));
271  });
272 
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>(
278  database_->ExistsDescriptors(image_id));
279  });
280 }
281 
283  MaybeLoadCameras();
284  return cameras_cache_->at(camera_id);
285 }
286 
288  MaybeLoadImages();
289  return images_cache_->at(image_id);
290 }
291 
292 std::shared_ptr<FeatureKeypoints> FeatureMatcherCache::GetKeypoints(
293  const image_t image_id) {
294  return keypoints_cache_->Get(image_id);
295 }
296 
297 std::shared_ptr<FeatureDescriptors> FeatureMatcherCache::GetDescriptors(
298  const image_t image_id) {
299  return descriptors_cache_->Get(image_id);
300 }
301 
303  const image_t image_id2) {
304  std::unique_lock<std::mutex> lock(database_mutex_);
305  return database_->ReadMatches(image_id1, image_id2);
306 }
307 
308 std::vector<image_t> FeatureMatcherCache::GetImageIds() {
309  MaybeLoadImages();
310 
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);
315  }
316 
317  return image_ids;
318 }
319 
321  return *keypoints_exists_cache_->Get(image_id);
322 }
323 
325  return *descriptors_exists_cache_->Get(image_id);
326 }
327 
329  const image_t image_id2) {
330  std::unique_lock<std::mutex> lock(database_mutex_);
331  return database_->ExistsMatches(image_id1, image_id2);
332 }
333 
335  const image_t image_id2) {
336  std::unique_lock<std::mutex> lock(database_mutex_);
337  return database_->ExistsInlierMatches(image_id1, image_id2);
338 }
339 
341  const image_t image_id2,
342  const FeatureMatches& matches) {
343  std::unique_lock<std::mutex> lock(database_mutex_);
344  database_->WriteMatches(image_id1, image_id2, matches);
345 }
346 
348  const image_t image_id1, const image_t image_id2,
349  const TwoViewGeometry& two_view_geometry) {
350  std::unique_lock<std::mutex> lock(database_mutex_);
351  database_->WriteTwoViewGeometry(image_id1, image_id2, two_view_geometry);
352 }
353 
355  const image_t image_id2) {
356  std::unique_lock<std::mutex> lock(database_mutex_);
357  database_->DeleteMatches(image_id1, image_id2);
358 }
359 
361  const image_t image_id2) {
362  std::unique_lock<std::mutex> lock(database_mutex_);
363  database_->DeleteInlierMatches(image_id1, image_id2);
364 }
365 
367  FeatureMatcherCache* cache)
368  : options_(options), cache_(cache) {}
369 
370 void FeatureMatcherThread::SetMaxNumMatches(const int max_num_matches) {
371  options_.max_num_matches = max_num_matches;
372 }
373 
375  FeatureMatcherCache* cache,
376  JobQueue<Input>* input_queue,
377  JobQueue<Output>* output_queue)
378  : FeatureMatcherThread(options, cache),
379  input_queue_(input_queue),
380  output_queue_(output_queue) {
381  CHECK(options_.Check());
382 }
383 
386 
387  while (true) {
388  if (IsStopped()) {
389  break;
390  }
391 
392  const auto input_job = input_queue_->Pop();
393  if (input_job.IsValid()) {
394  auto data = input_job.Data();
395 
396  if (!cache_->ExistsDescriptors(data.image_id1) ||
397  !cache_->ExistsDescriptors(data.image_id2)) {
398  CHECK(output_queue_->Push(data));
399  continue;
400  }
401 
402  const auto descriptors1 = cache_->GetDescriptors(data.image_id1);
403  const auto descriptors2 = cache_->GetDescriptors(data.image_id2);
404  MatchSiftFeaturesCPU(options_, *descriptors1, *descriptors2, &data.matches);
405 
406  CHECK(output_queue_->Push(data));
407  }
408  }
409 }
410 
412  FeatureMatcherCache* cache,
413  JobQueue<Input>* input_queue,
414  JobQueue<Output>* output_queue)
415  : FeatureMatcherThread(options, cache),
416  input_queue_(input_queue),
417  output_queue_(output_queue) {
418  CHECK(options_.Check());
419 
422 
423 #ifndef CUDA_ENABLED
425 #endif
426 }
427 
429 #ifndef CUDA_ENABLED
430  if (!opengl_context_) {
431  std::cerr << "ERROR: OpenGL context is null" << std::endl;
433  return;
434  }
435  opengl_context_->MakeCurrent();
436 #endif
437 
438  // CRITICAL: Use constructor with max_num_matches to avoid later reallocation
439  // This prevents double free caused by assignment operator
440  SiftMatchGPU sift_match_gpu(options_.max_num_matches);
441  if (!CreateSiftGPUMatcher(options_, &sift_match_gpu)) {
442  std::cout << "WARNING: SiftGPU not fully supported" << std::endl;
444  return;
445  }
446 
448 
449  while (true) {
450  if (IsStopped()) {
451  break;
452  }
453 
454  const auto input_job = input_queue_->Pop();
455  if (input_job.IsValid()) {
456  auto data = input_job.Data();
457 
458  if (!cache_->ExistsDescriptors(data.image_id1) ||
459  !cache_->ExistsDescriptors(data.image_id2)) {
460  CHECK(output_queue_->Push(data));
461  continue;
462  }
463 
464  try {
465  const FeatureDescriptors* descriptors1_ptr;
466  GetDescriptorData(0, data.image_id1, &descriptors1_ptr);
467  const FeatureDescriptors* descriptors2_ptr;
468  GetDescriptorData(1, data.image_id2, &descriptors2_ptr);
469  MatchSiftFeaturesGPU(options_, descriptors1_ptr, descriptors2_ptr,
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 << "): "
474  << e.what() << std::endl;
475  // Use resize(0) to indicate empty result, safer than clear()
476  data.matches.resize(0);
477  } catch (...) {
478  std::cerr << "ERROR: Unknown exception in GPU matching for image pair ("
479  << data.image_id1 << ", " << data.image_id2 << ")" << std::endl;
480  data.matches.resize(0);
481  }
482 
483  CHECK(output_queue_->Push(data));
484  }
485  }
486 }
487 
489  const int index, const image_t image_id,
490  const FeatureDescriptors** descriptors_ptr) {
491  CHECK_GE(index, 0);
492  CHECK_LE(index, 1);
493  if (prev_uploaded_image_ids_[index] == image_id) {
494  *descriptors_ptr = nullptr;
495  } else {
496  prev_uploaded_descriptors_[index] = *cache_->GetDescriptors(image_id);
497  *descriptors_ptr = &prev_uploaded_descriptors_[index];
498  prev_uploaded_image_ids_[index] = image_id;
499  }
500 }
501 
503  const SiftMatchingOptions& options, FeatureMatcherCache* cache,
504  JobQueue<Input>* input_queue, JobQueue<Output>* output_queue)
505  : FeatureMatcherThread(options, cache),
506  input_queue_(input_queue),
507  output_queue_(output_queue) {
508  CHECK(options_.Check());
509 }
510 
511 void GuidedSiftCPUFeatureMatcher::Run() {
513 
514  while (true) {
515  if (IsStopped()) {
516  break;
517  }
518 
519  const auto input_job = input_queue_->Pop();
520  if (input_job.IsValid()) {
521  auto data = input_job.Data();
522 
523  if (data.two_view_geometry.inlier_matches.size() <
524  static_cast<size_t>(options_.min_num_inliers)) {
525  CHECK(output_queue_->Push(data));
526  continue;
527  }
528 
529  if (!cache_->ExistsKeypoints(data.image_id1) ||
530  !cache_->ExistsKeypoints(data.image_id2) ||
531  !cache_->ExistsDescriptors(data.image_id1) ||
532  !cache_->ExistsDescriptors(data.image_id2)) {
533  CHECK(output_queue_->Push(data));
534  continue;
535  }
536 
537  const auto keypoints1 = cache_->GetKeypoints(data.image_id1);
538  const auto keypoints2 = cache_->GetKeypoints(data.image_id2);
539  const auto descriptors1 =
540  cache_->GetDescriptors(data.image_id1);
541  const auto descriptors2 =
542  cache_->GetDescriptors(data.image_id2);
543  MatchGuidedSiftFeaturesCPU(options_, *keypoints1, *keypoints2, *descriptors1,
544  *descriptors2, &data.two_view_geometry);
545 
546  CHECK(output_queue_->Push(data));
547  }
548  }
549 }
550 
552  const SiftMatchingOptions& options, FeatureMatcherCache* cache,
553  JobQueue<Input>* input_queue, JobQueue<Output>* output_queue)
554  : FeatureMatcherThread(options, cache),
555  input_queue_(input_queue),
556  output_queue_(output_queue) {
557  CHECK(options_.Check());
558 
559  prev_uploaded_image_ids_[0] = kInvalidImageId;
560  prev_uploaded_image_ids_[1] = kInvalidImageId;
561 
562 #ifndef CUDA_ENABLED
563  opengl_context_.reset(new OpenGLContextManager());
564 #endif
565 }
566 
567 void GuidedSiftGPUFeatureMatcher::Run() {
568 #ifndef CUDA_ENABLED
569  if (!opengl_context_) {
570  std::cerr << "ERROR: OpenGL context is null" << std::endl;
572  return;
573  }
574  opengl_context_->MakeCurrent();
575 #endif
576 
577  // CRITICAL: Use constructor with max_num_matches to avoid later reallocation
578  // This prevents double free caused by assignment operator
579  SiftMatchGPU sift_match_gpu(options_.max_num_matches);
580  if (!CreateSiftGPUMatcher(options_, &sift_match_gpu)) {
581  std::cout << "WARNING: SiftGPU not fully supported" << std::endl;
583  return;
584  }
585 
587 
588  while (true) {
589  if (IsStopped()) {
590  break;
591  }
592 
593  const auto input_job = input_queue_->Pop();
594  if (input_job.IsValid()) {
595  auto data = input_job.Data();
596 
597  if (data.two_view_geometry.inlier_matches.size() <
598  static_cast<size_t>(options_.min_num_inliers)) {
599  CHECK(output_queue_->Push(data));
600  continue;
601  }
602 
603  if (!cache_->ExistsKeypoints(data.image_id1) ||
604  !cache_->ExistsKeypoints(data.image_id2) ||
605  !cache_->ExistsDescriptors(data.image_id1) ||
606  !cache_->ExistsDescriptors(data.image_id2)) {
607  CHECK(output_queue_->Push(data));
608  continue;
609  }
610 
611  try {
612  const FeatureDescriptors* descriptors1_ptr;
613  const FeatureKeypoints* keypoints1_ptr;
614  GetFeatureData(0, data.image_id1, &keypoints1_ptr, &descriptors1_ptr);
615  const FeatureDescriptors* descriptors2_ptr;
616  const FeatureKeypoints* keypoints2_ptr;
617  GetFeatureData(1, data.image_id2, &keypoints2_ptr, &descriptors2_ptr);
618 
619  MatchGuidedSiftFeaturesGPU(options_, keypoints1_ptr, keypoints2_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 << "): "
625  << e.what() << std::endl;
626  data.two_view_geometry = TwoViewGeometry();
627  } catch (...) {
628  std::cerr << "ERROR: Unknown exception in guided GPU matching for image pair ("
629  << data.image_id1 << ", " << data.image_id2 << ")" << std::endl;
630  data.two_view_geometry = TwoViewGeometry();
631  }
632 
633  CHECK(output_queue_->Push(data));
634  }
635  }
636 }
637 
638 void GuidedSiftGPUFeatureMatcher::GetFeatureData(
639  const int index, const image_t image_id,
640  const FeatureKeypoints** keypoints_ptr,
641  const FeatureDescriptors** descriptors_ptr) {
642  CHECK_GE(index, 0);
643  CHECK_LE(index, 1);
644  if (prev_uploaded_image_ids_[index] == image_id) {
645  *keypoints_ptr = nullptr;
646  *descriptors_ptr = nullptr;
647  } else {
648  prev_uploaded_keypoints_[index] = *cache_->GetKeypoints(image_id);
649  prev_uploaded_descriptors_[index] = *cache_->GetDescriptors(image_id);
650  *keypoints_ptr = &prev_uploaded_keypoints_[index];
651  *descriptors_ptr = &prev_uploaded_descriptors_[index];
652  prev_uploaded_image_ids_[index] = image_id;
653  }
654 }
655 
657  const SiftMatchingOptions& options, FeatureMatcherCache* cache,
658  JobQueue<Input>* input_queue, JobQueue<Output>* output_queue)
659  : options_(options),
660  cache_(cache),
661  input_queue_(input_queue),
662  output_queue_(output_queue) {
663  CHECK(options_.Check());
664 
666  static_cast<size_t>(options_.min_num_inliers);
670  static_cast<size_t>(options_.min_num_trials);
672  static_cast<size_t>(options_.max_num_trials);
675 }
676 
678  while (true) {
679  if (IsStopped()) {
680  break;
681  }
682 
683  const auto input_job = input_queue_->Pop();
684  if (input_job.IsValid()) {
685  auto data = input_job.Data();
686 
687  if (data.matches.size() < static_cast<size_t>(options_.min_num_inliers)) {
688  CHECK(output_queue_->Push(data));
689  continue;
690  }
691 
692  try {
693  const auto& image1 = cache_->GetImage(data.image_id1);
694  const auto& image2 = cache_->GetImage(data.image_id2);
695  const auto& camera1 = cache_->GetCamera(image1.CameraId());
696  const auto& camera2 = cache_->GetCamera(image2.CameraId());
697  const auto keypoints1 = cache_->GetKeypoints(data.image_id1);
698  const auto keypoints2 = cache_->GetKeypoints(data.image_id2);
699  const auto points1 = FeatureKeypointsToPointsVector(*keypoints1);
700  const auto points2 = FeatureKeypointsToPointsVector(*keypoints2);
701 
703  data.two_view_geometry.EstimateMultiple(camera1, points1, camera2,
704  points2, data.matches,
706  } else {
707  data.two_view_geometry.Estimate(camera1, points1, camera2, points2,
708  data.matches,
710  }
711  } catch (const std::exception& e) {
712  std::cerr << "ERROR: TwoViewGeometryVerifier failed for image pair ("
713  << data.image_id1 << ", " << data.image_id2 << "): "
714  << e.what() << std::endl;
715  // Clear geometry on error
716  data.two_view_geometry = TwoViewGeometry();
717  }
718 
719  CHECK(output_queue_->Push(data));
720  }
721  }
722 }
723 
725  Database* database,
726  FeatureMatcherCache* cache)
727  : options_(options), database_(database), cache_(cache), is_setup_(false) {
728  CHECK(options_.Check());
729 
730  const int num_threads = GetEffectiveNumThreads(options_.num_threads);
731  CHECK_GT(num_threads, 0);
732 
733  std::vector<int> gpu_indices = CSVToVector<int>(options_.gpu_index);
734  CHECK_GT(gpu_indices.size(), 0);
735 
736 #ifdef CUDA_ENABLED
737  if (options_.use_gpu && gpu_indices.size() == 1 && gpu_indices[0] == -1) {
738  const int num_cuda_devices = GetNumCudaDevices();
739  if (num_cuda_devices <= 0) {
740  std::cerr << "WARNING: No CUDA devices available, falling back to CPU matching" << std::endl;
741  options_.use_gpu = false;
742  gpu_indices.clear();
743  gpu_indices.push_back(-1);
744  } else {
745  gpu_indices.resize(num_cuda_devices);
746  std::iota(gpu_indices.begin(), gpu_indices.end(), 0);
747  }
748  }
749 #endif // CUDA_ENABLED
750 
751  if (options_.use_gpu) {
752  auto gpu_options = options_;
753  matchers_.reserve(gpu_indices.size());
754  for (const auto& gpu_index : gpu_indices) {
755  gpu_options.gpu_index = std::to_string(gpu_index);
756  matchers_.emplace_back(new SiftGPUFeatureMatcher(
757  gpu_options, cache, &matcher_queue_, &verifier_queue_));
758  }
759  } else {
760  matchers_.reserve(num_threads);
761  for (int i = 0; i < num_threads; ++i) {
762  matchers_.emplace_back(new SiftCPUFeatureMatcher(
763  options_, cache, &matcher_queue_, &verifier_queue_));
764  }
765  }
766 
767  verifiers_.reserve(num_threads);
768  if (options_.guided_matching) {
769  for (int i = 0; i < num_threads; ++i) {
770  verifiers_.emplace_back(new TwoViewGeometryVerifier(
771  options_, cache, &verifier_queue_, &guided_matcher_queue_));
772  }
773 
774  if (options_.use_gpu) {
775  auto gpu_options = options_;
776  guided_matchers_.reserve(gpu_indices.size());
777  for (const auto& gpu_index : gpu_indices) {
778  gpu_options.gpu_index = std::to_string(gpu_index);
779  guided_matchers_.emplace_back(new GuidedSiftGPUFeatureMatcher(
780  gpu_options, cache, &guided_matcher_queue_, &output_queue_));
781  }
782  } else {
783  guided_matchers_.reserve(num_threads);
784  for (int i = 0; i < num_threads; ++i) {
785  guided_matchers_.emplace_back(new GuidedSiftCPUFeatureMatcher(
786  options_, cache, &guided_matcher_queue_, &output_queue_));
787  }
788  }
789  } else {
790  for (int i = 0; i < num_threads; ++i) {
791  verifiers_.emplace_back(new TwoViewGeometryVerifier(
792  options_, cache, &verifier_queue_, &output_queue_));
793  }
794  }
795 }
796 
798  matcher_queue_.Wait();
799  verifier_queue_.Wait();
800  guided_matcher_queue_.Wait();
801  output_queue_.Wait();
802 
803  for (size_t i = 0; i < matchers_.size(); ++i) {
804  matchers_[i]->Stop();
805  }
806 
807  for (size_t i = 0; i < verifiers_.size(); ++i) {
808  verifiers_[i]->Stop();
809  }
810 
811  for (size_t i = 0; i < guided_matchers_.size(); ++i) {
812  guided_matchers_[i]->Stop();
813  }
814 
815  matcher_queue_.Stop();
816  verifier_queue_.Stop();
817  guided_matcher_queue_.Stop();
818  output_queue_.Stop();
819 
820  for (size_t i = 0; i < matchers_.size(); ++i) {
821  matchers_[i]->Wait();
822  }
823 
824  for (size_t i = 0; i < verifiers_.size(); ++i) {
825  verifiers_[i]->Wait();
826  }
827 
828  for (size_t i = 0; i < guided_matchers_.size(); ++i) {
829  guided_matchers_[i]->Wait();
830  }
831 }
832 
834  const int max_num_features = CHECK_NOTNULL(database_)->MaxNumDescriptors();
835  options_.max_num_matches =
836  std::min(options_.max_num_matches, max_num_features);
837 
838  for (auto& matcher : matchers_) {
839  matcher->SetMaxNumMatches(options_.max_num_matches);
840  matcher->Start();
841  }
842 
843  for (auto& verifier : verifiers_) {
844  verifier->Start();
845  }
846 
847  for (auto& guided_matcher : guided_matchers_) {
848  guided_matcher->SetMaxNumMatches(options_.max_num_matches);
849  guided_matcher->Start();
850  }
851 
852  for (auto& matcher : matchers_) {
853  if (!matcher->CheckValidSetup()) {
854  return false;
855  }
856  }
857 
858  for (auto& guided_matcher : guided_matchers_) {
859  if (!guided_matcher->CheckValidSetup()) {
860  return false;
861  }
862  }
863 
864  is_setup_ = true;
865 
866  return true;
867 }
868 
870  const std::vector<std::pair<image_t, image_t>>& image_pairs) {
871  CHECK_NOTNULL(database_);
872  CHECK_NOTNULL(cache_);
873  CHECK(is_setup_);
874 
875  if (image_pairs.empty()) {
876  return;
877  }
878 
880  // Match the image pairs
882 
883  std::unordered_set<image_pair_t> image_pair_ids;
884  image_pair_ids.reserve(image_pairs.size());
885 
886  size_t num_outputs = 0;
887  for (const auto& image_pair : image_pairs) {
888  // Avoid self-matches.
889  if (image_pair.first == image_pair.second) {
890  continue;
891  }
892 
893  // Avoid duplicate image pairs.
894  const image_pair_t pair_id =
895  Database::ImagePairToPairId(image_pair.first, image_pair.second);
896  if (image_pair_ids.count(pair_id) > 0) {
897  continue;
898  }
899 
900  image_pair_ids.insert(pair_id);
901 
902  const bool exists_matches =
903  cache_->ExistsMatches(image_pair.first, image_pair.second);
904  const bool exists_inlier_matches =
905  cache_->ExistsInlierMatches(image_pair.first, image_pair.second);
906 
907  if (exists_matches && exists_inlier_matches) {
908  continue;
909  }
910 
911  num_outputs += 1;
912 
913  // If only one of the matches or inlier matches exist, we recompute them
914  // from scratch and delete the existing results. This must be done before
915  // pushing the jobs to the queue, otherwise database constraints might fail
916  // when writing an existing result into the database.
917 
918  if (exists_inlier_matches) {
919  cache_->DeleteInlierMatches(image_pair.first, image_pair.second);
920  }
921 
923  data.image_id1 = image_pair.first;
924  data.image_id2 = image_pair.second;
925 
926  if (exists_matches) {
927  data.matches = cache_->GetMatches(image_pair.first, image_pair.second);
928  cache_->DeleteMatches(image_pair.first, image_pair.second);
929  CHECK(verifier_queue_.Push(data));
930  } else {
931  CHECK(matcher_queue_.Push(data));
932  }
933  }
934 
936  // Write results to database
938 
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();
943 
944  if (output.matches.size() < static_cast<size_t>(options_.min_num_inliers)) {
945  output.matches = {};
946  }
947 
948  if (output.two_view_geometry.inlier_matches.size() <
949  static_cast<size_t>(options_.min_num_inliers)) {
950  output.two_view_geometry = TwoViewGeometry();
951  }
952 
953  cache_->WriteMatches(output.image_id1, output.image_id2, output.matches);
954  cache_->WriteTwoViewGeometry(output.image_id1, output.image_id2,
955  output.two_view_geometry);
956  }
957 
958  CHECK_EQ(output_queue_.Size(), 0);
959 }
960 
962  const ExhaustiveMatchingOptions& options,
963  const SiftMatchingOptions& match_options, const std::string& database_path)
964  : options_(options),
965  match_options_(match_options) {
966  CHECK(options_.Check());
967  CHECK(match_options_.Check());
968 
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());
972 }
973 
974 void ExhaustiveFeatureMatcher::Run() {
975  PrintHeading1("Exhaustive feature matching");
976 
977  if (!matcher_->Setup()) {
978  return;
979  }
980 
981  cache_->Setup();
982 
983  const std::vector<image_t> image_ids = cache_->GetImageIds();
984 
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;
989 
990  std::vector<std::pair<image_t, image_t>> image_pairs;
991  image_pairs.reserve(num_pairs_per_block);
992 
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;
1001 
1002  if (IsStopped()) {
1003  GetTimer().PrintMinutes();
1004  return;
1005  }
1006 
1007  Timer timer;
1008  timer.Start();
1009 
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)
1013  << std::flush;
1014 
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) ||
1021  (idx1 < idx2 &&
1022  block_id1 < block_id2)) { // Avoid duplicate pairs
1023  image_pairs.emplace_back(image_ids[idx1], image_ids[idx2]);
1024  }
1025  }
1026  }
1027 
1028  DatabaseTransaction database_transaction(database_.get());
1029  matcher_->Match(image_pairs);
1030 
1031  PrintElapsedTime(timer);
1032  }
1033  }
1034 
1035  GetTimer().PrintMinutes();
1036 }
1037 
1039  const SequentialMatchingOptions& options,
1040  const SiftMatchingOptions& match_options, const std::string& database_path)
1041  : options_(options),
1042  match_options_(match_options),
1043  database_(database_path),
1044  cache_(std::make_shared<FeatureMatcherCache>(std::max(5 * options_.loop_detection_num_images,
1045  5 * options_.overlap), &database_)),
1046  matcher_(match_options, &database_, cache_.get()) {
1047  CHECK(options_.Check());
1048  CHECK(match_options_.Check());
1049 }
1050 
1051 void SequentialFeatureMatcher::Run() {
1052  PrintHeading1("Sequential feature matching");
1053 
1054  if (!matcher_.Setup()) {
1055  return;
1056  }
1057 
1058  cache_->Setup();
1059 
1060  const std::vector<image_t> ordered_image_ids = GetOrderedImageIds();
1061 
1062  RunSequentialMatching(ordered_image_ids);
1063  if (options_.loop_detection) {
1064  RunLoopDetection(ordered_image_ids);
1065  }
1066 
1067  GetTimer().PrintMinutes();
1068 }
1069 
1070 std::vector<image_t> SequentialFeatureMatcher::GetOrderedImageIds() const {
1071  const std::vector<image_t> image_ids = cache_->GetImageIds();
1072 
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));
1077  }
1078 
1079  std::sort(ordered_images.begin(), ordered_images.end(),
1080  [](const Image& image1, const Image& image2) {
1081  return image1.Name() < image2.Name();
1082  });
1083 
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());
1088  }
1089 
1090  return ordered_image_ids;
1091 }
1092 
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);
1097 
1098  for (size_t image_idx1 = 0; image_idx1 < image_ids.size(); ++image_idx1) {
1099  if (IsStopped()) {
1100  return;
1101  }
1102 
1103  const auto image_id1 = image_ids.at(image_idx1);
1104 
1105  Timer timer;
1106  timer.Start();
1107 
1108  std::cout << StringPrintf("Matching image [%d/%d]", image_idx1 + 1,
1109  image_ids.size())
1110  << std::flush;
1111 
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));
1117  if (options_.quadratic_overlap) {
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));
1122  }
1123  }
1124  } else {
1125  break;
1126  }
1127  }
1128 
1129  DatabaseTransaction database_transaction(&database_);
1130  matcher_.Match(image_pairs);
1131 
1132  PrintElapsedTime(timer);
1133  }
1134 }
1135 
1136 void SequentialFeatureMatcher::RunLoopDetection(
1137  const std::vector<image_t>& image_ids) {
1138  // Read the pre-trained vocabulary tree from disk.
1139  // Automatically download and cache if URI format is provided.
1140  std::string vocab_tree_path =
1141  MaybeDownloadAndCacheFile(options_.vocab_tree_path).string();
1142  retrieval::VisualIndex<> visual_index;
1143  visual_index.Read(vocab_tree_path);
1144 
1145  // Index all images in the visual index.
1146  IndexImagesInVisualIndex(match_options_.num_threads,
1147  options_.loop_detection_num_checks,
1148  options_.loop_detection_max_num_features, image_ids,
1149  this, cache_.get(), &visual_index);
1150 
1151  if (IsStopped()) {
1152  return;
1153  }
1154 
1155  // Only perform loop detection for every n-th image.
1156  std::vector<image_t> match_image_ids;
1157  for (size_t i = 0; i < image_ids.size();
1158  i += options_.loop_detection_period) {
1159  match_image_ids.push_back(image_ids[i]);
1160  }
1161 
1162  MatchNearestNeighborsInVisualIndex(
1163  match_options_.num_threads, options_.loop_detection_num_images,
1165  options_.loop_detection_num_checks,
1167  options_.loop_detection_max_num_features, match_image_ids, this, cache_.get(),
1168  &visual_index, &matcher_);
1169 }
1170 
1172  const VocabTreeMatchingOptions& options,
1173  const SiftMatchingOptions& match_options, const std::string& database_path)
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());
1181 }
1182 
1183 void VocabTreeFeatureMatcher::Run() {
1184  PrintHeading1("Vocabulary tree feature matching");
1185 
1186  if (!matcher_.Setup()) {
1187  return;
1188  }
1189 
1190  cache_.Setup();
1191 
1192  // Read the pre-trained vocabulary tree from disk.
1193  // Automatically download and cache if URI format is provided.
1194  std::string vocab_tree_path =
1195  MaybeDownloadAndCacheFile(options_.vocab_tree_path).string();
1196  retrieval::VisualIndex<> visual_index;
1197  visual_index.Read(vocab_tree_path);
1198 
1199  const std::vector<image_t> all_image_ids = cache_.GetImageIds();
1200  std::vector<image_t> image_ids;
1201  if (options_.match_list_path == "") {
1202  image_ids = cache_.GetImageIds();
1203  } else {
1204  // Map image names to image identifiers.
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) {
1208  const auto& image = cache_.GetImage(image_id);
1209  image_name_to_image_id.emplace(image.Name(), image_id);
1210  }
1211 
1212  // Read the match list path.
1213  std::ifstream file(options_.match_list_path);
1214  CHECK(file.is_open()) << options_.match_list_path;
1215  std::string line;
1216  while (std::getline(file, line)) {
1217  StringTrim(&line);
1218 
1219  if (line.empty() || line[0] == '#') {
1220  continue;
1221  }
1222 
1223  if (image_name_to_image_id.count(line) == 0) {
1224  std::cerr << "ERROR: Image " << line << " does not exist." << std::endl;
1225  } else {
1226  image_ids.push_back(image_name_to_image_id.at(line));
1227  }
1228  }
1229  }
1230 
1231  // Index all images in the visual index.
1232  IndexImagesInVisualIndex(match_options_.num_threads, options_.num_checks,
1233  options_.max_num_features, all_image_ids, this,
1234  &cache_, &visual_index);
1235 
1236  if (IsStopped()) {
1237  GetTimer().PrintMinutes();
1238  return;
1239  }
1240 
1241  // Match all images in the visual index.
1242  MatchNearestNeighborsInVisualIndex(
1243  match_options_.num_threads, options_.num_images,
1244  options_.num_nearest_neighbors, options_.num_checks,
1246  image_ids, this, &cache_, &visual_index, &matcher_);
1247 
1248  GetTimer().PrintMinutes();
1249 }
1250 
1252  const SpatialMatchingOptions& options,
1253  const SiftMatchingOptions& match_options, const std::string& database_path)
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());
1261 }
1262 
1263 void SpatialFeatureMatcher::Run() {
1264  PrintHeading1("Spatial feature matching");
1265 
1266  if (!matcher_.Setup()) {
1267  return;
1268  }
1269 
1270  cache_.Setup();
1271 
1272  const std::vector<image_t> image_ids = cache_.GetImageIds();
1273 
1275  // Spatial indexing
1277 
1278  Timer timer;
1279  timer.Start();
1280 
1281  std::cout << "Indexing images..." << std::flush;
1282 
1283  GPSTransform gps_transform;
1284 
1285  size_t num_locations = 0;
1286  Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor> location_matrix(
1287  image_ids.size(), 3);
1288 
1289  std::vector<size_t> location_idxs;
1290  location_idxs.reserve(image_ids.size());
1291 
1292  std::vector<Eigen::Vector3d> ells(1);
1293 
1294  for (size_t i = 0; i < image_ids.size(); ++i) {
1295  const auto image_id = image_ids[i];
1296  const auto& image = cache_.GetImage(image_id);
1297 
1298  if ((image.TvecPrior(0) == 0 && image.TvecPrior(1) == 0 &&
1299  options_.ignore_z) ||
1300  (image.TvecPrior(0) == 0 && image.TvecPrior(1) == 0 &&
1301  image.TvecPrior(2) == 0 && !options_.ignore_z)) {
1302  continue;
1303  }
1304 
1305  location_idxs.push_back(i);
1306 
1307  if (options_.is_gps) {
1308  ells[0](0) = image.TvecPrior(0);
1309  ells[0](1) = image.TvecPrior(1);
1310  ells[0](2) = options_.ignore_z ? 0 : image.TvecPrior(2);
1311 
1312  const auto xyzs = gps_transform.EllToXYZ(ells);
1313 
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));
1317  } else {
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));
1324  }
1325 
1326  num_locations += 1;
1327  }
1328 
1329  PrintElapsedTime(timer);
1330 
1331  if (num_locations == 0) {
1332  std::cout << " => No images with location data." << std::endl;
1333  GetTimer().PrintMinutes();
1334  return;
1335  }
1336 
1338  // Building spatial index
1340 
1341  timer.Restart();
1342 
1343  std::cout << "Building search index..." << std::flush;
1344 
1345  flann::Matrix<float> locations(location_matrix.data(), num_locations,
1346  location_matrix.cols());
1347 
1348  flann::LinearIndexParams index_params;
1349  flann::LinearIndex<flann::L2<float>> search_index(index_params);
1350  search_index.buildIndex(locations);
1351 
1352  PrintElapsedTime(timer);
1353 
1355  // Searching spatial index
1357 
1358  timer.Restart();
1359 
1360  std::cout << "Searching for nearest neighbors..." << std::flush;
1361 
1362  const int knn = std::min<int>(options_.max_num_neighbors, num_locations);
1363 
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);
1367 
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);
1371 
1372  flann::SearchParams search_params(flann::FLANN_CHECKS_AUTOTUNED);
1373  if (match_options_.num_threads == ThreadPool::kMaxNumThreads) {
1374  search_params.cores = std::thread::hardware_concurrency();
1375  } else {
1376  search_params.cores = match_options_.num_threads;
1377  }
1378  if (search_params.cores <= 0) {
1379  search_params.cores = 1;
1380  }
1381 
1382  search_index.knnSearch(locations, indices, distances, knn, search_params);
1383 
1384  PrintElapsedTime(timer);
1385 
1387  // Matching
1389 
1390  const float max_distance =
1391  static_cast<float>(options_.max_distance * options_.max_distance);
1392 
1393  std::vector<std::pair<image_t, image_t>> image_pairs;
1394  image_pairs.reserve(knn);
1395 
1396  for (size_t i = 0; i < num_locations; ++i) {
1397  if (IsStopped()) {
1398  GetTimer().PrintMinutes();
1399  return;
1400  }
1401 
1402  timer.Restart();
1403 
1404  std::cout << StringPrintf("Matching image [%d/%d]", i + 1, num_locations)
1405  << std::flush;
1406 
1407  image_pairs.clear();
1408 
1409  for (int j = 0; j < knn; ++j) {
1410  // Check if query equals result.
1411  if (index_matrix(i, j) == i) {
1412  continue;
1413  }
1414 
1415  // Since the nearest neighbors are sorted by distance, we can break.
1416  if (distance_matrix(i, j) > max_distance) {
1417  break;
1418  }
1419 
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);
1425  }
1426 
1427  DatabaseTransaction database_transaction(&database_);
1428  matcher_.Match(image_pairs);
1429 
1430  PrintElapsedTime(timer);
1431  }
1432 
1433  GetTimer().PrintMinutes();
1434 }
1435 
1437  const TransitiveMatchingOptions& options,
1438  const SiftMatchingOptions& match_options, const std::string& database_path)
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());
1446 }
1447 
1448 void TransitiveFeatureMatcher::Run() {
1449  PrintHeading1("Transitive feature matching");
1450 
1451  if (!matcher_.Setup()) {
1452  return;
1453  }
1454 
1455  cache_.Setup();
1456 
1457  const std::vector<image_t> image_ids = cache_.GetImageIds();
1458 
1459  std::vector<std::pair<image_t, image_t>> image_pairs;
1460  std::unordered_set<image_pair_t> image_pair_ids;
1461 
1462  for (int iteration = 0; iteration < options_.num_iterations; ++iteration) {
1463  if (IsStopped()) {
1464  GetTimer().PrintMinutes();
1465  return;
1466  }
1467 
1468  Timer timer;
1469  timer.Start();
1470 
1471  std::cout << StringPrintf("Iteration [%d/%d]", iteration + 1,
1472  options_.num_iterations)
1473  << std::endl;
1474 
1475  std::vector<std::pair<image_t, image_t>> existing_image_pairs;
1476  std::vector<int> existing_num_inliers;
1477  database_.ReadTwoViewGeometryNumInliers(&existing_image_pairs,
1478  &existing_num_inliers);
1479 
1480  CHECK_EQ(existing_image_pairs.size(), existing_num_inliers.size());
1481 
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);
1486  }
1487 
1488  const size_t batch_size = static_cast<size_t>(options_.batch_size);
1489 
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 =
1499  Database::ImagePairToPairId(image_id1, image_id3);
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) {
1504  num_batches += 1;
1505  std::cout << StringPrintf(" Batch %d", num_batches)
1506  << std::flush;
1507  DatabaseTransaction database_transaction(&database_);
1508  matcher_.Match(image_pairs);
1509  image_pairs.clear();
1510  PrintElapsedTime(timer);
1511  timer.Restart();
1512 
1513  if (IsStopped()) {
1514  GetTimer().PrintMinutes();
1515  return;
1516  }
1517  }
1518  }
1519  }
1520  }
1521  }
1522  }
1523 
1524  num_batches += 1;
1525  std::cout << StringPrintf(" Batch %d", num_batches) << std::flush;
1526  DatabaseTransaction database_transaction(&database_);
1527  matcher_.Match(image_pairs);
1528  PrintElapsedTime(timer);
1529  }
1530 
1531  GetTimer().PrintMinutes();
1532 }
1533 
1535  const ImagePairsMatchingOptions& options,
1536  const SiftMatchingOptions& match_options, const std::string& database_path)
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());
1544 }
1545 
1546 void ImagePairsFeatureMatcher::Run() {
1547  PrintHeading1("Custom feature matching");
1548 
1549  if (!matcher_.Setup()) {
1550  return;
1551  }
1552 
1553  cache_.Setup();
1554 
1556  // Reading image pairs list
1558 
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()) {
1562  const auto& image = cache_.GetImage(image_id);
1563  image_name_to_image_id.emplace(image.Name(), image_id);
1564  }
1565 
1566  std::ifstream file(options_.match_list_path);
1567  CHECK(file.is_open()) << options_.match_list_path;
1568 
1569  std::string line;
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)) {
1573  StringTrim(&line);
1574 
1575  if (line.empty() || line[0] == '#') {
1576  continue;
1577  }
1578 
1579  std::stringstream line_stream(line);
1580 
1581  std::string image_name1;
1582  std::string image_name2;
1583 
1584  std::getline(line_stream, image_name1, ' ');
1585  StringTrim(&image_name1);
1586  std::getline(line_stream, image_name2, ' ');
1587  StringTrim(&image_name2);
1588 
1589  if (image_name_to_image_id.count(image_name1) == 0) {
1590  std::cerr << "ERROR: Image " << image_name1 << " does not exist."
1591  << std::endl;
1592  continue;
1593  }
1594  if (image_name_to_image_id.count(image_name2) == 0) {
1595  std::cerr << "ERROR: Image " << image_name2 << " does not exist."
1596  << std::endl;
1597  continue;
1598  }
1599 
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);
1602  const image_pair_t image_pair =
1603  Database::ImagePairToPairId(image_id1, image_id2);
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);
1607  }
1608  }
1609 
1611  // Feature matching
1613 
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);
1617 
1618  for (size_t i = 0; i < image_pairs.size(); i += options_.block_size) {
1619  if (IsStopped()) {
1620  GetTimer().PrintMinutes();
1621  return;
1622  }
1623 
1624  Timer timer;
1625  timer.Start();
1626 
1627  std::cout << StringPrintf("Matching block [%d/%d]",
1628  i / options_.block_size + 1, num_match_blocks)
1629  << std::flush;
1630 
1631  const size_t block_end = i + options_.block_size <= image_pairs.size()
1632  ? i + options_.block_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]);
1638  }
1639 
1640  DatabaseTransaction database_transaction(&database_);
1641  matcher_.Match(block_image_pairs);
1642 
1643  PrintElapsedTime(timer);
1644  }
1645 
1646  GetTimer().PrintMinutes();
1647 }
1648 
1650  const FeaturePairsMatchingOptions& options,
1651  const SiftMatchingOptions& match_options, const std::string& database_path)
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());
1658 }
1659 
1660 void FeaturePairsFeatureMatcher::Run() {
1661  PrintHeading1("Importing matches");
1662 
1663  cache_.Setup();
1664 
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()) {
1668  const auto& image = cache_.GetImage(image_id);
1669  image_name_to_image.emplace(image.Name(), &image);
1670  }
1671 
1672  std::ifstream file(options_.match_list_path);
1673  CHECK(file.is_open()) << options_.match_list_path;
1674 
1675  std::string line;
1676  while (std::getline(file, line)) {
1677  if (IsStopped()) {
1678  GetTimer().PrintMinutes();
1679  return;
1680  }
1681 
1682  StringTrim(&line);
1683  if (line.empty()) {
1684  continue;
1685  }
1686 
1687  std::istringstream line_stream(line);
1688 
1689  std::string image_name1, image_name2;
1690  try {
1691  line_stream >> image_name1 >> image_name2;
1692  } catch (...) {
1693  std::cerr << "ERROR: Could not read image pair." << std::endl;
1694  break;
1695  }
1696 
1697  std::cout << StringPrintf("%s - %s", image_name1.c_str(),
1698  image_name2.c_str())
1699  << std::endl;
1700 
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())
1704  << std::endl;
1705  break;
1706  }
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())
1710  << std::endl;
1711  break;
1712  }
1713 
1714  const Image& image1 = *image_name_to_image[image_name1];
1715  const Image& image2 = *image_name_to_image[image_name2];
1716 
1717  bool skip_pair = false;
1718  if (database_.ExistsInlierMatches(image1.ImageId(), image2.ImageId())) {
1719  std::cout << "SKIP: Matches for image pair already exist in database."
1720  << std::endl;
1721  skip_pair = true;
1722  }
1723 
1724  FeatureMatches matches;
1725  while (std::getline(file, line)) {
1726  StringTrim(&line);
1727 
1728  if (line.empty()) {
1729  break;
1730  }
1731 
1732  std::istringstream line_stream(line);
1733 
1734  FeatureMatch match;
1735  try {
1736  line_stream >> match.point2D_idx1 >> match.point2D_idx2;
1737  } catch (...) {
1738  std::cerr << "ERROR: Cannot read feature matches." << std::endl;
1739  break;
1740  }
1741 
1742  matches.push_back(match);
1743  }
1744 
1745  if (skip_pair) {
1746  continue;
1747  }
1748 
1749  const Camera& camera1 = cache_.GetCamera(image1.CameraId());
1750  const Camera& camera2 = cache_.GetCamera(image2.CameraId());
1751 
1752  if (options_.verify_matches) {
1753  database_.WriteMatches(image1.ImageId(), image2.ImageId(), matches);
1754 
1755  const auto keypoints1 = cache_.GetKeypoints(image1.ImageId());
1756  const auto keypoints2 = cache_.GetKeypoints(image2.ImageId());
1757 
1758  TwoViewGeometry two_view_geometry;
1759  TwoViewGeometry::Options two_view_geometry_options;
1760  two_view_geometry_options.min_num_inliers =
1761  static_cast<size_t>(match_options_.min_num_inliers);
1762  two_view_geometry_options.ransac_options.max_error =
1763  match_options_.max_error;
1764  two_view_geometry_options.ransac_options.confidence =
1765  match_options_.confidence;
1766  two_view_geometry_options.ransac_options.min_num_trials =
1767  static_cast<size_t>(match_options_.min_num_trials);
1768  two_view_geometry_options.ransac_options.max_num_trials =
1769  static_cast<size_t>(match_options_.max_num_trials);
1770  two_view_geometry_options.ransac_options.min_inlier_ratio =
1771  match_options_.min_inlier_ratio;
1772 
1773  two_view_geometry.Estimate(
1774  camera1, FeatureKeypointsToPointsVector(*keypoints1), camera2,
1775  FeatureKeypointsToPointsVector(*keypoints2), matches,
1776  two_view_geometry_options);
1777 
1778  database_.WriteTwoViewGeometry(image1.ImageId(), image2.ImageId(),
1779  two_view_geometry);
1780  } else {
1781  TwoViewGeometry two_view_geometry;
1782 
1783  if (camera1.HasPriorFocalLength() && camera2.HasPriorFocalLength()) {
1784  two_view_geometry.config = TwoViewGeometry::CALIBRATED;
1785  } else {
1786  two_view_geometry.config = TwoViewGeometry::UNCALIBRATED;
1787  }
1788 
1789  two_view_geometry.inlier_matches = matches;
1790 
1791  database_.WriteTwoViewGeometry(image1.ImageId(), image2.ImageId(),
1792  two_view_geometry);
1793  }
1794  }
1795 
1796  GetTimer().PrintMinutes();
1797 }
1798 
1799 } // namespace colmap
std::shared_ptr< core::Tensor > image
FeatureDescriptors ReadDescriptors(const image_t image_id) const
Definition: database.cc:448
void ReadTwoViewGeometryNumInliers(std::vector< std::pair< image_t, image_t >> *image_pairs, std::vector< int > *num_inliers) const
Definition: database.cc:579
FeatureMatches ReadMatches(const image_t image_id1, const image_t image_id2) const
Definition: database.cc:461
void DeleteInlierMatches(const image_t image_id1, const image_t image_id2) const
Definition: database.cc:818
void WriteMatches(const image_t image_id1, const image_t image_id2, const FeatureMatches &matches) const
Definition: database.cc:685
bool ExistsMatches(const image_t image_id1, const image_t image_id2) const
Definition: database.cc:315
FeatureKeypoints ReadKeypoints(const image_t image_id) const
Definition: database.cc:436
void WriteTwoViewGeometry(const image_t image_id1, const image_t image_id2, const TwoViewGeometry &two_view_geometry) const
Definition: database.cc:703
static image_pair_t ImagePairToPairId(const image_t image_id1, const image_t image_id2)
Definition: database.h:339
bool ExistsKeypoints(const image_t image_id) const
Definition: database.cc:307
bool ExistsInlierMatches(const image_t image_id1, const image_t image_id2) const
Definition: database.cc:321
std::vector< Camera > ReadAllCameras() const
Definition: database.cc:380
void DeleteMatches(const image_t image_id1, const image_t image_id2) const
Definition: database.cc:809
bool ExistsDescriptors(const image_t image_id) const
Definition: database.cc:311
std::vector< Image > ReadAllImages() const
Definition: database.cc:423
ExhaustiveFeatureMatcher(const ExhaustiveMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
Definition: matching.cc:961
const Image & GetImage(const image_t image_id)
Definition: matching.cc:287
FeatureMatches GetMatches(const image_t image_id1, const image_t image_id2)
Definition: matching.cc:302
FeatureMatcherCache(const size_t cache_size, const Database *database)
Definition: matching.cc:216
void WriteMatches(const image_t image_id1, const image_t image_id2, const FeatureMatches &matches)
Definition: matching.cc:340
bool ExistsInlierMatches(const image_t image_id1, const image_t image_id2)
Definition: matching.cc:334
void DeleteInlierMatches(const image_t image_id1, const image_t image_id2)
Definition: matching.cc:360
void WriteTwoViewGeometry(const image_t image_id1, const image_t image_id2, const TwoViewGeometry &two_view_geometry)
Definition: matching.cc:347
std::vector< image_t > GetImageIds()
Definition: matching.cc:308
const Camera & GetCamera(const camera_t camera_id)
Definition: matching.cc:282
bool ExistsDescriptors(const image_t image_id)
Definition: matching.cc:324
void DeleteMatches(const image_t image_id1, const image_t image_id2)
Definition: matching.cc:354
std::shared_ptr< FeatureKeypoints > GetKeypoints(const image_t image_id)
Definition: matching.cc:292
bool ExistsKeypoints(const image_t image_id)
Definition: matching.cc:320
bool ExistsMatches(const image_t image_id1, const image_t image_id2)
Definition: matching.cc:328
std::shared_ptr< FeatureDescriptors > GetDescriptors(const image_t image_id)
Definition: matching.cc:297
void SetMaxNumMatches(const int max_num_matches)
Definition: matching.cc:370
FeatureMatcherThread(const SiftMatchingOptions &options, FeatureMatcherCache *cache)
Definition: matching.cc:366
SiftMatchingOptions options_
Definition: matching.h:214
FeatureMatcherCache * cache_
Definition: matching.h:215
FeaturePairsFeatureMatcher(const FeaturePairsMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
Definition: matching.cc:1649
GuidedSiftCPUFeatureMatcher(const SiftMatchingOptions &options, FeatureMatcherCache *cache, JobQueue< Input > *input_queue, JobQueue< Output > *output_queue)
Definition: matching.cc:502
GuidedSiftGPUFeatureMatcher(const SiftMatchingOptions &options, FeatureMatcherCache *cache, JobQueue< Input > *input_queue, JobQueue< Output > *output_queue)
Definition: matching.cc:551
ImagePairsFeatureMatcher(const ImagePairsMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
Definition: matching.cc:1534
SequentialFeatureMatcher(const SequentialMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
Definition: matching.cc:1038
SiftCPUFeatureMatcher(const SiftMatchingOptions &options, FeatureMatcherCache *cache, JobQueue< Input > *input_queue, JobQueue< Output > *output_queue)
Definition: matching.cc:374
JobQueue< Input > * input_queue_
Definition: matching.h:231
JobQueue< Output > * output_queue_
Definition: matching.h:232
SiftFeatureMatcher(const SiftMatchingOptions &options, Database *database, FeatureMatcherCache *cache)
Definition: matching.cc:724
void Match(const std::vector< std::pair< image_t, image_t >> &image_pairs)
Definition: matching.cc:869
void GetDescriptorData(const int index, const image_t image_id, const FeatureDescriptors **descriptors_ptr)
Definition: matching.cc:488
JobQueue< Input > * input_queue_
Definition: matching.h:252
JobQueue< Output > * output_queue_
Definition: matching.h:253
std::array< image_t, 2 > prev_uploaded_image_ids_
Definition: matching.h:258
std::array< FeatureDescriptors, 2 > prev_uploaded_descriptors_
Definition: matching.h:259
SiftGPUFeatureMatcher(const SiftMatchingOptions &options, FeatureMatcherCache *cache, JobQueue< Input > *input_queue, JobQueue< Output > *output_queue)
Definition: matching.cc:411
std::unique_ptr< OpenGLContextManager > opengl_context_
Definition: matching.h:255
SpatialFeatureMatcher(const SpatialMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
Definition: matching.cc:1251
static const int kMaxNumThreads
Definition: threading.h:173
const Timer & GetTimer() const
Definition: threading.cc:154
void SignalInvalidSetup()
Definition: threading.cc:146
void SignalValidSetup()
Definition: threading.cc:138
bool IsStopped()
Definition: threading.cc:97
void PrintMinutes() const
Definition: timer.cc:93
TransitiveFeatureMatcher(const TransitiveMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
Definition: matching.cc:1436
JobQueue< Output > * output_queue_
Definition: matching.h:325
FeatureMatcherCache * cache_
Definition: matching.h:323
TwoViewGeometryVerifier(const SiftMatchingOptions &options, FeatureMatcherCache *cache, JobQueue< Input > *input_queue, JobQueue< Output > *output_queue)
Definition: matching.cc:656
JobQueue< Input > * input_queue_
Definition: matching.h:324
TwoViewGeometry::Options two_view_geometry_options_
Definition: matching.h:322
const SiftMatchingOptions options_
Definition: matching.h:321
VocabTreeFeatureMatcher(const VocabTreeMatchingOptions &options, const SiftMatchingOptions &match_options, const std::string &database_path)
Definition: matching.cc:1171
const double * e
GraphType data
Definition: graph_cut.cc:138
#define CHECK_OPTION_GT(val1, val2)
Definition: logging.h:34
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
uint64_t image_pair_t
Definition: types.h:64
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)
Definition: sift.cc:1267
void StringTrim(std::string *str)
Definition: string.cc:188
void MatchSiftFeaturesGPU(const SiftMatchingOptions &match_options, const FeatureDescriptors *descriptors1, const FeatureDescriptors *descriptors2, SiftMatchGPU *sift_match_gpu, FeatureMatches *matches)
Definition: sift.cc:1195
void ExtractTopScaleFeatures(FeatureKeypoints *keypoints, FeatureDescriptors *descriptors, const size_t num_features)
Definition: utils.cc:79
bool CreateSiftGPUMatcher(const SiftMatchingOptions &match_options, SiftMatchGPU *sift_match_gpu)
Definition: sift.cc:1124
Eigen::Matrix< uint8_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > FeatureDescriptors
Definition: types.h:79
int GetNumCudaDevices()
Definition: cuda.cc:57
void MatchSiftFeaturesCPU(const SiftMatchingOptions &match_options, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, FeatureMatches *matches)
Definition: sift.cc:1042
void MatchGuidedSiftFeaturesCPU(const SiftMatchingOptions &match_options, const FeatureKeypoints &keypoints1, const FeatureKeypoints &keypoints2, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, TwoViewGeometry *two_view_geometry)
Definition: sift.cc:1050
uint32_t image_t
Definition: types.h:61
std::vector< Eigen::Vector2d > FeatureKeypointsToPointsVector(const FeatureKeypoints &keypoints)
Definition: utils.cc:38
void PrintHeading1(const std::string &heading)
Definition: misc.cc:225
std::vector< FeatureKeypoint > FeatureKeypoints
Definition: types.h:77
std::string StringPrintf(const char *format,...)
Definition: string.cc:131
const image_t kInvalidImageId
Definition: types.h:76
uint32_t camera_t
Definition: types.h:58
std::vector< FeatureMatch > FeatureMatches
Definition: types.h:80
int GetEffectiveNumThreads(const int num_threads)
Definition: threading.cc:269
std::filesystem::path MaybeDownloadAndCacheFile(const std::string &uri)
std::string to_string(const T &n)
Definition: Common.h:20
Definition: Eigen.h:85
CorePointDescSet * descriptors
size_t min_num_trials
Definition: ransac.h:40
double min_inlier_ratio
Definition: ransac.h:29
size_t max_num_trials
Definition: ransac.h:41
bool Check() const
Definition: sift.cc:403
std::string gpu_index
Definition: sift.h:109