36 #include "util/version.h"
43 window_closed_(false) {
44 std::setlocale(LC_NUMERIC,
"C");
61 const size_t idx = reconstruction_manager_.
Read(
path);
62 reconstruction_manager_widget_->
Update();
75 QMessageBox::StandardButton reply;
76 reply = QMessageBox::question(
78 tr(
"You have not saved your project. Do you want to save it?"),
79 QMessageBox::Yes | QMessageBox::No);
80 if (reply == QMessageBox::Yes) {
85 QMessageBox::StandardButton reply;
86 reply = QMessageBox::question(
this,
"", tr(
"Do you really want to quit?"),
87 QMessageBox::Yes | QMessageBox::No);
88 if (reply == QMessageBox::No) {
91 if (mapper_controller_) {
92 mapper_controller_->Stop();
93 mapper_controller_->Wait();
99 window_closed_ =
true;
103 void MainWindow::CreateWidgets() {
105 setCentralWidget(model_viewer_widget_);
115 reconstruction_options_widget_ =
119 render_options_widget_ =
123 reconstruction_manager_widget_ =
129 dock_log_widget_ =
new QDockWidget(
"Log",
this);
130 dock_log_widget_->setWidget(log_widget_);
131 addDockWidget(Qt::RightDockWidgetArea, dock_log_widget_);
134 void MainWindow::CreateActions() {
139 action_project_new_ =
140 new QAction(QIcon(
":/media/project-new.png"), tr(
"New project"),
this);
141 action_project_new_->setShortcuts(QKeySequence::New);
142 connect(action_project_new_, &QAction::triggered,
this,
143 &MainWindow::ProjectNew);
145 action_project_open_ =
146 new QAction(QIcon(
":/media/project-open.png"), tr(
"Open project"),
this);
147 action_project_open_->setShortcuts(QKeySequence::Open);
148 connect(action_project_open_, &QAction::triggered,
this,
149 &MainWindow::ProjectOpen);
151 action_project_edit_ =
152 new QAction(QIcon(
":/media/project-edit.png"), tr(
"Edit project"),
this);
153 connect(action_project_edit_, &QAction::triggered,
this,
154 &MainWindow::ProjectEdit);
156 action_project_save_ =
157 new QAction(QIcon(
":/media/project-save.png"), tr(
"Save project"),
this);
158 action_project_save_->setShortcuts(QKeySequence::Save);
159 connect(action_project_save_, &QAction::triggered,
this,
160 &MainWindow::ProjectSave);
162 action_project_save_as_ =
new QAction(QIcon(
":/media/project-save-as.png"),
163 tr(
"Save project as..."),
this);
164 action_project_save_as_->setShortcuts(QKeySequence::SaveAs);
165 connect(action_project_save_as_, &QAction::triggered,
this,
166 &MainWindow::ProjectSaveAs);
169 new QAction(QIcon(
":/media/import.png"), tr(
"Import model"),
this);
170 connect(action_import_, &QAction::triggered,
this, &MainWindow::Import);
171 blocking_actions_.push_back(action_import_);
173 action_import_from_ =
new QAction(QIcon(
":/media/import-from.png"),
174 tr(
"Import model from..."),
this);
175 connect(action_import_from_, &QAction::triggered,
this,
176 &MainWindow::ImportFrom);
177 blocking_actions_.push_back(action_import_from_);
180 new QAction(QIcon(
":/media/export.png"), tr(
"Export model"),
this);
181 connect(action_export_, &QAction::triggered,
this, &MainWindow::Export);
182 blocking_actions_.push_back(action_export_);
184 action_export_all_ =
new QAction(QIcon(
":/media/export-all.png"),
185 tr(
"Export all models"),
this);
186 connect(action_export_all_, &QAction::triggered,
this,
187 &MainWindow::ExportAll);
188 blocking_actions_.push_back(action_export_all_);
190 action_export_as_ =
new QAction(QIcon(
":/media/export-as.png"),
191 tr(
"Export model as..."),
this);
192 connect(action_export_as_, &QAction::triggered,
this, &MainWindow::ExportAs);
193 blocking_actions_.push_back(action_export_as_);
195 action_export_as_text_ =
new QAction(QIcon(
":/media/export-as-text.png"),
196 tr(
"Export model as text"),
this);
197 connect(action_export_as_text_, &QAction::triggered,
this,
198 &MainWindow::ExportAsText);
199 blocking_actions_.push_back(action_export_as_text_);
201 action_quit_ =
new QAction(tr(
"Quit"),
this);
202 connect(action_quit_, &QAction::triggered,
this, &MainWindow::close);
208 action_feature_extraction_ =
new QAction(
209 QIcon(
":/media/feature-extraction.png"), tr(
"Feature extraction"),
this);
210 connect(action_feature_extraction_, &QAction::triggered,
this,
211 &MainWindow::FeatureExtraction);
212 blocking_actions_.push_back(action_feature_extraction_);
214 action_feature_matching_ =
new QAction(QIcon(
":/media/feature-matching.png"),
215 tr(
"Feature matching"),
this);
216 connect(action_feature_matching_, &QAction::triggered,
this,
217 &MainWindow::FeatureMatching);
218 blocking_actions_.push_back(action_feature_matching_);
220 action_database_management_ =
221 new QAction(QIcon(
":/media/database-management.png"),
222 tr(
"Database management"),
this);
223 connect(action_database_management_, &QAction::triggered,
this,
224 &MainWindow::DatabaseManagement);
225 blocking_actions_.push_back(action_database_management_);
231 action_automatic_reconstruction_ =
232 new QAction(QIcon(
":/media/automatic-reconstruction.png"),
233 tr(
"Automatic reconstruction"),
this);
234 connect(action_automatic_reconstruction_, &QAction::triggered,
this,
235 &MainWindow::AutomaticReconstruction);
237 action_reconstruction_start_ =
238 new QAction(QIcon(
":/media/reconstruction-start.png"),
239 tr(
"Start reconstruction"),
this);
240 connect(action_reconstruction_start_, &QAction::triggered,
this,
241 &MainWindow::ReconstructionStart);
242 blocking_actions_.push_back(action_reconstruction_start_);
244 action_reconstruction_step_ =
245 new QAction(QIcon(
":/media/reconstruction-step.png"),
246 tr(
"Reconstruct next image"),
this);
247 connect(action_reconstruction_step_, &QAction::triggered,
this,
248 &MainWindow::ReconstructionStep);
249 blocking_actions_.push_back(action_reconstruction_step_);
251 action_reconstruction_pause_ =
252 new QAction(QIcon(
":/media/reconstruction-pause.png"),
253 tr(
"Pause reconstruction"),
this);
254 connect(action_reconstruction_pause_, &QAction::triggered,
this,
255 &MainWindow::ReconstructionPause);
256 action_reconstruction_pause_->setEnabled(
false);
257 blocking_actions_.push_back(action_reconstruction_pause_);
259 action_reconstruction_reset_ =
260 new QAction(QIcon(
":/media/reconstruction-reset.png"),
261 tr(
"Reset reconstruction"),
this);
262 connect(action_reconstruction_reset_, &QAction::triggered,
this,
263 &MainWindow::ReconstructionOverwrite);
265 action_reconstruction_normalize_ =
266 new QAction(QIcon(
":/media/reconstruction-normalize.png"),
267 tr(
"Normalize reconstruction"),
this);
268 connect(action_reconstruction_normalize_, &QAction::triggered,
this,
269 &MainWindow::ReconstructionNormalize);
270 blocking_actions_.push_back(action_reconstruction_normalize_);
272 action_reconstruction_options_ =
273 new QAction(QIcon(
":/media/reconstruction-options.png"),
274 tr(
"Reconstruction options"),
this);
275 connect(action_reconstruction_options_, &QAction::triggered,
this,
276 &MainWindow::ReconstructionOptions);
277 blocking_actions_.push_back(action_reconstruction_options_);
279 action_bundle_adjustment_ =
new QAction(
280 QIcon(
":/media/bundle-adjustment.png"), tr(
"Bundle adjustment"),
this);
281 connect(action_bundle_adjustment_, &QAction::triggered,
this,
282 &MainWindow::BundleAdjustment);
283 action_bundle_adjustment_->setEnabled(
false);
284 blocking_actions_.push_back(action_bundle_adjustment_);
286 action_dense_reconstruction_ =
287 new QAction(QIcon(
":/media/dense-reconstruction.png"),
288 tr(
"Dense reconstruction"),
this);
289 connect(action_dense_reconstruction_, &QAction::triggered,
this,
290 &MainWindow::DenseReconstruction);
296 action_render_toggle_ =
new QAction(QIcon(
":/media/render-enabled.png"),
297 tr(
"Disable rendering"),
this);
298 connect(action_render_toggle_, &QAction::triggered,
this,
299 &MainWindow::RenderToggle);
301 action_render_reset_view_ =
new QAction(
302 QIcon(
":/media/render-reset-view.png"), tr(
"Reset view"),
this);
303 connect(action_render_reset_view_, &QAction::triggered, model_viewer_widget_,
306 action_render_options_ =
new QAction(QIcon(
":/media/render-options.png"),
307 tr(
"Render options"),
this);
308 connect(action_render_options_, &QAction::triggered,
this,
309 &MainWindow::RenderOptions);
312 reconstruction_manager_widget_,
313 static_cast<void (QComboBox::*)(
int)
>(&QComboBox::currentIndexChanged),
314 this, &MainWindow::SelectReconstructionIdx);
320 action_reconstruction_stats_ =
321 new QAction(QIcon(
":/media/reconstruction-stats.png"),
322 tr(
"Show model statistics"),
this);
323 connect(action_reconstruction_stats_, &QAction::triggered,
this,
324 &MainWindow::ReconstructionStats);
326 action_match_matrix_ =
new QAction(QIcon(
":/media/match-matrix.png"),
327 tr(
"Show match matrix"),
this);
328 connect(action_match_matrix_, &QAction::triggered,
this,
329 &MainWindow::MatchMatrix);
332 new QAction(QIcon(
":/media/log.png"), tr(
"Show log"),
this);
333 connect(action_log_show_, &QAction::triggered,
this, &MainWindow::ShowLog);
336 new QAction(QIcon(
":/media/grab-image.png"), tr(
"Grab image"),
this);
337 connect(action_grab_image_, &QAction::triggered,
this,
338 &MainWindow::GrabImage);
341 new QAction(QIcon(
":/media/grab-movie.png"), tr(
"Grab movie"),
this);
342 connect(action_grab_movie_, &QAction::triggered, model_viewer_widget_,
346 new QAction(QIcon(
":/media/undistort.png"), tr(
"Undistortion"),
this);
347 connect(action_undistort_, &QAction::triggered,
this,
348 &MainWindow::UndistortImages);
349 blocking_actions_.push_back(action_undistort_);
351 action_extract_colors_ =
new QAction(tr(
"Extract colors"),
this);
352 connect(action_extract_colors_, &QAction::triggered,
this,
353 &MainWindow::ExtractColors);
355 action_set_options_ =
new QAction(tr(
"Set options for ..."),
this);
356 connect(action_set_options_, &QAction::triggered,
this,
357 &MainWindow::SetOptions);
359 action_reset_options_ =
new QAction(tr(
"Set default options"),
this);
360 connect(action_reset_options_, &QAction::triggered,
this,
361 &MainWindow::ResetOptions);
367 action_render_ =
new QAction(tr(
"Render"),
this);
368 connect(action_render_, &QAction::triggered,
this, &MainWindow::Render,
369 Qt::BlockingQueuedConnection);
371 action_render_now_ =
new QAction(tr(
"Render now"),
this);
373 connect(action_render_now_, &QAction::triggered,
this, &MainWindow::RenderNow,
374 Qt::BlockingQueuedConnection);
376 action_reconstruction_finish_ =
377 new QAction(tr(
"Finish reconstruction"),
this);
378 connect(action_reconstruction_finish_, &QAction::triggered,
this,
379 &MainWindow::ReconstructionFinish, Qt::BlockingQueuedConnection);
381 action_about_ =
new QAction(tr(
"About"),
this);
382 connect(action_about_, &QAction::triggered,
this, &MainWindow::About);
383 action_documentation_ =
new QAction(tr(
"Documentation"),
this);
384 connect(action_documentation_, &QAction::triggered,
this,
385 &MainWindow::Documentation);
386 action_support_ =
new QAction(tr(
"Support"),
this);
387 connect(action_support_, &QAction::triggered,
this, &MainWindow::Support);
388 action_license_ =
new QAction(tr(
"License"),
this);
389 connect(action_license_, &QAction::triggered, license_widget_,
393 void MainWindow::CreateMenus() {
394 QMenu* file_menu =
new QMenu(tr(
"File"),
this);
395 file_menu->addAction(action_project_new_);
396 file_menu->addAction(action_project_open_);
397 file_menu->addAction(action_project_edit_);
398 file_menu->addAction(action_project_save_);
399 file_menu->addAction(action_project_save_as_);
400 file_menu->addSeparator();
401 file_menu->addAction(action_import_);
402 file_menu->addAction(action_import_from_);
403 file_menu->addSeparator();
404 file_menu->addAction(action_export_);
405 file_menu->addAction(action_export_all_);
406 file_menu->addAction(action_export_as_);
407 file_menu->addAction(action_export_as_text_);
408 file_menu->addSeparator();
409 file_menu->addAction(action_quit_);
410 menuBar()->addAction(file_menu->menuAction());
412 QMenu* preprocessing_menu =
new QMenu(tr(
"Processing"),
this);
413 preprocessing_menu->addAction(action_feature_extraction_);
414 preprocessing_menu->addAction(action_feature_matching_);
415 preprocessing_menu->addAction(action_database_management_);
416 menuBar()->addAction(preprocessing_menu->menuAction());
418 QMenu* reconstruction_menu =
new QMenu(tr(
"Reconstruction"),
this);
419 reconstruction_menu->addAction(action_automatic_reconstruction_);
420 reconstruction_menu->addSeparator();
421 reconstruction_menu->addAction(action_reconstruction_start_);
422 reconstruction_menu->addAction(action_reconstruction_pause_);
423 reconstruction_menu->addAction(action_reconstruction_step_);
424 reconstruction_menu->addSeparator();
425 reconstruction_menu->addAction(action_reconstruction_reset_);
426 reconstruction_menu->addAction(action_reconstruction_normalize_);
427 reconstruction_menu->addAction(action_reconstruction_options_);
428 reconstruction_menu->addSeparator();
429 reconstruction_menu->addAction(action_bundle_adjustment_);
430 reconstruction_menu->addAction(action_dense_reconstruction_);
431 menuBar()->addAction(reconstruction_menu->menuAction());
433 QMenu* render_menu =
new QMenu(tr(
"Render"),
this);
434 render_menu->addAction(action_render_toggle_);
435 render_menu->addAction(action_render_reset_view_);
436 render_menu->addAction(action_render_options_);
437 menuBar()->addAction(render_menu->menuAction());
439 QMenu* extras_menu =
new QMenu(tr(
"Extras"),
this);
440 extras_menu->addAction(action_log_show_);
441 extras_menu->addAction(action_match_matrix_);
442 extras_menu->addAction(action_reconstruction_stats_);
443 extras_menu->addSeparator();
444 extras_menu->addAction(action_grab_image_);
445 extras_menu->addAction(action_grab_movie_);
446 extras_menu->addSeparator();
447 extras_menu->addAction(action_undistort_);
448 extras_menu->addAction(action_extract_colors_);
449 extras_menu->addSeparator();
450 extras_menu->addAction(action_set_options_);
451 extras_menu->addAction(action_reset_options_);
452 menuBar()->addAction(extras_menu->menuAction());
454 QMenu* help_menu =
new QMenu(tr(
"Help"),
this);
455 help_menu->addAction(action_about_);
456 help_menu->addAction(action_documentation_);
457 help_menu->addAction(action_support_);
458 help_menu->addAction(action_license_);
459 menuBar()->addAction(help_menu->menuAction());
464 menuBar()->setNativeMenuBar(
false);
467 void MainWindow::CreateToolbar() {
468 file_toolbar_ = addToolBar(tr(
"File"));
469 file_toolbar_->addAction(action_project_new_);
470 file_toolbar_->addAction(action_project_open_);
471 file_toolbar_->addAction(action_project_edit_);
472 file_toolbar_->addAction(action_project_save_);
473 file_toolbar_->addAction(action_import_);
474 file_toolbar_->addAction(action_export_);
475 file_toolbar_->setIconSize(QSize(16, 16));
477 preprocessing_toolbar_ = addToolBar(tr(
"Processing"));
478 preprocessing_toolbar_->addAction(action_feature_extraction_);
479 preprocessing_toolbar_->addAction(action_feature_matching_);
480 preprocessing_toolbar_->addAction(action_database_management_);
481 preprocessing_toolbar_->setIconSize(QSize(16, 16));
483 reconstruction_toolbar_ = addToolBar(tr(
"Reconstruction"));
484 reconstruction_toolbar_->addAction(action_automatic_reconstruction_);
485 reconstruction_toolbar_->addAction(action_reconstruction_start_);
486 reconstruction_toolbar_->addAction(action_reconstruction_step_);
487 reconstruction_toolbar_->addAction(action_reconstruction_pause_);
488 reconstruction_toolbar_->addAction(action_reconstruction_options_);
489 reconstruction_toolbar_->addAction(action_bundle_adjustment_);
490 reconstruction_toolbar_->addAction(action_dense_reconstruction_);
491 reconstruction_toolbar_->setIconSize(QSize(16, 16));
493 render_toolbar_ = addToolBar(tr(
"Render"));
494 render_toolbar_->addAction(action_render_toggle_);
495 render_toolbar_->addAction(action_render_reset_view_);
496 render_toolbar_->addAction(action_render_options_);
497 render_toolbar_->addWidget(reconstruction_manager_widget_);
498 render_toolbar_->setIconSize(QSize(16, 16));
500 extras_toolbar_ = addToolBar(tr(
"Extras"));
501 extras_toolbar_->addAction(action_log_show_);
502 extras_toolbar_->addAction(action_match_matrix_);
503 extras_toolbar_->addAction(action_reconstruction_stats_);
504 extras_toolbar_->addAction(action_grab_image_);
505 extras_toolbar_->addAction(action_grab_movie_);
506 extras_toolbar_->setIconSize(QSize(16, 16));
509 void MainWindow::CreateStatusbar() {
511 font.setPointSize(11);
513 statusbar_timer_label_ =
new QLabel(
"Time 00:00:00:00",
this);
514 statusbar_timer_label_->setFont(font);
515 statusbar_timer_label_->setAlignment(Qt::AlignCenter);
516 statusBar()->addWidget(statusbar_timer_label_, 1);
517 statusbar_timer_ =
new QTimer(
this);
518 connect(statusbar_timer_, &QTimer::timeout,
this, &MainWindow::UpdateTimer);
519 statusbar_timer_->start(1000);
522 new QLabel(
"0 Images - 0 Points",
this);
528 void MainWindow::CreateControllers() {
529 if (mapper_controller_) {
530 mapper_controller_->Stop();
531 mapper_controller_->Wait();
536 &reconstruction_manager_));
537 mapper_controller_->AddCallback(
539 if (!mapper_controller_->IsStopped()) {
540 action_render_now_->trigger();
543 mapper_controller_->AddCallback(
545 if (!mapper_controller_->IsStopped()) {
546 action_render_->trigger();
549 mapper_controller_->AddCallback(
551 if (!mapper_controller_->IsStopped()) {
552 action_render_now_->trigger();
555 mapper_controller_->AddCallback(
557 if (!mapper_controller_->IsStopped()) {
558 action_render_now_->trigger();
559 action_reconstruction_finish_->trigger();
561 if (reconstruction_manager_.
Size() == 0) {
562 action_reconstruction_reset_->trigger();
567 void MainWindow::ProjectNew() {
568 if (ReconstructionOverwrite()) {
569 project_widget_->
Reset();
570 project_widget_->show();
571 project_widget_->raise();
575 bool MainWindow::ProjectOpen() {
576 if (!ReconstructionOverwrite()) {
580 const std::string project_path =
581 QFileDialog::getOpenFileName(
this, tr(
"Select project file"),
"",
582 tr(
"Project file (*.ini)"))
586 if (project_path !=
"") {
587 if (options_.
ReRead(project_path)) {
594 ShowInvalidProjectError();
601 void MainWindow::ProjectEdit() {
602 project_widget_->show();
603 project_widget_->raise();
606 void MainWindow::ProjectSave() {
608 std::string project_path =
609 QFileDialog::getSaveFileName(
this, tr(
"Select project file"),
"",
610 tr(
"Project file (*.ini)"))
614 if (project_path !=
"") {
616 project_path +=
".ini";
629 void MainWindow::ProjectSaveAs() {
630 const std::string new_project_path =
631 QFileDialog::getSaveFileName(
this, tr(
"Select project file"),
"",
632 tr(
"Project file (*.ini)"))
635 if (new_project_path !=
"") {
643 void MainWindow::Import() {
644 const std::string import_path =
645 QFileDialog::getExistingDirectory(
this, tr(
"Select source..."),
"",
646 QFileDialog::ShowDirsOnly)
651 if (import_path ==
"") {
655 const std::string project_path =
JoinPaths(import_path,
"project.ini");
656 const std::string cameras_bin_path =
JoinPaths(import_path,
"cameras.bin");
657 const std::string images_bin_path =
JoinPaths(import_path,
"images.bin");
658 const std::string points3D_bin_path =
JoinPaths(import_path,
"points3D.bin");
659 const std::string cameras_txt_path =
JoinPaths(import_path,
"cameras.txt");
660 const std::string images_txt_path =
JoinPaths(import_path,
"images.txt");
661 const std::string points3D_txt_path =
JoinPaths(import_path,
"points3D.txt");
667 QMessageBox::critical(
this,
"",
668 tr(
"cameras, images, and points3D files do not exist "
669 "in chosen directory."));
673 if (!ReconstructionOverwrite()) {
677 bool edit_project =
false;
679 options_.
ReRead(project_path);
681 QMessageBox::StandardButton reply = QMessageBox::question(
683 tr(
"Directory does not contain a <i>project.ini</i>. To "
684 "resume the reconstruction, you need to specify a valid "
685 "database and image path. Do you want to select the paths "
686 "now (or press <i>No</i> to only visualize the reconstruction)?"),
687 QMessageBox::Yes | QMessageBox::No);
688 if (reply == QMessageBox::Yes) {
694 "Importing...", [
this, import_path, edit_project]() {
695 const size_t idx = reconstruction_manager_.
Read(import_path);
696 reconstruction_manager_widget_->
Update();
698 action_bundle_adjustment_->setEnabled(
true);
699 action_render_now_->trigger();
701 action_project_edit_->trigger();
706 void MainWindow::ImportFrom() {
707 const std::string import_path =
708 QFileDialog::getOpenFileName(
this, tr(
"Select source..."),
"")
713 if (import_path ==
"") {
718 QMessageBox::critical(
this,
"", tr(
"Invalid file"));
723 QMessageBox::critical(
this,
"",
724 tr(
"Invalid file format (supported formats: PLY)"));
728 thread_control_widget_->
StartFunction(
"Importing...", [
this, import_path]() {
729 const size_t reconstruction_idx = reconstruction_manager_.
Add();
730 reconstruction_manager_.
Get(reconstruction_idx).ImportPLY(import_path);
731 options_.
render->min_track_len = 0;
732 reconstruction_manager_widget_->
Update();
734 action_render_now_->trigger();
738 void MainWindow::Export() {
739 if (!IsSelectedReconstructionValid()) {
743 const std::string export_path =
744 QFileDialog::getExistingDirectory(
this, tr(
"Select destination..."),
"",
745 QFileDialog::ShowDirsOnly)
750 if (export_path ==
"") {
754 const std::string cameras_name =
"cameras.bin";
755 const std::string images_name =
"images.bin";
756 const std::string points3D_name =
"points3D.bin";
758 const std::string project_path =
JoinPaths(export_path,
"project.ini");
759 const std::string cameras_path =
JoinPaths(export_path, cameras_name);
760 const std::string images_path =
JoinPaths(export_path, images_name);
761 const std::string points3D_path =
JoinPaths(export_path, points3D_name);
765 QMessageBox::StandardButton reply = QMessageBox::question(
768 "The files <i>%s</i>, <i>%s</i>, or <i>%s</i> already "
769 "exist in the selected destination. Do you want to overwrite them?",
770 cameras_name.c_str(), images_name.c_str(), points3D_name.c_str())
772 QMessageBox::Yes | QMessageBox::No);
773 if (reply == QMessageBox::No) {
779 "Exporting...", [
this, export_path, project_path]() {
780 const auto& reconstruction =
781 reconstruction_manager_.
Get(SelectedReconstructionIdx());
783 options_.
Write(project_path);
787 void MainWindow::ExportAll() {
788 if (!IsSelectedReconstructionValid()) {
792 const std::string export_path =
793 QFileDialog::getExistingDirectory(
this, tr(
"Select destination..."),
"",
794 QFileDialog::ShowDirsOnly)
799 if (export_path ==
"") {
803 thread_control_widget_->
StartFunction(
"Exporting...", [
this, export_path]() {
804 reconstruction_manager_.
Write(export_path, &options_);
808 void MainWindow::ExportAs() {
809 if (!IsSelectedReconstructionValid()) {
813 QString filter(
"NVM (*.nvm)");
814 const std::string export_path =
815 QFileDialog::getSaveFileName(
816 this, tr(
"Select destination..."),
"",
817 "NVM (*.nvm);;Bundler (*.out);;PLY (*.ply);;VRML (*.wrl)", &filter)
822 if (export_path ==
"") {
827 "Exporting...", [
this, export_path, filter]() {
828 const Reconstruction& reconstruction =
829 reconstruction_manager_.
Get(SelectedReconstructionIdx());
830 if (filter ==
"NVM (*.nvm)") {
832 }
else if (filter ==
"Bundler (*.out)") {
833 reconstruction.ExportBundler(export_path, export_path +
".list.txt");
834 }
else if (filter ==
"PLY (*.ply)") {
835 reconstruction.ExportPLY(export_path);
836 }
else if (filter ==
"VRML (*.wrl)") {
837 const auto base_path =
838 export_path.substr(0, export_path.find_last_of(
"."));
839 reconstruction.ExportVRML(base_path +
".images.wrl",
840 base_path +
".points3D.wrl", 1,
841 Eigen::Vector3d(1, 0, 0));
846 void MainWindow::ExportAsText() {
847 if (!IsSelectedReconstructionValid()) {
851 const std::string export_path =
852 QFileDialog::getExistingDirectory(
this, tr(
"Select destination..."),
"",
853 QFileDialog::ShowDirsOnly)
858 if (export_path ==
"") {
862 const std::string cameras_name =
"cameras.txt";
863 const std::string images_name =
"images.txt";
864 const std::string points3D_name =
"points3D.txt";
866 const std::string project_path =
JoinPaths(export_path,
"project.ini");
867 const std::string cameras_path =
JoinPaths(export_path, cameras_name);
868 const std::string images_path =
JoinPaths(export_path, images_name);
869 const std::string points3D_path =
JoinPaths(export_path, points3D_name);
873 QMessageBox::StandardButton reply = QMessageBox::question(
876 "The files <i>%s</i>, <i>%s</i>, or <i>%s</i> already "
877 "exist in the selected destination. Do you want to overwrite them?",
878 cameras_name.c_str(), images_name.c_str(), points3D_name.c_str())
880 QMessageBox::Yes | QMessageBox::No);
881 if (reply == QMessageBox::No) {
887 "Exporting...", [
this, export_path, project_path]() {
888 const auto& reconstruction =
889 reconstruction_manager_.
Get(SelectedReconstructionIdx());
891 options_.
Write(project_path);
895 void MainWindow::FeatureExtraction() {
896 if (options_.
Check()) {
897 feature_extraction_widget_->show();
898 feature_extraction_widget_->raise();
900 ShowInvalidProjectError();
904 void MainWindow::FeatureMatching() {
905 if (options_.
Check()) {
906 feature_matching_widget_->show();
907 feature_matching_widget_->raise();
909 ShowInvalidProjectError();
913 void MainWindow::DatabaseManagement() {
914 if (options_.
Check()) {
915 database_management_widget_->show();
916 database_management_widget_->raise();
918 ShowInvalidProjectError();
922 void MainWindow::AutomaticReconstruction() {
923 automatic_reconstruction_widget_->show();
924 automatic_reconstruction_widget_->raise();
927 void MainWindow::ReconstructionStart() {
928 if (!mapper_controller_->IsStarted() && !options_.
Check()) {
929 ShowInvalidProjectError();
933 if (mapper_controller_->IsFinished() && HasSelectedReconstruction()) {
934 QMessageBox::critical(
this,
"",
935 tr(
"Reset reconstruction before starting."));
939 if (mapper_controller_->IsStarted()) {
942 mapper_controller_->Resume();
947 mapper_controller_->Start();
948 action_reconstruction_start_->setText(tr(
"Resume reconstruction"));
951 DisableBlockingActions();
952 action_reconstruction_pause_->setEnabled(
true);
955 void MainWindow::ReconstructionStep() {
956 if (mapper_controller_->IsFinished() && HasSelectedReconstruction()) {
957 QMessageBox::critical(
this,
"",
958 tr(
"Reset reconstruction before starting."));
962 action_reconstruction_step_->setEnabled(
false);
963 ReconstructionStart();
964 ReconstructionPause();
965 action_reconstruction_step_->setEnabled(
true);
968 void MainWindow::ReconstructionPause() {
970 mapper_controller_->Pause();
971 EnableBlockingActions();
972 action_reconstruction_pause_->setEnabled(
false);
975 void MainWindow::ReconstructionOptions() {
976 reconstruction_options_widget_->show();
977 reconstruction_options_widget_->raise();
980 void MainWindow::ReconstructionFinish() {
982 mapper_controller_->Stop();
983 EnableBlockingActions();
984 action_reconstruction_start_->setEnabled(
false);
985 action_reconstruction_step_->setEnabled(
false);
986 action_reconstruction_pause_->setEnabled(
false);
989 void MainWindow::ReconstructionReset() {
992 reconstruction_manager_.
Clear();
993 reconstruction_manager_widget_->
Update();
998 EnableBlockingActions();
999 action_reconstruction_start_->setText(tr(
"Start reconstruction"));
1000 action_reconstruction_pause_->setEnabled(
false);
1005 void MainWindow::ReconstructionNormalize() {
1006 if (!IsSelectedReconstructionValid()) {
1009 action_reconstruction_step_->setEnabled(
false);
1010 reconstruction_manager_.
Get(SelectedReconstructionIdx()).
Normalize();
1011 action_reconstruction_step_->setEnabled(
true);
1014 bool MainWindow::ReconstructionOverwrite() {
1015 if (reconstruction_manager_.
Size() == 0) {
1016 ReconstructionReset();
1020 QMessageBox::StandardButton reply = QMessageBox::question(
1022 tr(
"Do you really want to overwrite the existing reconstruction?"),
1023 QMessageBox::Yes | QMessageBox::No);
1024 if (reply == QMessageBox::No) {
1027 ReconstructionReset();
1032 void MainWindow::BundleAdjustment() {
1033 if (!IsSelectedReconstructionValid()) {
1037 bundle_adjustment_widget_->
Show(
1038 &reconstruction_manager_.
Get(SelectedReconstructionIdx()));
1041 void MainWindow::DenseReconstruction() {
1042 if (HasSelectedReconstruction()) {
1043 dense_reconstruction_widget_->
Show(
1044 &reconstruction_manager_.
Get(SelectedReconstructionIdx()));
1046 dense_reconstruction_widget_->
Show(
nullptr);
1050 void MainWindow::Render() {
1051 if (reconstruction_manager_.
Size() == 0) {
1055 const Reconstruction& reconstruction =
1056 reconstruction_manager_.
Get(SelectedReconstructionIdx());
1059 if (options_.
render->adapt_refresh_rate) {
1060 refresh_rate =
static_cast<int>(reconstruction.NumRegImages() / 50 + 1);
1062 refresh_rate = options_.
render->refresh_rate;
1066 render_options_widget_->
counter % refresh_rate != 0) {
1067 render_options_widget_->
counter += 1;
1071 render_options_widget_->
counter += 1;
1076 void MainWindow::RenderNow() {
1077 reconstruction_manager_widget_->
Update();
1078 RenderSelectedReconstruction();
1081 void MainWindow::RenderSelectedReconstruction() {
1082 if (reconstruction_manager_.
Size() == 0) {
1087 const size_t reconstruction_idx = SelectedReconstructionIdx();
1089 &reconstruction_manager_.
Get(reconstruction_idx);
1093 void MainWindow::RenderClear() {
1099 void MainWindow::RenderOptions() {
1100 render_options_widget_->show();
1101 render_options_widget_->raise();
1104 void MainWindow::SelectReconstructionIdx(
const size_t) {
1105 RenderSelectedReconstruction();
1108 size_t MainWindow::SelectedReconstructionIdx() {
1109 size_t reconstruction_idx =
1111 if (reconstruction_idx ==
1113 if (reconstruction_manager_.
Size() > 0) {
1114 reconstruction_idx = reconstruction_manager_.
Size() - 1;
1117 return reconstruction_idx;
1120 bool MainWindow::HasSelectedReconstruction() {
1121 const size_t reconstruction_idx =
1123 if (reconstruction_idx ==
1125 if (reconstruction_manager_.
Size() == 0) {
1132 bool MainWindow::IsSelectedReconstructionValid() {
1133 if (!HasSelectedReconstruction()) {
1134 QMessageBox::critical(
this,
"", tr(
"No reconstruction selected"));
1140 void MainWindow::GrabImage() {
1141 QString file_name = QFileDialog::getSaveFileName(
this, tr(
"Save image"),
"",
1142 tr(
"Images (*.png *.jpg)"));
1143 if (file_name !=
"") {
1146 file_name +=
".png";
1149 image.save(file_name);
1153 void MainWindow::UndistortImages() {
1154 if (!IsSelectedReconstructionValid()) {
1157 undistortion_widget_->
Show(
1158 reconstruction_manager_.
Get(SelectedReconstructionIdx()));
1161 void MainWindow::ReconstructionStats() {
1162 if (!IsSelectedReconstructionValid()) {
1165 reconstruction_stats_widget_->show();
1166 reconstruction_stats_widget_->raise();
1167 reconstruction_stats_widget_->
Show(
1168 reconstruction_manager_.
Get(SelectedReconstructionIdx()));
1171 void MainWindow::MatchMatrix() { match_matrix_widget_->
Show(); }
1173 void MainWindow::ShowLog() {
1174 log_widget_->show();
1175 log_widget_->raise();
1176 dock_log_widget_->show();
1177 dock_log_widget_->raise();
1180 void MainWindow::ExtractColors() {
1181 if (!IsSelectedReconstructionValid()) {
1185 thread_control_widget_->
StartFunction(
"Extracting colors...", [
this]() {
1186 auto& reconstruction =
1187 reconstruction_manager_.
Get(SelectedReconstructionIdx());
1192 void MainWindow::SetOptions() {
1193 QStringList data_items;
1194 data_items <<
"Individual images"
1196 <<
"Internet images";
1198 const QString data_item =
1199 QInputDialog::getItem(
this,
"",
"Data:", data_items, 0,
false, &data_ok);
1204 QStringList quality_items;
1205 quality_items <<
"Low"
1210 const QString quality_item = QInputDialog::getItem(
1211 this,
"",
"Quality:", quality_items, 2,
false, &quality_ok);
1216 const bool kResetPaths =
false;
1219 if (data_item ==
"Individual images") {
1221 }
else if (data_item ==
"Video frames") {
1223 }
else if (data_item ==
"Internet images") {
1226 LOG(FATAL) <<
"Data type does not exist";
1229 if (quality_item ==
"Low") {
1231 }
else if (quality_item ==
"Medium") {
1233 }
else if (quality_item ==
"High") {
1235 }
else if (quality_item ==
"Extreme") {
1238 LOG(FATAL) <<
"Quality level does not exist";
1242 void MainWindow::ResetOptions() {
1243 const bool kResetPaths =
false;
1247 void MainWindow::About() {
1250 #
if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1251 QString::asprintf(
"<span style='font-weight:normal'><b>%s</b><br />"
1252 "<small>(%s)</small><br /><br />"
1253 "<b>Author:</b>Asher<br /><br />"
1254 "<b>Email:</b>ludahai19@163.com</span>",
1257 QString().sprintf(
"<span style='font-weight:normal'><b>%s</b><br />"
1258 "<small>(%s)</small><br /><br />"
1259 "<b>Author:</b>Asher<br /><br />"
1260 "<b>Email:</b>ludahai19@163.com</span>",
1265 void MainWindow::Documentation() {
1266 QDesktopServices::openUrl(QUrl(
"https://colmap.github.io/"));
1269 void MainWindow::Support() {
1270 QDesktopServices::openUrl(
1271 QUrl(
"https://groups.google.com/forum/#!forum/colmap"));
1274 void MainWindow::RenderToggle() {
1277 render_options_widget_->
counter = 0;
1278 action_render_toggle_->setIcon(QIcon(
":/media/render-disabled.png"));
1279 action_render_toggle_->setText(tr(
"Enable rendering"));
1282 render_options_widget_->
counter = 0;
1284 action_render_toggle_->setIcon(QIcon(
":/media/render-enabled.png"));
1285 action_render_toggle_->setText(tr(
"Disable rendering"));
1289 void MainWindow::UpdateTimer() {
1290 const int elapsed_time =
static_cast<int>(timer_.
ElapsedSeconds());
1291 const int seconds = elapsed_time % 60;
1292 const int minutes = (elapsed_time / 60) % 60;
1293 const int hours = (elapsed_time / 3600) % 24;
1294 const int days = elapsed_time / 86400;
1295 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1296 statusbar_timer_label_->setText(QString::asprintf(
1297 "Time %02d:%02d:%02d:%02d", days, hours, minutes, seconds));
1299 statusbar_timer_label_->setText(QString().sprintf(
1300 "Time %02d:%02d:%02d:%02d", days, hours, minutes, seconds));
1304 void MainWindow::ShowInvalidProjectError() {
1305 QMessageBox::critical(
this,
"",
1306 tr(
"You must create a valid project using: <i>File > "
1307 "New project</i> or <i>File > Edit project</i>"));
1310 void MainWindow::EnableBlockingActions() {
1311 for (
auto& action : blocking_actions_) {
1312 action->setEnabled(
true);
1316 void MainWindow::DisableBlockingActions() {
1317 for (
auto& action : blocking_actions_) {
1318 action->setDisabled(
true);
1322 void MainWindow::UpdateWindowTitle() {
1324 setWindowTitle(QString::fromStdString(
"COLMAP"));
1327 if (project_title.size() > 80) {
1329 "..." + project_title.substr(project_title.size() - 77, 77);
1331 setWindowTitle(QString::fromStdString(
"COLMAP - " + project_title));
std::shared_ptr< core::Tensor > image
@ LAST_IMAGE_REG_CALLBACK
@ INITIAL_IMAGE_PAIR_REG_CALLBACK
@ NEXT_IMAGE_REG_CALLBACK
friend class BundleAdjustmentWidget
friend class AutomaticReconstructionWidget
void closeEvent(QCloseEvent *event)
friend class DenseReconstructionWidget
void ImportReconstruction(const std::string &path)
MainWindow(const OptionManager &options)
void ModifyForIndividualData()
bool ReRead(const std::string &path)
std::shared_ptr< RenderOptions > render
std::shared_ptr< std::string > database_path
void ModifyForVideoData()
void ModifyForInternetData()
std::shared_ptr< IncrementalMapperOptions > mapper
void ModifyForMediumQuality()
void ResetOptions(const bool reset_paths)
std::shared_ptr< std::string > project_path
void Write(const std::string &path) const
void ModifyForHighQuality()
void ModifyForLowQuality()
void ModifyForExtremeQuality()
std::shared_ptr< std::string > image_path
const Reconstruction & Get(const size_t idx) const
size_t Read(const std::string &path)
void Write(const std::string &path, const OptionManager *options) const
void ExtractColorsForAllImages(const std::string &path)
void Normalize(const double extent=10.0, const double p0=0.1, const double p1=0.9, const bool use_images=true)
bool ExportNVM(const std::string &path, bool skip_distortion=false) const
void WriteBinary(const std::string &path) const
void WriteText(const std::string &path) const
double ElapsedSeconds() const
static const std::string path
colmap::IncrementalMapperController IncrementalMapperController
std::string GetBuildInfo()
bool HasFileExtension(const std::string &file_name, const std::string &ext)
std::string GetVersionInfo()
bool ExistsFile(const std::string &path)
std::string JoinPaths(T const &... paths)
std::string StringPrintf(const char *format,...)