15 #include "ui/render_options.h"
16 #include "util/misc.h"
17 #include "util/version.h"
27 statusbar_timer_label_(nullptr) {
34 options_.AddAllOptions();
38 options_.render->projection_type =
39 colmap::RenderOptions::ProjectionType::ORTHOGRAPHIC;
47 settings.beginGroup(
"Reconstruction");
50 std::string import_path =
54 std::string project_path =
56 if (!import_path.empty() && project_path.empty()) {
57 project_path =
JoinPaths(import_path,
"project.ini");
60 if (project_path !=
"") {
62 if (ExistsFile(project_path)) {
63 if (options_.ReRead(project_path)) {
64 *options_.project_path = project_path;
69 settings.value(
"database_path",
"").toString());
71 settings.value(
"image_path",
"").toString());
72 if (!database_path.empty() && !image_path.empty()) {
73 *options_.project_path = project_path;
74 *options_.database_path = database_path;
75 *options_.image_path = image_path;
79 ShowInvalidProjectError();
86 settings.value(
"database_path",
"").toString());
88 settings.value(
"image_path",
"").toString());
89 if (!database_path.empty() && !image_path.empty()) {
90 *options_.project_path = project_path;
91 *options_.database_path = database_path;
92 *options_.image_path = image_path;
96 ShowInvalidProjectError();
105 const size_t idx = reconstruction_manager_.Read(
path);
106 reconstruction_manager_widget_->
Update();
112 if (project_widget_->
IsValid() && *options_.project_path ==
"") {
114 QMessageBox::StandardButton reply;
115 reply = QMessageBox::question(
117 tr(
"You have not saved your reconstruction project. Do you "
119 QMessageBox::Yes | QMessageBox::No);
120 if (reply == QMessageBox::Yes) {
125 if (mapper_controller_) {
126 mapper_controller_->Stop();
127 mapper_controller_->Wait();
131 log_widget_->close();
135 if (model_viewer_widget_) {
136 model_viewer_widget_->
Release();
141 void ReconstructionWidget::CreateWidgets() {
152 reconstruction_options_widget_ =
155 dense_reconstruction_widget_ =
157 render_options_widget_ =
161 reconstruction_manager_widget_ =
166 dock_log_widget_ =
new QDockWidget(
"Log",
this);
167 dock_log_widget_->setWidget(log_widget_);
170 void ReconstructionWidget::CreateActions() {
175 action_project_new_ =
new QAction(QIcon(
":/media/project-new.png"),
176 tr(
"New project"),
this);
178 connect(action_project_new_, &QAction::triggered,
this,
179 &ReconstructionWidget::ProjectNew);
181 action_project_open_ =
new QAction(QIcon(
":/media/project-open.png"),
182 tr(
"Open project"),
this);
184 connect(action_project_open_, &QAction::triggered,
this,
185 &ReconstructionWidget::ProjectOpen);
187 action_project_edit_ =
new QAction(QIcon(
":/media/project-edit.png"),
188 tr(
"Edit project"),
this);
189 connect(action_project_edit_, &QAction::triggered,
this,
190 &ReconstructionWidget::ProjectEdit);
192 action_project_save_ =
new QAction(QIcon(
":/media/project-save.png"),
193 tr(
"Save project"),
this);
195 connect(action_project_save_, &QAction::triggered,
this,
196 &ReconstructionWidget::ProjectSave);
198 action_project_save_as_ =
new QAction(QIcon(
":/media/project-save-as.png"),
199 tr(
"Save project as..."),
this);
201 connect(action_project_save_as_, &QAction::triggered,
this,
202 &ReconstructionWidget::ProjectSaveAs);
205 new QAction(QIcon(
":/media/import.png"), tr(
"Import model"),
this);
206 connect(action_import_, &QAction::triggered,
this,
207 &ReconstructionWidget::Import);
208 blocking_actions_.push_back(action_import_);
210 action_import_from_ =
new QAction(QIcon(
":/media/import-from.png"),
211 tr(
"Import model from..."),
this);
212 connect(action_import_from_, &QAction::triggered,
this,
213 &ReconstructionWidget::ImportFrom);
214 blocking_actions_.push_back(action_import_from_);
217 new QAction(QIcon(
":/media/export.png"), tr(
"Export model"),
this);
218 connect(action_export_, &QAction::triggered,
this,
219 &ReconstructionWidget::Export);
220 blocking_actions_.push_back(action_export_);
222 action_export_all_ =
new QAction(QIcon(
":/media/export-all.png"),
223 tr(
"Export all models"),
this);
224 connect(action_export_all_, &QAction::triggered,
this,
225 &ReconstructionWidget::ExportAll);
226 blocking_actions_.push_back(action_export_all_);
228 action_export_as_ =
new QAction(QIcon(
":/media/export-as.png"),
229 tr(
"Export model as..."),
this);
230 connect(action_export_as_, &QAction::triggered,
this,
231 &ReconstructionWidget::ExportAs);
232 blocking_actions_.push_back(action_export_as_);
234 action_export_as_text_ =
new QAction(QIcon(
":/media/export-as-text.png"),
235 tr(
"Export model as text"),
this);
236 connect(action_export_as_text_, &QAction::triggered,
this,
237 &ReconstructionWidget::ExportAsText);
238 blocking_actions_.push_back(action_export_as_text_);
240 action_quit_ =
new QAction(tr(
"Quit"),
this);
241 connect(action_quit_, &QAction::triggered,
this,
248 action_feature_extraction_ =
249 new QAction(QIcon(
":/media/feature-extraction.png"),
250 tr(
"Feature extraction"),
this);
251 connect(action_feature_extraction_, &QAction::triggered,
this,
252 &ReconstructionWidget::FeatureExtraction);
253 blocking_actions_.push_back(action_feature_extraction_);
255 action_feature_matching_ =
256 new QAction(QIcon(
":/media/feature-matching.png"),
257 tr(
"Feature matching"),
this);
258 connect(action_feature_matching_, &QAction::triggered,
this,
259 &ReconstructionWidget::FeatureMatching);
260 blocking_actions_.push_back(action_feature_matching_);
262 action_database_management_ =
263 new QAction(QIcon(
":/media/database-management.png"),
264 tr(
"Database management"),
this);
265 connect(action_database_management_, &QAction::triggered,
this,
266 &ReconstructionWidget::DatabaseManagement);
267 blocking_actions_.push_back(action_database_management_);
273 action_automatic_reconstruction_ =
274 new QAction(QIcon(
":/media/automatic-reconstruction.png"),
275 tr(
"Automatic reconstruction"),
this);
276 connect(action_automatic_reconstruction_, &QAction::triggered,
this,
277 &ReconstructionWidget::AutomaticReconstruction);
279 action_reconstruction_start_ =
280 new QAction(QIcon(
":/media/reconstruction-start.png"),
281 tr(
"Start reconstruction"),
this);
282 connect(action_reconstruction_start_, &QAction::triggered,
this,
283 &ReconstructionWidget::ReconstructionStart);
284 blocking_actions_.push_back(action_reconstruction_start_);
286 action_reconstruction_step_ =
287 new QAction(QIcon(
":/media/reconstruction-step.png"),
288 tr(
"Reconstruct next image"),
this);
289 connect(action_reconstruction_step_, &QAction::triggered,
this,
290 &ReconstructionWidget::ReconstructionStep);
291 blocking_actions_.push_back(action_reconstruction_step_);
293 action_reconstruction_pause_ =
294 new QAction(QIcon(
":/media/reconstruction-pause.png"),
295 tr(
"Pause reconstruction"),
this);
296 connect(action_reconstruction_pause_, &QAction::triggered,
this,
297 &ReconstructionWidget::ReconstructionPause);
298 action_reconstruction_pause_->setEnabled(
false);
299 blocking_actions_.push_back(action_reconstruction_pause_);
301 action_reconstruction_reset_ =
302 new QAction(QIcon(
":/media/reconstruction-reset.png"),
303 tr(
"Reset reconstruction"),
this);
304 connect(action_reconstruction_reset_, &QAction::triggered,
this,
305 &ReconstructionWidget::ReconstructionOverwrite);
307 action_reconstruction_normalize_ =
308 new QAction(QIcon(
":/media/reconstruction-normalize.png"),
309 tr(
"Normalize reconstruction"),
this);
310 connect(action_reconstruction_normalize_, &QAction::triggered,
this,
311 &ReconstructionWidget::ReconstructionNormalize);
312 blocking_actions_.push_back(action_reconstruction_normalize_);
314 action_reconstruction_options_ =
315 new QAction(QIcon(
":/media/reconstruction-options.png"),
316 tr(
"Reconstruction options"),
this);
317 connect(action_reconstruction_options_, &QAction::triggered,
this,
318 &ReconstructionWidget::ReconstructionOptions);
319 blocking_actions_.push_back(action_reconstruction_options_);
321 action_bundle_adjustment_ =
322 new QAction(QIcon(
":/media/bundle-adjustment.png"),
323 tr(
"Bundle adjustment"),
this);
324 connect(action_bundle_adjustment_, &QAction::triggered,
this,
325 &ReconstructionWidget::BundleAdjustment);
326 action_bundle_adjustment_->setEnabled(
false);
327 blocking_actions_.push_back(action_bundle_adjustment_);
329 action_dense_reconstruction_ =
330 new QAction(QIcon(
":/media/dense-reconstruction.png"),
331 tr(
"Dense reconstruction"),
this);
332 connect(action_dense_reconstruction_, &QAction::triggered,
this,
333 &ReconstructionWidget::DenseReconstruction);
339 action_render_toggle_ =
new QAction(QIcon(
":/media/render-enabled.png"),
340 tr(
"Disable rendering"),
this);
341 connect(action_render_toggle_, &QAction::triggered,
this,
342 &ReconstructionWidget::RenderToggle);
344 action_render_reset_view_ =
new QAction(
345 QIcon(
":/media/render-reset-view.png"), tr(
"Reset view"),
this);
346 connect(action_render_reset_view_, &QAction::triggered,
349 action_render_options_ =
new QAction(QIcon(
":/media/render-options.png"),
350 tr(
"Render options"),
this);
351 connect(action_render_options_, &QAction::triggered,
this,
352 &ReconstructionWidget::RenderOptions);
354 connect(reconstruction_manager_widget_,
355 static_cast<void (QComboBox::*)(
int)
>(
356 &QComboBox::currentIndexChanged),
357 this, &ReconstructionWidget::SelectReconstructionIdx);
363 action_reconstruction_stats_ =
364 new QAction(QIcon(
":/media/reconstruction-stats.png"),
365 tr(
"Show model statistics"),
this);
366 connect(action_reconstruction_stats_, &QAction::triggered,
this,
367 &ReconstructionWidget::ReconstructionStats);
369 action_match_matrix_ =
new QAction(QIcon(
":/media/match-matrix.png"),
370 tr(
"Show match matrix"),
this);
371 connect(action_match_matrix_, &QAction::triggered,
this,
372 &ReconstructionWidget::MatchMatrix);
375 new QAction(QIcon(
":/media/log.png"), tr(
"Show log"),
this);
376 connect(action_log_show_, &QAction::triggered,
this,
379 action_grab_image_ =
new QAction(QIcon(
":/media/grab-image.png"),
380 tr(
"Grab image"),
this);
381 connect(action_grab_image_, &QAction::triggered,
this,
382 &ReconstructionWidget::GrabImage);
384 action_grab_movie_ =
new QAction(QIcon(
":/media/grab-movie.png"),
385 tr(
"Grab movie"),
this);
386 connect(action_grab_movie_, &QAction::triggered, model_viewer_widget_,
389 action_undistort_ =
new QAction(QIcon(
":/media/undistort.png"),
390 tr(
"Undistortion"),
this);
391 connect(action_undistort_, &QAction::triggered,
this,
392 &ReconstructionWidget::UndistortImages);
393 blocking_actions_.push_back(action_undistort_);
395 action_extract_colors_ =
new QAction(tr(
"Extract colors"),
this);
396 connect(action_extract_colors_, &QAction::triggered,
this,
397 &ReconstructionWidget::ExtractColors);
399 action_set_options_ =
new QAction(tr(
"Set options for ..."),
this);
400 connect(action_set_options_, &QAction::triggered,
this,
401 &ReconstructionWidget::SetOptions);
403 action_reset_options_ =
new QAction(tr(
"Set default options"),
this);
404 connect(action_reset_options_, &QAction::triggered,
this,
405 &ReconstructionWidget::ResetOptions);
411 action_render_ =
new QAction(tr(
"Render"),
this);
412 connect(action_render_, &QAction::triggered,
this,
413 &ReconstructionWidget::Render, Qt::BlockingQueuedConnection);
415 action_render_now_ =
new QAction(tr(
"Render now"),
this);
416 connect(action_render_now_, &QAction::triggered,
this,
417 &ReconstructionWidget::RenderNow, Qt::BlockingQueuedConnection);
419 action_reconstruction_finish_ =
420 new QAction(tr(
"Finish reconstruction"),
this);
421 connect(action_reconstruction_finish_, &QAction::triggered,
this,
422 &ReconstructionWidget::ReconstructionFinish,
423 Qt::BlockingQueuedConnection);
426 void ReconstructionWidget::CreateMenus() {
427 QMenu* file_menu =
new QMenu(tr(
"ProjectFile"),
this);
428 file_menu->addAction(action_project_new_);
429 file_menu->addAction(action_project_open_);
430 file_menu->addAction(action_project_edit_);
431 file_menu->addAction(action_project_save_);
432 file_menu->addAction(action_project_save_as_);
433 file_menu->addSeparator();
434 file_menu->addAction(action_import_);
435 file_menu->addAction(action_import_from_);
436 file_menu->addSeparator();
437 file_menu->addAction(action_export_);
438 file_menu->addAction(action_export_all_);
439 file_menu->addAction(action_export_as_);
440 file_menu->addAction(action_export_as_text_);
441 file_menu->addSeparator();
442 file_menu->addAction(action_quit_);
443 menus_list_.push_back(file_menu);
445 QMenu* preprocessing_menu =
new QMenu(tr(
"Processing"),
this);
446 preprocessing_menu->addAction(action_feature_extraction_);
447 preprocessing_menu->addAction(action_feature_matching_);
448 preprocessing_menu->addAction(action_database_management_);
449 menus_list_.push_back(preprocessing_menu);
451 QMenu* reconstruction_menu =
new QMenu(tr(
"Reconstruction"),
this);
452 reconstruction_menu->addAction(action_automatic_reconstruction_);
453 reconstruction_menu->addSeparator();
454 reconstruction_menu->addAction(action_reconstruction_start_);
455 reconstruction_menu->addAction(action_reconstruction_pause_);
456 reconstruction_menu->addAction(action_reconstruction_step_);
457 reconstruction_menu->addSeparator();
458 reconstruction_menu->addAction(action_reconstruction_reset_);
459 reconstruction_menu->addAction(action_reconstruction_normalize_);
460 reconstruction_menu->addAction(action_reconstruction_options_);
461 reconstruction_menu->addSeparator();
462 reconstruction_menu->addAction(action_bundle_adjustment_);
463 reconstruction_menu->addAction(action_dense_reconstruction_);
464 menus_list_.push_back(reconstruction_menu);
466 QMenu* render_menu =
new QMenu(tr(
"Render"),
this);
467 render_menu->addAction(action_render_toggle_);
468 render_menu->addAction(action_render_reset_view_);
469 render_menu->addAction(action_render_options_);
470 menus_list_.push_back(render_menu);
472 QMenu* extras_menu =
new QMenu(tr(
"Extras"),
this);
473 extras_menu->addAction(action_log_show_);
474 extras_menu->addAction(action_match_matrix_);
475 extras_menu->addAction(action_reconstruction_stats_);
476 extras_menu->addSeparator();
477 extras_menu->addAction(action_grab_image_);
478 extras_menu->addAction(action_grab_movie_);
479 extras_menu->addSeparator();
480 extras_menu->addAction(action_undistort_);
481 extras_menu->addAction(action_extract_colors_);
482 extras_menu->addSeparator();
483 extras_menu->addAction(action_set_options_);
484 extras_menu->addAction(action_reset_options_);
485 menus_list_.push_back(extras_menu);
488 void ReconstructionWidget::CreateToolbar() {
489 reconstruction_toolbar_ =
new QToolBar(tr(
"Reconstruction"),
this);
490 reconstruction_toolbar_->setObjectName(QString::fromUtf8(
"Reconstruction"));
491 reconstruction_toolbar_->addAction(action_project_new_);
492 reconstruction_toolbar_->addAction(action_project_open_);
493 reconstruction_toolbar_->addAction(action_project_edit_);
494 reconstruction_toolbar_->addAction(action_project_save_);
495 reconstruction_toolbar_->addAction(action_import_);
496 reconstruction_toolbar_->addAction(action_export_);
497 reconstruction_toolbar_->addAction(action_feature_extraction_);
498 reconstruction_toolbar_->addAction(action_feature_matching_);
499 reconstruction_toolbar_->addAction(action_database_management_);
500 reconstruction_toolbar_->addAction(action_automatic_reconstruction_);
501 reconstruction_toolbar_->addAction(action_reconstruction_start_);
502 reconstruction_toolbar_->addAction(action_reconstruction_step_);
503 reconstruction_toolbar_->addAction(action_reconstruction_pause_);
504 reconstruction_toolbar_->addAction(action_reconstruction_reset_);
505 reconstruction_toolbar_->addAction(action_reconstruction_normalize_);
506 reconstruction_toolbar_->addAction(action_reconstruction_options_);
507 reconstruction_toolbar_->addAction(action_bundle_adjustment_);
508 reconstruction_toolbar_->addAction(action_dense_reconstruction_);
509 reconstruction_toolbar_->addAction(action_render_toggle_);
510 reconstruction_toolbar_->addAction(action_render_reset_view_);
511 reconstruction_toolbar_->addAction(action_render_options_);
512 reconstruction_toolbar_->addWidget(reconstruction_manager_widget_);
513 reconstruction_toolbar_->addAction(action_log_show_);
514 reconstruction_toolbar_->addAction(action_match_matrix_);
515 reconstruction_toolbar_->addAction(action_reconstruction_stats_);
516 reconstruction_toolbar_->addAction(action_grab_image_);
517 reconstruction_toolbar_->addAction(action_grab_movie_);
520 toolbar_list_.push_back(reconstruction_toolbar_);
523 void ReconstructionWidget::CreateStatusbar() {
525 font.setPointSize(11);
527 statusbar_timer_label_ =
new QLabel(
"Time 00:00:00:00",
this);
528 statusbar_timer_label_->setFont(font);
529 statusbar_timer_label_->setAlignment(Qt::AlignCenter);
530 statusbar_timer_ =
new QTimer(
this);
531 connect(statusbar_timer_, &QTimer::timeout,
this,
532 &ReconstructionWidget::UpdateTimer);
533 statusbar_timer_->start(1000);
536 new QLabel(
"0 Images - 0 Points",
this);
541 void ReconstructionWidget::CreateControllers() {
542 if (mapper_controller_) {
543 mapper_controller_->Stop();
544 mapper_controller_->Wait();
548 options_.mapper.get(), *options_.image_path,
549 *options_.database_path, &reconstruction_manager_));
550 mapper_controller_->AddCallback(
551 IncrementalMapperController::INITIAL_IMAGE_PAIR_REG_CALLBACK,
553 if (!mapper_controller_->IsStopped()) {
554 action_render_now_->trigger();
557 mapper_controller_->AddCallback(
558 IncrementalMapperController::NEXT_IMAGE_REG_CALLBACK, [
this]() {
559 if (!mapper_controller_->IsStopped()) {
560 action_render_->trigger();
563 mapper_controller_->AddCallback(
564 IncrementalMapperController::LAST_IMAGE_REG_CALLBACK, [
this]() {
565 if (!mapper_controller_->IsStopped()) {
566 action_render_now_->trigger();
569 mapper_controller_->AddCallback(
570 IncrementalMapperController::FINISHED_CALLBACK, [
this]() {
571 if (!mapper_controller_->IsStopped()) {
572 action_render_now_->trigger();
573 action_reconstruction_finish_->trigger();
575 if (reconstruction_manager_.Size() == 0) {
576 action_reconstruction_reset_->trigger();
581 void ReconstructionWidget::ProjectNew() {
582 if (ReconstructionOverwrite()) {
583 project_widget_->
Reset();
584 project_widget_->show();
585 project_widget_->raise();
589 bool ReconstructionWidget::ProjectOpen() {
590 if (!ReconstructionOverwrite()) {
595 settings.beginGroup(
"Reconstruction");
596 QString last_project_path = settings.value(
"project_path",
"").toString();
599 const std::string project_path =
600 QFileDialog::getOpenFileName(
601 this, tr(
"Select project file"),
602 QFileInfo(last_project_path).dir().absolutePath(),
603 tr(
"Project file (*.ini)"))
607 if (project_path !=
"") {
608 if (options_.ReRead(project_path)) {
609 *options_.project_path = project_path;
613 project_widget_->
persistSave(*options_.project_path,
614 *options_.database_path,
615 *options_.image_path);
618 ShowInvalidProjectError();
625 void ReconstructionWidget::ProjectEdit() {
626 project_widget_->show();
627 project_widget_->raise();
630 void ReconstructionWidget::ProjectSave() {
631 if (!ExistsFile(*options_.project_path)) {
633 settings.beginGroup(
"Reconstruction");
634 QString last_project_path =
635 settings.value(
"project_path",
"").toString();
638 std::string project_path =
639 QFileDialog::getSaveFileName(
640 this, tr(
"Select project file"),
641 QFileInfo(last_project_path).dir().absolutePath(),
642 tr(
"Project file (*.ini)"))
646 if (project_path !=
"") {
648 project_path +=
".ini";
650 *options_.project_path = project_path;
651 options_.Write(*options_.project_path);
655 options_.Write(*options_.project_path);
658 if (ExistsFile(*options_.project_path)) {
659 project_widget_->
persistSave(*options_.project_path,
660 *options_.database_path,
661 *options_.image_path);
665 void ReconstructionWidget::ProjectSaveAs() {
667 settings.beginGroup(
"Reconstruction");
668 QString last_project_path = settings.value(
"project_path",
"").toString();
670 const std::string new_project_path =
671 QFileDialog::getSaveFileName(
672 this, tr(
"Select project file"),
673 QFileInfo(last_project_path).dir().absolutePath(),
674 tr(
"Project file (*.ini)"))
677 if (new_project_path !=
"") {
678 *options_.project_path = new_project_path;
679 options_.Write(*options_.project_path);
681 project_widget_->
persistSave(*options_.project_path,
682 *options_.database_path,
683 *options_.image_path);
689 void ReconstructionWidget::Import() {
691 settings.beginGroup(
"Reconstruction");
692 QString last_import_path = settings.value(
"import_path",
"").toString();
694 const std::string import_path =
695 QFileDialog::getExistingDirectory(
this, tr(
"Select source..."),
697 QFileDialog::ShowDirsOnly)
702 if (import_path ==
"") {
707 const std::string project_path =
JoinPaths(import_path,
"project.ini");
708 const std::string cameras_bin_path =
JoinPaths(import_path,
"cameras.bin");
709 const std::string images_bin_path =
JoinPaths(import_path,
"images.bin");
710 const std::string points3D_bin_path =
712 const std::string cameras_txt_path =
JoinPaths(import_path,
"cameras.txt");
713 const std::string images_txt_path =
JoinPaths(import_path,
"images.txt");
714 const std::string points3D_txt_path =
717 if ((!ExistsFile(cameras_bin_path) || !ExistsFile(images_bin_path) ||
718 !ExistsFile(points3D_bin_path)) &&
719 (!ExistsFile(cameras_txt_path) || !ExistsFile(images_txt_path) ||
720 !ExistsFile(points3D_txt_path))) {
721 QMessageBox::critical(
723 tr(
"cameras, images, and points3D files do not exist "
724 "in chosen directory."));
736 if (!ReconstructionOverwrite()) {
740 bool edit_project =
false;
741 if (ExistsFile(project_path)) {
742 options_.ReRead(project_path);
744 QMessageBox::StandardButton reply = QMessageBox::question(
746 tr(
"Directory does not contain a <i>project.ini</i>. To "
747 "resume the reconstruction, you need to specify a valid "
748 "database and image path. Do you want to select the paths "
749 "now (or press <i>No</i> to only visualize the "
751 QMessageBox::Yes | QMessageBox::No);
752 if (reply == QMessageBox::Yes) {
758 "Importing...", [
this, import_path, edit_project]() {
759 const size_t idx = reconstruction_manager_.Read(import_path);
760 reconstruction_manager_widget_->
Update();
762 action_bundle_adjustment_->setEnabled(
true);
763 action_render_now_->trigger();
765 action_project_edit_->trigger();
770 void ReconstructionWidget::ImportFrom() {
772 settings.beginGroup(
"Reconstruction");
773 QString last_model_import_path =
774 settings.value(
"model_import_path",
"").toString();
776 const std::string import_path =
777 QFileDialog::getOpenFileName(
this, tr(
"Select source..."),
778 last_model_import_path)
783 if (import_path ==
"") {
788 if (!ExistsFile(import_path)) {
789 QMessageBox::critical(
this,
"", tr(
"Invalid file"));
795 QMessageBox::critical(
796 this,
"", tr(
"Invalid file format (supported formats: PLY)"));
806 const size_t reconstruction_idx = reconstruction_manager_.Add();
807 reconstruction_manager_.Get(reconstruction_idx).ImportPLY(import_path);
808 options_.render->min_track_len = 0;
809 reconstruction_manager_widget_->
Update();
812 action_render_now_->trigger();
816 void ReconstructionWidget::Export() {
817 if (!IsSelectedReconstructionValid()) {
822 settings.beginGroup(
"Reconstruction");
823 QString last_export_path = settings.value(
"export_path",
"").toString();
825 const std::string export_path =
826 QFileDialog::getExistingDirectory(
this, tr(
"Select destination..."),
828 QFileDialog::ShowDirsOnly)
833 if (export_path ==
"") {
838 const std::string cameras_name =
"cameras.bin";
839 const std::string images_name =
"images.bin";
840 const std::string points3D_name =
"points3D.bin";
842 const std::string project_path =
JoinPaths(export_path,
"project.ini");
843 const std::string cameras_path =
JoinPaths(export_path, cameras_name);
844 const std::string images_path =
JoinPaths(export_path, images_name);
845 const std::string points3D_path =
JoinPaths(export_path, points3D_name);
854 if (ExistsFile(cameras_path) || ExistsFile(images_path) ||
855 ExistsFile(points3D_path)) {
856 QMessageBox::StandardButton reply = QMessageBox::question(
859 "The files <i>%s</i>, <i>%s</i>, or <i>%s</i> already "
860 "exist in the selected destination. Do you want to "
862 cameras_name.c_str(), images_name.c_str(),
863 points3D_name.c_str())
865 QMessageBox::Yes | QMessageBox::No);
866 if (reply == QMessageBox::No) {
871 thread_control_widget_->
StartFunction(
"Exporting...", [
this, export_path,
873 const auto& reconstruction =
874 reconstruction_manager_.Get(SelectedReconstructionIdx());
875 reconstruction.WriteBinary(export_path);
876 options_.Write(project_path);
880 void ReconstructionWidget::ExportAll() {
881 if (!IsSelectedReconstructionValid()) {
886 settings.beginGroup(
"Reconstruction");
887 QString last_export_path = settings.value(
"export_path",
"").toString();
889 const std::string export_path =
890 QFileDialog::getExistingDirectory(
this, tr(
"Select destination..."),
892 QFileDialog::ShowDirsOnly)
897 if (export_path ==
"") {
905 "Exporting...", [
this, export_path]() {
906 reconstruction_manager_.Write(export_path, &options_);
911 void ReconstructionWidget::ExportAs() {
912 if (!IsSelectedReconstructionValid()) {
917 settings.beginGroup(
"Reconstruction");
918 QString last_export_path =
919 settings.value(
"model_export_path",
"").toString();
921 QString filter(
"NVM (*.nvm)");
922 const std::string export_path =
923 QFileDialog::getSaveFileName(
924 this, tr(
"Select destination..."), last_export_path,
925 "NVM (*.nvm);;Bundler (*.out);;PLY (*.ply);;VRML (*.wrl)",
931 if (export_path ==
"") {
939 thread_control_widget_->
StartFunction(
"Exporting...", [
this, export_path,
941 const Reconstruction& reconstruction =
942 reconstruction_manager_.Get(SelectedReconstructionIdx());
943 if (filter ==
"NVM (*.nvm)") {
944 reconstruction.ExportNVM(export_path);
945 }
else if (filter ==
"Bundler (*.out)") {
946 reconstruction.ExportBundler(export_path,
947 export_path +
".list.txt");
948 }
else if (filter ==
"PLY (*.ply)") {
949 reconstruction.ExportPLY(export_path);
950 }
else if (filter ==
"VRML (*.wrl)") {
951 const auto base_path =
952 export_path.substr(0, export_path.find_last_of(
"."));
953 reconstruction.ExportVRML(base_path +
".images.wrl",
954 base_path +
".points3D.wrl", 1,
955 Eigen::Vector3d(1, 0, 0));
960 void ReconstructionWidget::ExportAsText() {
961 if (!IsSelectedReconstructionValid()) {
966 settings.beginGroup(
"Reconstruction");
967 QString last_export_path = settings.value(
"export_path",
"").toString();
969 const std::string export_path =
970 QFileDialog::getExistingDirectory(
this, tr(
"Select destination..."),
972 QFileDialog::ShowDirsOnly)
977 if (export_path ==
"") {
982 const std::string cameras_name =
"cameras.txt";
983 const std::string images_name =
"images.txt";
984 const std::string points3D_name =
"points3D.txt";
986 const std::string project_path =
JoinPaths(export_path,
"project.ini");
987 const std::string cameras_path =
JoinPaths(export_path, cameras_name);
988 const std::string images_path =
JoinPaths(export_path, images_name);
989 const std::string points3D_path =
JoinPaths(export_path, points3D_name);
998 if (ExistsFile(cameras_path) || ExistsFile(images_path) ||
999 ExistsFile(points3D_path)) {
1000 QMessageBox::StandardButton reply = QMessageBox::question(
1003 "The files <i>%s</i>, <i>%s</i>, or <i>%s</i> already "
1004 "exist in the selected destination. Do you want to "
1006 cameras_name.c_str(), images_name.c_str(),
1007 points3D_name.c_str())
1009 QMessageBox::Yes | QMessageBox::No);
1010 if (reply == QMessageBox::No) {
1015 thread_control_widget_->
StartFunction(
"Exporting...", [
this, export_path,
1017 const auto& reconstruction =
1018 reconstruction_manager_.Get(SelectedReconstructionIdx());
1019 reconstruction.WriteText(export_path);
1020 options_.Write(project_path);
1024 void ReconstructionWidget::FeatureExtraction() {
1025 if (options_.Check()) {
1026 feature_extraction_widget_->show();
1027 feature_extraction_widget_->raise();
1029 ShowInvalidProjectError();
1033 void ReconstructionWidget::FeatureMatching() {
1034 if (options_.Check()) {
1035 feature_matching_widget_->show();
1036 feature_matching_widget_->raise();
1038 ShowInvalidProjectError();
1042 void ReconstructionWidget::DatabaseManagement() {
1043 if (options_.Check()) {
1044 database_management_widget_->show();
1045 database_management_widget_->raise();
1047 ShowInvalidProjectError();
1051 void ReconstructionWidget::AutomaticReconstruction() {
1052 automatic_reconstruction_widget_->show();
1053 automatic_reconstruction_widget_->raise();
1056 void ReconstructionWidget::ReconstructionStart() {
1057 if (!mapper_controller_->IsStarted() && !options_.Check()) {
1058 ShowInvalidProjectError();
1062 if (mapper_controller_->IsFinished() && HasSelectedReconstruction()) {
1063 QMessageBox::critical(
this,
"",
1064 tr(
"Reset reconstruction before starting."));
1068 if (mapper_controller_->IsStarted()) {
1071 mapper_controller_->Resume();
1074 CreateControllers();
1076 mapper_controller_->Start();
1077 action_reconstruction_start_->setText(tr(
"Resume reconstruction"));
1080 DisableBlockingActions();
1081 action_reconstruction_pause_->setEnabled(
true);
1084 void ReconstructionWidget::ReconstructionStep() {
1085 if (mapper_controller_->IsFinished() && HasSelectedReconstruction()) {
1086 QMessageBox::critical(
this,
"",
1087 tr(
"Reset reconstruction before starting."));
1091 action_reconstruction_step_->setEnabled(
false);
1092 ReconstructionStart();
1093 ReconstructionPause();
1094 action_reconstruction_step_->setEnabled(
true);
1097 void ReconstructionWidget::ReconstructionPause() {
1099 mapper_controller_->Pause();
1100 EnableBlockingActions();
1101 action_reconstruction_pause_->setEnabled(
false);
1104 void ReconstructionWidget::ReconstructionOptions() {
1105 reconstruction_options_widget_->show();
1106 reconstruction_options_widget_->raise();
1109 void ReconstructionWidget::ReconstructionFinish() {
1111 mapper_controller_->Stop();
1112 EnableBlockingActions();
1113 action_reconstruction_start_->setEnabled(
false);
1114 action_reconstruction_step_->setEnabled(
false);
1115 action_reconstruction_pause_->setEnabled(
false);
1118 void ReconstructionWidget::ReconstructionReset() {
1119 CreateControllers();
1121 reconstruction_manager_.Clear();
1122 reconstruction_manager_widget_->
Update();
1127 EnableBlockingActions();
1128 action_reconstruction_start_->setText(tr(
"Start reconstruction"));
1129 action_reconstruction_pause_->setEnabled(
false);
1134 void ReconstructionWidget::ReconstructionNormalize() {
1135 if (!IsSelectedReconstructionValid()) {
1138 action_reconstruction_step_->setEnabled(
false);
1139 reconstruction_manager_.Get(SelectedReconstructionIdx()).Normalize();
1140 action_reconstruction_step_->setEnabled(
true);
1143 bool ReconstructionWidget::ReconstructionOverwrite() {
1144 if (reconstruction_manager_.Size() == 0) {
1145 ReconstructionReset();
1149 QMessageBox::StandardButton reply = QMessageBox::question(
1151 tr(
"Do you really want to overwrite the existing reconstruction?"),
1152 QMessageBox::Yes | QMessageBox::No);
1153 if (reply == QMessageBox::No) {
1156 ReconstructionReset();
1161 void ReconstructionWidget::BundleAdjustment() {
1162 if (!IsSelectedReconstructionValid()) {
1166 bundle_adjustment_widget_->
Show(
1167 &reconstruction_manager_.Get(SelectedReconstructionIdx()));
1170 void ReconstructionWidget::DenseReconstruction() {
1171 if (HasSelectedReconstruction()) {
1172 dense_reconstruction_widget_->
Show(
1173 &reconstruction_manager_.Get(SelectedReconstructionIdx()));
1175 dense_reconstruction_widget_->
Show(
nullptr);
1179 void ReconstructionWidget::Render() {
1180 if (reconstruction_manager_.Size() == 0) {
1184 const Reconstruction& reconstruction =
1185 reconstruction_manager_.Get(SelectedReconstructionIdx());
1188 if (options_.render->adapt_refresh_rate) {
1189 refresh_rate =
static_cast<int>(reconstruction.NumRegImages() / 50 + 1);
1191 refresh_rate = options_.render->refresh_rate;
1195 render_options_widget_->
counter % refresh_rate != 0) {
1196 render_options_widget_->
counter += 1;
1200 render_options_widget_->
counter += 1;
1205 void ReconstructionWidget::RenderNow() {
1206 reconstruction_manager_widget_->
Update();
1207 RenderSelectedReconstruction();
1210 void ReconstructionWidget::RenderSelectedReconstruction() {
1211 if (reconstruction_manager_.Size() == 0) {
1216 const size_t reconstruction_idx = SelectedReconstructionIdx();
1218 &reconstruction_manager_.Get(reconstruction_idx);
1222 void ReconstructionWidget::RenderClear() {
1228 void ReconstructionWidget::RenderOptions() {
1229 render_options_widget_->show();
1230 render_options_widget_->raise();
1233 void ReconstructionWidget::SelectReconstructionIdx(
const size_t) {
1234 RenderSelectedReconstruction();
1237 size_t ReconstructionWidget::SelectedReconstructionIdx() {
1238 size_t reconstruction_idx =
1240 if (reconstruction_idx ==
1242 if (reconstruction_manager_.Size() > 0) {
1243 reconstruction_idx = reconstruction_manager_.Size() - 1;
1246 return reconstruction_idx;
1249 bool ReconstructionWidget::HasSelectedReconstruction() {
1250 const size_t reconstruction_idx =
1252 if (reconstruction_idx ==
1254 if (reconstruction_manager_.Size() == 0) {
1261 bool ReconstructionWidget::IsSelectedReconstructionValid() {
1262 if (!HasSelectedReconstruction()) {
1263 QMessageBox::critical(
this,
"", tr(
"No reconstruction selected"));
1269 void ReconstructionWidget::GrabImage() {
1270 QString file_name = QFileDialog::getSaveFileName(
1271 this, tr(
"Save image"),
"", tr(
"Images (*.png *.jpg)"));
1272 if (file_name !=
"") {
1275 file_name +=
".png";
1278 image.save(file_name);
1282 void ReconstructionWidget::UndistortImages() {
1283 if (!IsSelectedReconstructionValid()) {
1286 undistortion_widget_->
Show(
1287 reconstruction_manager_.Get(SelectedReconstructionIdx()));
1290 void ReconstructionWidget::ReconstructionStats() {
1291 if (!IsSelectedReconstructionValid()) {
1294 reconstruction_stats_widget_->show();
1295 reconstruction_stats_widget_->raise();
1296 reconstruction_stats_widget_->
Show(
1297 reconstruction_manager_.Get(SelectedReconstructionIdx()));
1300 void ReconstructionWidget::MatchMatrix() { match_matrix_widget_->
Show(); }
1303 log_widget_->show();
1304 log_widget_->raise();
1305 dock_log_widget_->show();
1306 dock_log_widget_->raise();
1310 log_widget_->hide();
1311 dock_log_widget_->hide();
1314 void ReconstructionWidget::ExtractColors() {
1315 if (!IsSelectedReconstructionValid()) {
1319 thread_control_widget_->
StartFunction(
"Extracting colors...", [
this]() {
1320 auto& reconstruction =
1321 reconstruction_manager_.Get(SelectedReconstructionIdx());
1322 reconstruction.ExtractColorsForAllImages(*options_.image_path);
1326 void ReconstructionWidget::SetOptions() {
1327 QStringList data_items;
1328 data_items <<
"Individual images"
1330 <<
"Internet images";
1332 const QString data_item = QInputDialog::getItem(
1333 this,
"",
"Data:", data_items, 0,
false, &data_ok);
1338 QStringList quality_items;
1339 quality_items <<
"Low"
1344 const QString quality_item = QInputDialog::getItem(
1345 this,
"",
"Quality:", quality_items, 2,
false, &quality_ok);
1350 const bool kResetPaths =
false;
1351 options_.ResetOptions(kResetPaths);
1353 if (data_item ==
"Individual images") {
1354 options_.ModifyForIndividualData();
1355 }
else if (data_item ==
"Video frames") {
1356 options_.ModifyForVideoData();
1357 }
else if (data_item ==
"Internet images") {
1358 options_.ModifyForInternetData();
1360 LOG(FATAL) <<
"Data type does not exist";
1363 if (quality_item ==
"Low") {
1364 options_.ModifyForLowQuality();
1365 }
else if (quality_item ==
"Medium") {
1366 options_.ModifyForMediumQuality();
1367 }
else if (quality_item ==
"High") {
1368 options_.ModifyForHighQuality();
1369 }
else if (quality_item ==
"Extreme") {
1370 options_.ModifyForExtremeQuality();
1372 LOG(FATAL) <<
"Quality level does not exist";
1376 void ReconstructionWidget::ResetOptions() {
1377 const bool kResetPaths =
false;
1378 options_.ResetOptions(kResetPaths);
1381 void ReconstructionWidget::RenderToggle() {
1384 render_options_widget_->
counter = 0;
1385 action_render_toggle_->setIcon(QIcon(
":/media/render-disabled.png"));
1386 action_render_toggle_->setText(tr(
"Enable rendering"));
1389 render_options_widget_->
counter = 0;
1391 action_render_toggle_->setIcon(QIcon(
":/media/render-enabled.png"));
1392 action_render_toggle_->setText(tr(
"Disable rendering"));
1396 void ReconstructionWidget::UpdateTimer() {
1397 const int elapsed_time =
static_cast<int>(timer_.ElapsedSeconds());
1398 const int seconds = elapsed_time % 60;
1399 const int minutes = (elapsed_time / 60) % 60;
1400 const int hours = (elapsed_time / 3600) % 24;
1401 const int days = elapsed_time / 86400;
1402 if (statusbar_timer_label_) {
1403 statusbar_timer_label_->setText(QString().asprintf(
1404 "Time %02d:%02d:%02d:%02d", days, hours, minutes,
seconds));
1408 void ReconstructionWidget::ShowInvalidProjectError() {
1409 QMessageBox::critical(
this,
"",
1410 tr(
"You must create a valid project using: <i>File > "
1411 "New project</i> or <i>File > Edit project</i>"));
1414 void ReconstructionWidget::EnableBlockingActions() {
1415 for (
auto& action : blocking_actions_) {
1416 action->setEnabled(
true);
1420 void ReconstructionWidget::DisableBlockingActions() {
1421 for (
auto& action : blocking_actions_) {
1422 action->setDisabled(
true);
std::shared_ptr< core::Tensor > image
static const std::string path
std::string JoinPaths(T const &...paths)
bool HasFileExtension(const std::string &file_name, const std::string &ext)
std::string StringPrintf(const char *format,...)
Generic file read and write utility for python interface.
colmap::IncrementalMapperController IncrementalMapperController