| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include "ui/feature_matching_widget.h" |
| |
|
| | #include "feature/matching.h" |
| | #include "ui/options_widget.h" |
| | #include "ui/thread_control_widget.h" |
| |
|
| | namespace colmap { |
| |
|
| | class FeatureMatchingTab : public QWidget { |
| | public: |
| | FeatureMatchingTab(QWidget* parent, OptionManager* options); |
| |
|
| | virtual void Run() = 0; |
| |
|
| | protected: |
| | void CreateGeneralOptions(); |
| |
|
| | OptionManager* options_; |
| | OptionsWidget* options_widget_; |
| | QGridLayout* grid_layout_; |
| | ThreadControlWidget* thread_control_widget_; |
| | }; |
| |
|
| | class ExhaustiveMatchingTab : public FeatureMatchingTab { |
| | public: |
| | ExhaustiveMatchingTab(QWidget* parent, OptionManager* options); |
| | void Run() override; |
| | }; |
| |
|
| | class SequentialMatchingTab : public FeatureMatchingTab { |
| | public: |
| | SequentialMatchingTab(QWidget* parent, OptionManager* options); |
| | void Run() override; |
| | }; |
| |
|
| | class VocabTreeMatchingTab : public FeatureMatchingTab { |
| | public: |
| | VocabTreeMatchingTab(QWidget* parent, OptionManager* options); |
| | void Run() override; |
| | }; |
| |
|
| | class SpatialMatchingTab : public FeatureMatchingTab { |
| | public: |
| | SpatialMatchingTab(QWidget* parent, OptionManager* options); |
| | void Run() override; |
| | }; |
| |
|
| | class TransitiveMatchingTab : public FeatureMatchingTab { |
| | public: |
| | TransitiveMatchingTab(QWidget* parent, OptionManager* options); |
| | void Run() override; |
| | }; |
| |
|
| | class CustomMatchingTab : public FeatureMatchingTab { |
| | public: |
| | CustomMatchingTab(QWidget* parent, OptionManager* options); |
| | void Run() override; |
| |
|
| | private: |
| | std::string match_list_path_; |
| | QComboBox* match_type_cb_; |
| | }; |
| |
|
| | FeatureMatchingTab::FeatureMatchingTab(QWidget* parent, OptionManager* options) |
| | : QWidget(parent), |
| | options_(options), |
| | options_widget_(new OptionsWidget(this)), |
| | grid_layout_(new QGridLayout(this)), |
| | thread_control_widget_(new ThreadControlWidget(this)) {} |
| |
|
| | void FeatureMatchingTab::CreateGeneralOptions() { |
| | options_widget_->AddSpacer(); |
| | options_widget_->AddSpacer(); |
| | options_widget_->AddSection("General Options"); |
| | options_widget_->AddSpacer(); |
| |
|
| | options_widget_->AddOptionInt(&options_->sift_matching->num_threads, |
| | "num_threads", -1); |
| | options_widget_->AddOptionBool(&options_->sift_matching->use_gpu, "use_gpu"); |
| | options_widget_->AddOptionText(&options_->sift_matching->gpu_index, |
| | "gpu_index"); |
| | options_widget_->AddOptionDouble(&options_->sift_matching->max_ratio, |
| | "max_ratio"); |
| | options_widget_->AddOptionDouble(&options_->sift_matching->max_distance, |
| | "max_distance"); |
| | options_widget_->AddOptionBool(&options_->sift_matching->cross_check, |
| | "cross_check"); |
| | options_widget_->AddOptionInt(&options_->sift_matching->max_num_matches, |
| | "max_num_matches"); |
| | options_widget_->AddOptionDouble(&options_->sift_matching->max_error, |
| | "max_error"); |
| | options_widget_->AddOptionDouble(&options_->sift_matching->confidence, |
| | "confidence", 0, 1, 0.00001, 5); |
| | options_widget_->AddOptionInt(&options_->sift_matching->max_num_trials, |
| | "max_num_trials"); |
| | options_widget_->AddOptionDouble(&options_->sift_matching->min_inlier_ratio, |
| | "min_inlier_ratio", 0, 1, 0.001, 3); |
| | options_widget_->AddOptionInt(&options_->sift_matching->min_num_inliers, |
| | "min_num_inliers"); |
| | options_widget_->AddOptionBool(&options_->sift_matching->multiple_models, |
| | "multiple_models"); |
| | options_widget_->AddOptionBool(&options_->sift_matching->guided_matching, |
| | "guided_matching"); |
| | options_widget_->AddOptionBool(&options_->sift_matching->planar_scene, |
| | "planar_scene"); |
| | options_widget_->AddSpacer(); |
| |
|
| | QScrollArea* options_scroll_area = new QScrollArea(this); |
| | options_scroll_area->setAlignment(Qt::AlignHCenter); |
| | options_scroll_area->setWidget(options_widget_); |
| | grid_layout_->addWidget(options_scroll_area, grid_layout_->rowCount(), 0); |
| |
|
| | QPushButton* run_button = new QPushButton(tr("Run"), this); |
| | grid_layout_->addWidget(run_button, grid_layout_->rowCount(), 0); |
| | connect(run_button, &QPushButton::released, this, &FeatureMatchingTab::Run); |
| | } |
| |
|
| | ExhaustiveMatchingTab::ExhaustiveMatchingTab(QWidget* parent, |
| | OptionManager* options) |
| | : FeatureMatchingTab(parent, options) { |
| | options_widget_->AddOptionInt(&options_->exhaustive_matching->block_size, |
| | "block_size", 2); |
| |
|
| | CreateGeneralOptions(); |
| | } |
| |
|
| | void ExhaustiveMatchingTab::Run() { |
| | options_widget_->WriteOptions(); |
| |
|
| | auto matcher = std::make_unique<ExhaustiveFeatureMatcher>( |
| | *options_->exhaustive_matching, *options_->sift_matching, |
| | *options_->database_path); |
| | thread_control_widget_->StartThread("Matching...", true, std::move(matcher)); |
| | } |
| |
|
| | SequentialMatchingTab::SequentialMatchingTab(QWidget* parent, |
| | OptionManager* options) |
| | : FeatureMatchingTab(parent, options) { |
| | options_widget_->AddOptionInt(&options_->sequential_matching->overlap, |
| | "overlap"); |
| | options_widget_->AddOptionBool( |
| | &options_->sequential_matching->quadratic_overlap, "quadratic_overlap"); |
| | options_widget_->AddOptionBool(&options_->sequential_matching->loop_detection, |
| | "loop_detection"); |
| | options_widget_->AddOptionInt( |
| | &options_->sequential_matching->loop_detection_period, |
| | "loop_detection_period"); |
| | options_widget_->AddOptionInt( |
| | &options_->sequential_matching->loop_detection_num_images, |
| | "loop_detection_num_images"); |
| | options_widget_->AddOptionInt( |
| | &options_->sequential_matching->loop_detection_num_nearest_neighbors, |
| | "loop_detection_num_nearest_neighbors"); |
| | options_widget_->AddOptionInt( |
| | &options_->sequential_matching->loop_detection_num_checks, |
| | "loop_detection_num_checks", 1); |
| | options_widget_->AddOptionInt( |
| | &options_->sequential_matching |
| | ->loop_detection_num_images_after_verification, |
| | "loop_detection_num_images_after_verification", 0); |
| | options_widget_->AddOptionInt( |
| | &options_->sequential_matching->loop_detection_max_num_features, |
| | "loop_detection_max_num_features", -1); |
| | options_widget_->AddOptionFilePath( |
| | &options_->sequential_matching->vocab_tree_path, "vocab_tree_path"); |
| |
|
| | CreateGeneralOptions(); |
| | } |
| |
|
| | void SequentialMatchingTab::Run() { |
| | options_widget_->WriteOptions(); |
| |
|
| | if (options_->sequential_matching->loop_detection && |
| | !ExistsFile(options_->sequential_matching->vocab_tree_path)) { |
| | QMessageBox::critical(this, "", tr("Invalid vocabulary tree path.")); |
| | return; |
| | } |
| |
|
| | auto matcher = std::make_unique<SequentialFeatureMatcher>( |
| | *options_->sequential_matching, *options_->sift_matching, |
| | *options_->database_path); |
| | thread_control_widget_->StartThread("Matching...", true, std::move(matcher)); |
| | } |
| |
|
| | VocabTreeMatchingTab::VocabTreeMatchingTab(QWidget* parent, |
| | OptionManager* options) |
| | : FeatureMatchingTab(parent, options) { |
| | options_widget_->AddOptionInt(&options_->vocab_tree_matching->num_images, |
| | "num_images"); |
| | options_widget_->AddOptionInt( |
| | &options_->vocab_tree_matching->num_nearest_neighbors, |
| | "num_nearest_neighbors"); |
| | options_widget_->AddOptionInt(&options_->vocab_tree_matching->num_checks, |
| | "num_checks", 1); |
| | options_widget_->AddOptionInt( |
| | &options_->vocab_tree_matching->num_images_after_verification, |
| | "num_images_after_verification", 0); |
| | options_widget_->AddOptionInt( |
| | &options_->vocab_tree_matching->max_num_features, "max_num_features", -1); |
| | options_widget_->AddOptionFilePath( |
| | &options_->vocab_tree_matching->vocab_tree_path, "vocab_tree_path"); |
| |
|
| | CreateGeneralOptions(); |
| | } |
| |
|
| | void VocabTreeMatchingTab::Run() { |
| | options_widget_->WriteOptions(); |
| |
|
| | if (!ExistsFile(options_->vocab_tree_matching->vocab_tree_path)) { |
| | QMessageBox::critical(this, "", tr("Invalid vocabulary tree path.")); |
| | return; |
| | } |
| |
|
| | auto matcher = std::make_unique<VocabTreeFeatureMatcher>( |
| | *options_->vocab_tree_matching, *options_->sift_matching, |
| | *options_->database_path); |
| | thread_control_widget_->StartThread("Matching...", true, std::move(matcher)); |
| | } |
| |
|
| | SpatialMatchingTab::SpatialMatchingTab(QWidget* parent, OptionManager* options) |
| | : FeatureMatchingTab(parent, options) { |
| | options_widget_->AddOptionBool(&options_->spatial_matching->is_gps, "is_gps"); |
| | options_widget_->AddOptionBool(&options_->spatial_matching->ignore_z, |
| | "ignore_z"); |
| | options_widget_->AddOptionInt(&options_->spatial_matching->max_num_neighbors, |
| | "max_num_neighbors"); |
| | options_widget_->AddOptionDouble(&options_->spatial_matching->max_distance, |
| | "max_distance"); |
| |
|
| | CreateGeneralOptions(); |
| | } |
| |
|
| | void SpatialMatchingTab::Run() { |
| | options_widget_->WriteOptions(); |
| |
|
| | auto matcher = std::make_unique<SpatialFeatureMatcher>( |
| | *options_->spatial_matching, *options_->sift_matching, |
| | *options_->database_path); |
| | thread_control_widget_->StartThread("Matching...", true, std::move(matcher)); |
| | } |
| |
|
| | TransitiveMatchingTab::TransitiveMatchingTab(QWidget* parent, |
| | OptionManager* options) |
| | : FeatureMatchingTab(parent, options) { |
| | options_widget_->AddOptionInt(&options->transitive_matching->batch_size, |
| | "batch_size"); |
| | options_widget_->AddOptionInt(&options->transitive_matching->num_iterations, |
| | "num_iterations"); |
| |
|
| | CreateGeneralOptions(); |
| | } |
| |
|
| | void TransitiveMatchingTab::Run() { |
| | options_widget_->WriteOptions(); |
| |
|
| | auto matcher = std::make_unique<TransitiveFeatureMatcher>( |
| | *options_->transitive_matching, *options_->sift_matching, |
| | *options_->database_path); |
| | thread_control_widget_->StartThread("Matching...", true, std::move(matcher)); |
| | } |
| |
|
| | CustomMatchingTab::CustomMatchingTab(QWidget* parent, OptionManager* options) |
| | : FeatureMatchingTab(parent, options) { |
| | match_type_cb_ = new QComboBox(this); |
| | match_type_cb_->addItem(QString("Image pairs")); |
| | match_type_cb_->addItem(QString("Raw feature matches")); |
| | match_type_cb_->addItem(QString("Inlier feature matches")); |
| | options_widget_->AddOptionRow("type", match_type_cb_, nullptr); |
| |
|
| | options_widget_->AddOptionFilePath(&match_list_path_, "match_list_path"); |
| | options_widget_->AddOptionInt(&options_->image_pairs_matching->block_size, |
| | "block_size", 2); |
| |
|
| | CreateGeneralOptions(); |
| | } |
| |
|
| | void CustomMatchingTab::Run() { |
| | options_widget_->WriteOptions(); |
| |
|
| | if (!ExistsFile(match_list_path_)) { |
| | QMessageBox::critical(this, "", tr("Path does not exist!")); |
| | return; |
| | } |
| |
|
| | std::unique_ptr<Thread> matcher; |
| | if (match_type_cb_->currentIndex() == 0) { |
| | ImagePairsMatchingOptions matcher_options; |
| | matcher_options.match_list_path = match_list_path_; |
| | matcher = std::make_unique<ImagePairsFeatureMatcher>( |
| | matcher_options, *options_->sift_matching, *options_->database_path); |
| | } else { |
| | FeaturePairsMatchingOptions matcher_options; |
| | matcher_options.match_list_path = match_list_path_; |
| | if (match_type_cb_->currentIndex() == 1) { |
| | matcher_options.verify_matches = true; |
| | } else if (match_type_cb_->currentIndex() == 2) { |
| | matcher_options.verify_matches = false; |
| | } |
| |
|
| | matcher = std::make_unique<FeaturePairsFeatureMatcher>( |
| | matcher_options, *options_->sift_matching, *options_->database_path); |
| | } |
| |
|
| | thread_control_widget_->StartThread("Matching...", true, std::move(matcher)); |
| | } |
| |
|
| | FeatureMatchingWidget::FeatureMatchingWidget(QWidget* parent, |
| | OptionManager* options) |
| | : parent_(parent) { |
| | |
| | |
| | setWindowFlags(Qt::Window); |
| | setWindowTitle("Feature matching"); |
| |
|
| | QGridLayout* grid = new QGridLayout(this); |
| |
|
| | tab_widget_ = new QTabWidget(this); |
| | tab_widget_->addTab(new ExhaustiveMatchingTab(this, options), |
| | tr("Exhaustive")); |
| | tab_widget_->addTab(new SequentialMatchingTab(this, options), |
| | tr("Sequential")); |
| | tab_widget_->addTab(new VocabTreeMatchingTab(this, options), tr("VocabTree")); |
| | tab_widget_->addTab(new SpatialMatchingTab(this, options), tr("Spatial")); |
| | tab_widget_->addTab(new TransitiveMatchingTab(this, options), |
| | tr("Transitive")); |
| | tab_widget_->addTab(new CustomMatchingTab(this, options), tr("Custom")); |
| |
|
| | grid->addWidget(tab_widget_, 0, 0); |
| | } |
| |
|
| | void FeatureMatchingWidget::showEvent(QShowEvent* event) { |
| | parent_->setDisabled(true); |
| | } |
| |
|
| | void FeatureMatchingWidget::hideEvent(QHideEvent* event) { |
| | parent_->setEnabled(true); |
| | } |
| |
|
| | } |
| |
|