44 const static std::string kFusedFileName =
"fused.ply";
45 const static std::string kPoissonMeshedFileName =
"meshed-poisson.ply";
46 const static std::string kDelaunayMeshedFileName =
"meshed-delaunay.ply";
48 class StereoOptionsTab :
public OptionsWidget {
51 : OptionsWidget(parent) {
53 if (options->patch_match_stereo->max_image_size == -1) {
54 options->patch_match_stereo->max_image_size = 2000;
57 AddOptionInt(&options->patch_match_stereo->max_image_size,
"max_image_size",
59 AddOptionText(&options->patch_match_stereo->gpu_index,
"gpu_index");
60 AddOptionDouble(&options->patch_match_stereo->depth_min,
"depth_min", -1);
61 AddOptionDouble(&options->patch_match_stereo->depth_max,
"depth_max", -1);
62 AddOptionInt(&options->patch_match_stereo->window_radius,
"window_radius");
63 AddOptionInt(&options->patch_match_stereo->window_step,
"window_step");
64 AddOptionDouble(&options->patch_match_stereo->sigma_spatial,
66 AddOptionDouble(&options->patch_match_stereo->sigma_color,
"sigma_color");
67 AddOptionInt(&options->patch_match_stereo->num_samples,
"num_samples");
68 AddOptionDouble(&options->patch_match_stereo->ncc_sigma,
"ncc_sigma");
69 AddOptionDouble(&options->patch_match_stereo->min_triangulation_angle,
70 "min_triangulation_angle");
71 AddOptionDouble(&options->patch_match_stereo->incident_angle_sigma,
72 "incident_angle_sigma");
73 AddOptionInt(&options->patch_match_stereo->num_iterations,
75 AddOptionBool(&options->patch_match_stereo->geom_consistency,
77 AddOptionDouble(&options->patch_match_stereo->geom_consistency_regularizer,
78 "geom_consistency_regularizer");
79 AddOptionDouble(&options->patch_match_stereo->geom_consistency_max_cost,
80 "geom_consistency_max_cost");
81 AddOptionBool(&options->patch_match_stereo->filter,
"filter");
82 AddOptionDouble(&options->patch_match_stereo->filter_min_ncc,
85 &options->patch_match_stereo->filter_min_triangulation_angle,
86 "filter_min_triangulation_angle");
87 AddOptionInt(&options->patch_match_stereo->filter_min_num_consistent,
88 "filter_min_num_consistent");
90 &options->patch_match_stereo->filter_geom_consistency_max_cost,
91 "filter_geom_consistency_max_cost");
92 AddOptionDouble(&options->patch_match_stereo->cache_size,
93 "cache_size [gigabytes]", 0,
94 std::numeric_limits<double>::max(), 0.1, 1);
95 AddOptionBool(&options->patch_match_stereo->write_consistency_graph,
96 "write_consistency_graph");
100 class FusionOptionsTab :
public OptionsWidget {
103 : OptionsWidget(parent) {
104 AddOptionInt(&options->stereo_fusion->max_image_size,
"max_image_size", -1);
105 AddOptionInt(&options->stereo_fusion->min_num_pixels,
"min_num_pixels", 0);
106 AddOptionInt(&options->stereo_fusion->max_num_pixels,
"max_num_pixels", 0);
107 AddOptionInt(&options->stereo_fusion->max_traversal_depth,
108 "max_traversal_depth", 1);
109 AddOptionDouble(&options->stereo_fusion->max_reproj_error,
110 "max_reproj_error", 0);
111 AddOptionDouble(&options->stereo_fusion->max_depth_error,
"max_depth_error",
113 AddOptionDouble(&options->stereo_fusion->max_normal_error,
114 "max_normal_error", 0, 180);
115 AddOptionInt(&options->stereo_fusion->check_num_images,
"check_num_images",
117 AddOptionDouble(&options->stereo_fusion->cache_size,
118 "cache_size [gigabytes]", 0,
119 std::numeric_limits<double>::max(), 0.1, 1);
123 class MeshingOptionsTab :
public OptionsWidget {
126 : OptionsWidget(parent) {
127 AddSection(
"Poisson Meshing");
128 AddOptionDouble(&options->poisson_meshing->point_weight,
"point_weight", 0);
129 AddOptionInt(&options->poisson_meshing->depth,
"depth", 1);
130 AddOptionDouble(&options->poisson_meshing->color,
"color", 0);
131 AddOptionDouble(&options->poisson_meshing->trim,
"trim", 0);
132 AddOptionInt(&options->poisson_meshing->num_threads,
"num_threads", -1);
134 AddSection(
"Delaunay Meshing");
135 AddOptionDouble(&options->delaunay_meshing->max_proj_dist,
"max_proj_dist",
137 AddOptionDouble(&options->delaunay_meshing->max_depth_dist,
138 "max_depth_dist", 0);
139 AddOptionDouble(&options->delaunay_meshing->distance_sigma_factor,
140 "distance_sigma_factor", 0);
141 AddOptionDouble(&options->delaunay_meshing->quality_regularization,
142 "quality_regularization", 0);
143 AddOptionDouble(&options->delaunay_meshing->max_side_length_factor,
144 "max_side_length_factor", 0);
145 AddOptionDouble(&options->delaunay_meshing->max_side_length_percentile,
146 "max_side_length_percentile", 0);
147 AddOptionInt(&options->delaunay_meshing->num_threads,
"num_threads", -1);
151 class TexturingOptionsTab :
public OptionsWidget {
154 : OptionsWidget(parent),
156 mesh_source_combo_(nullptr) {
157 AddOptionBool(&options->texturing->verbose,
"verbose");
160 mesh_source_combo_ =
new QComboBox(
this);
161 mesh_source_combo_->addItem(
"Auto (Delaunay preferred)",
"auto");
162 mesh_source_combo_->addItem(
"Delaunay",
"delaunay");
163 mesh_source_combo_->addItem(
"Poisson",
"poisson");
166 connect(mesh_source_combo_,
167 QOverload<int>::of(&QComboBox::currentIndexChanged), [
this](
int index) {
168 QString
data = mesh_source_combo_->itemData(index).
toString();
169 options_->texturing->mesh_source =
data.toStdString();
172 AddWidgetRow(
"mesh_source", mesh_source_combo_);
174 AddOptionFilePath(&options->texturing->meshed_file_path,
176 AddOptionDirPath(&options->texturing->textured_file_path,
177 "textured_file_path");
179 AddSection(
"Advanced Options");
180 AddOptionBool(&options->texturing->use_depth_normal_maps,
181 "use_depth_normal_maps");
184 QComboBox* depth_type_combo =
new QComboBox(
this);
185 depth_type_combo->addItem(
"Geometric",
"geometric");
186 depth_type_combo->addItem(
"Photometric",
"photometric");
187 connect(depth_type_combo,
188 QOverload<int>::of(&QComboBox::currentIndexChanged),
189 [
this, depth_type_combo, options](
int index) {
190 QString
data = depth_type_combo->itemData(index).
toString();
191 options->texturing->depth_map_type =
data.toStdString();
194 if (options->texturing->depth_map_type ==
"geometric") {
195 depth_type_combo->setCurrentIndex(0);
197 depth_type_combo->setCurrentIndex(1);
199 AddWidgetRow(
"depth_map_type", depth_type_combo);
201 AddOptionDouble(&options->texturing->max_depth_error,
"max_depth_error",
203 AddOptionDouble(&options->texturing->min_normal_consistency,
204 "min_normal_consistency", -1, 1, 0.01, 2);
205 AddOptionDouble(&options->texturing->max_viewing_angle_deg,
206 "max_viewing_angle_deg", 0, 180, 1, 1);
207 AddOptionBool(&options->texturing->use_gradient_magnitude,
208 "use_gradient_magnitude");
212 void showEvent(QShowEvent*
event)
override {
215 if (mesh_source_combo_) {
216 std::string current_source = options_->texturing->mesh_source;
217 if (current_source ==
"poisson") {
218 mesh_source_combo_->setCurrentIndex(2);
219 }
else if (current_source ==
"delaunay") {
220 mesh_source_combo_->setCurrentIndex(1);
222 mesh_source_combo_->setCurrentIndex(0);
229 QComboBox* mesh_source_combo_;
233 std::vector<std::pair<std::string, std::string>> ReadPatchMatchConfig(
234 const std::string& config_path) {
235 std::ifstream file(config_path);
236 CHECK(file.is_open()) << config_path;
239 std::string ref_image_name;
240 std::vector<std::pair<std::string, std::string>> images;
241 while (std::getline(file, line)) {
244 if (line.empty() || line[0] ==
'#') {
248 if (ref_image_name.empty()) {
249 ref_image_name = line;
251 images.emplace_back(ref_image_name, line);
252 ref_image_name.clear();
264 setWindowFlags(Qt::Dialog);
265 setWindowModality(Qt::ApplicationModal);
266 setWindowTitle(
"Dense reconstruction options");
268 QGridLayout* grid =
new QGridLayout(
this);
270 QTabWidget* tab_widget =
new QTabWidget(
this);
271 tab_widget->setElideMode(Qt::TextElideMode::ElideRight);
272 tab_widget->addTab(
new StereoOptionsTab(
this, options),
"Stereo");
273 tab_widget->addTab(
new FusionOptionsTab(
this, options),
"Fusion");
274 tab_widget->addTab(
new MeshingOptionsTab(
this, options),
"Meshing");
275 tab_widget->addTab(
new TexturingOptionsTab(
this, options),
"Texturing");
277 grid->addWidget(tab_widget, 0, 0);
282 : QWidget(main_window),
283 main_window_(main_window),
285 reconstruction_(nullptr),
288 photometric_done_(false),
289 geometric_done_(false) {
290 setWindowFlags(Qt::Dialog);
291 setWindowModality(Qt::ApplicationModal);
292 setWindowTitle(
"Dense reconstruction");
293 resize(main_window_->size().width() - 20, main_window_->size().height() - 20);
295 QGridLayout* grid =
new QGridLayout(
this);
297 undistortion_button_ =
new QPushButton(tr(
"Undistortion"),
this);
298 connect(undistortion_button_, &QPushButton::released,
this,
299 &DenseReconstructionWidget::Undistort);
300 grid->addWidget(undistortion_button_, 0, 0, Qt::AlignLeft);
302 stereo_button_ =
new QPushButton(tr(
"Stereo"),
this);
303 connect(stereo_button_, &QPushButton::released,
this,
304 &DenseReconstructionWidget::Stereo);
305 grid->addWidget(stereo_button_, 0, 1, Qt::AlignLeft);
307 fusion_button_ =
new QPushButton(tr(
"Fusion"),
this);
308 connect(fusion_button_, &QPushButton::released,
this,
309 &DenseReconstructionWidget::Fusion);
310 grid->addWidget(fusion_button_, 0, 2, Qt::AlignLeft);
312 poisson_meshing_button_ =
new QPushButton(tr(
"Poisson"),
this);
313 connect(poisson_meshing_button_, &QPushButton::released,
this,
314 &DenseReconstructionWidget::PoissonMeshing);
315 grid->addWidget(poisson_meshing_button_, 0, 3, Qt::AlignLeft);
317 delaunay_meshing_button_ =
new QPushButton(tr(
"Delaunay"),
this);
318 connect(delaunay_meshing_button_, &QPushButton::released,
this,
319 &DenseReconstructionWidget::DelaunayMeshing);
320 grid->addWidget(delaunay_meshing_button_, 0, 4, Qt::AlignLeft);
322 texturing_button_ =
new QPushButton(tr(
"Texturing"),
this);
323 connect(texturing_button_, &QPushButton::released,
this,
324 &DenseReconstructionWidget::Texturing);
325 grid->addWidget(texturing_button_, 0, 5, Qt::AlignLeft);
327 QPushButton* options_button =
new QPushButton(tr(
"Options"),
this);
328 connect(options_button, &QPushButton::released, options_widget_,
329 &OptionsWidget::show);
330 grid->addWidget(options_button, 0, 6, Qt::AlignLeft);
332 QLabel* workspace_path_label =
new QLabel(
"Workspace",
this);
333 grid->addWidget(workspace_path_label, 0, 7, Qt::AlignRight);
335 workspace_path_text_ =
new QLineEdit(
this);
336 grid->addWidget(workspace_path_text_, 0, 8, Qt::AlignRight);
337 connect(workspace_path_text_, &QLineEdit::textChanged,
this,
338 &DenseReconstructionWidget::RefreshWorkspace, Qt::QueuedConnection);
340 QPushButton* refresh_path_button =
new QPushButton(tr(
"Refresh"),
this);
341 connect(refresh_path_button, &QPushButton::released,
this,
342 &DenseReconstructionWidget::RefreshWorkspace, Qt::QueuedConnection);
343 grid->addWidget(refresh_path_button, 0, 9, Qt::AlignRight);
345 QPushButton* workspace_path_button =
new QPushButton(tr(
"Select"),
this);
346 connect(workspace_path_button, &QPushButton::released,
this,
347 &DenseReconstructionWidget::SelectWorkspacePath,
348 Qt::QueuedConnection);
349 grid->addWidget(workspace_path_button, 0, 10, Qt::AlignRight);
351 QStringList table_header;
352 table_header <<
"image_name"
358 table_widget_ =
new QTableWidget(
this);
359 table_widget_->setColumnCount(table_header.size());
360 table_widget_->setHorizontalHeaderLabels(table_header);
362 table_widget_->setShowGrid(
true);
363 table_widget_->setSelectionBehavior(QAbstractItemView::SelectRows);
364 table_widget_->setSelectionMode(QAbstractItemView::SingleSelection);
365 table_widget_->setEditTriggers(QAbstractItemView::NoEditTriggers);
366 table_widget_->verticalHeader()->setDefaultSectionSize(25);
368 grid->addWidget(table_widget_, 1, 0, 1, 10);
370 grid->setColumnStretch(4, 1);
373 image_viewer_widget_->setWindowModality(Qt::ApplicationModal);
375 refresh_workspace_action_ =
new QAction(
this);
376 connect(refresh_workspace_action_, &QAction::triggered,
this,
377 &DenseReconstructionWidget::RefreshWorkspace);
379 write_fused_points_action_ =
new QAction(
this);
380 connect(write_fused_points_action_, &QAction::triggered,
this,
381 &DenseReconstructionWidget::WriteFusedPoints);
383 show_meshing_info_action_ =
new QAction(
this);
384 connect(show_meshing_info_action_, &QAction::triggered,
this,
385 &DenseReconstructionWidget::ShowMeshingInfo);
390 void DenseReconstructionWidget::showEvent(QShowEvent*
event) {
394 if (workspace_path_text_->text().isEmpty()) {
395 std::string default_workspace;
402 workspace_path_text_->setText(QString::fromStdString(default_workspace));
403 std::cout <<
"Auto-loaded workspace from project_path: " << default_workspace <<
std::endl;
413 workspace_path_text_->setText(QString::fromStdString(default_workspace));
414 std::cout <<
"Auto-loaded workspace from database_path: " << default_workspace <<
std::endl;
428 workspace_path_text_->setText(QString::fromStdString(default_workspace));
429 std::cout <<
"Auto-loaded workspace from image_path: " << default_workspace <<
std::endl;
435 std::cout <<
"Could not auto-load workspace path. Please select manually." <<
std::endl;
442 reconstruction_ = reconstruction;
447 void DenseReconstructionWidget::Undistort() {
448 const std::string workspace_path = GetWorkspacePath();
449 if (workspace_path.empty()) {
453 if (reconstruction_ ==
nullptr || reconstruction_->
NumRegImages() < 2) {
454 QMessageBox::critical(
this,
"",
455 tr(
"No reconstruction selected in main window"));
459 COLMAPUndistorter* undistorter =
460 new COLMAPUndistorter(UndistortCameraOptions(), reconstruction_,
463 [
this]() { refresh_workspace_action_->trigger(); });
464 thread_control_widget_->
StartThread(
"Undistorting...",
true, undistorter);
467 void DenseReconstructionWidget::Stereo() {
468 const std::string workspace_path = GetWorkspacePath();
469 if (workspace_path.empty()) {
474 mvs::PatchMatchController* processor =
new mvs::PatchMatchController(
477 [
this]() { refresh_workspace_action_->trigger(); });
478 thread_control_widget_->
StartThread(
"Stereo...",
true, processor);
480 QMessageBox::critical(
this,
"",
481 tr(
"Dense stereo reconstruction requires CUDA, which "
482 "is not available on your system."));
486 void DenseReconstructionWidget::Fusion() {
487 const std::string workspace_path = GetWorkspacePath();
488 if (workspace_path.empty()) {
492 std::string input_type;
493 if (geometric_done_) {
494 input_type =
"geometric";
495 }
else if (photometric_done_) {
496 input_type =
"photometric";
498 QMessageBox::critical(
this,
"",
499 tr(
"All images must be processed prior to fusion"));
502 mvs::StereoFusion* fuser =
new mvs::StereoFusion(
503 *options_->
stereo_fusion, workspace_path,
"COLMAP",
"", input_type);
505 fused_points_ = fuser->GetFusedPoints();
506 fused_points_visibility_ = fuser->GetFusedPointsVisibility();
507 write_fused_points_action_->trigger();
509 thread_control_widget_->
StartThread(
"Fusion...",
true, fuser);
512 void DenseReconstructionWidget::PoissonMeshing() {
513 const std::string workspace_path = GetWorkspacePath();
514 if (workspace_path.empty()) {
520 "Poisson Meshing...", [
this, workspace_path]() {
523 JoinPaths(workspace_path, kFusedFileName),
524 JoinPaths(workspace_path, kPoissonMeshedFileName));
525 refresh_workspace_action_->trigger();
526 show_meshing_info_action_->trigger();
531 void DenseReconstructionWidget::DelaunayMeshing() {
533 const std::string workspace_path = GetWorkspacePath();
534 if (workspace_path.empty()) {
540 "Delaunay Meshing...", [
this, workspace_path]() {
541 mvs::DenseDelaunayMeshing(
543 JoinPaths(workspace_path, kDelaunayMeshedFileName));
544 refresh_workspace_action_->trigger();
545 show_meshing_info_action_->trigger();
549 QMessageBox::critical(
this,
"",
550 tr(
"Delaunay meshing requires CGAL, which "
551 "is not available on your system."));
555 void DenseReconstructionWidget::Texturing() {
556 const std::string workspace_path = GetWorkspacePath();
557 if (workspace_path.empty()) {
561 if (reconstruction_ ==
nullptr || reconstruction_->
NumRegImages() < 2) {
562 QMessageBox::critical(
this,
"",
563 tr(
"No reconstruction selected in main window"));
569 const std::string poisson_path =
570 JoinPaths(workspace_path, kPoissonMeshedFileName);
571 const std::string delaunay_path =
572 JoinPaths(workspace_path, kDelaunayMeshedFileName);
573 const bool poisson_exists =
ExistsFile(poisson_path);
574 const bool delaunay_exists =
ExistsFile(delaunay_path);
576 if (options_->
texturing->mesh_source ==
"delaunayx") {
577 if (delaunay_exists) {
578 options_->
texturing->meshed_file_path = delaunay_path;
580 QMessageBox::critical(
this,
"",
581 tr(
"Delaunay mesh file not found. Please "
582 "run Delaunay meshing first."));
585 }
else if (options_->
texturing->mesh_source ==
"poisson") {
586 if (poisson_exists) {
587 options_->
texturing->meshed_file_path = poisson_path;
589 QMessageBox::critical(
this,
"",
590 tr(
"Poisson mesh file not found. Please "
591 "run Poisson meshing first."));
595 if (delaunay_exists) {
596 options_->
texturing->meshed_file_path = delaunay_path;
597 }
else if (poisson_exists) {
598 options_->
texturing->meshed_file_path = poisson_path;
600 QMessageBox::critical(
this,
"",
601 tr(
"No mesh file found. Please run "
602 "Poisson or Delaunay meshing first."));
609 if (options_->
texturing->textured_file_path.empty()) {
610 options_->
texturing->textured_file_path =
611 JoinPaths(workspace_path,
"textured-mesh.obj");
614 TexturingReconstruction* texturing_tool =
615 new TexturingReconstruction(*options_->
texturing, *reconstruction_,
618 show_meshing_info_action_->trigger();
620 thread_control_widget_->
StartThread(
"Texturing...",
true, texturing_tool);
623 void DenseReconstructionWidget::SelectWorkspacePath() {
624 std::string workspace_path;
625 if (workspace_path_text_->text().isEmpty()) {
628 workspace_path = workspace_path_text_->text().toUtf8().constData();
631 workspace_path_text_->setText(QFileDialog::getExistingDirectory(
632 this, tr(
"Select workspace path..."),
633 QString::fromStdString(workspace_path), QFileDialog::ShowDirsOnly));
638 std::string DenseReconstructionWidget::GetWorkspacePath() {
639 const std::string workspace_path =
640 workspace_path_text_->text().toUtf8().constData();
642 return workspace_path;
644 QMessageBox::critical(
this,
"", tr(
"Invalid workspace path"));
649 void DenseReconstructionWidget::RefreshWorkspace() {
650 table_widget_->clearContents();
651 table_widget_->setRowCount(0);
653 const std::string workspace_path =
654 workspace_path_text_->text().toUtf8().constData();
656 undistortion_button_->setEnabled(
true);
658 undistortion_button_->setEnabled(
false);
659 stereo_button_->setEnabled(
false);
660 fusion_button_->setEnabled(
false);
661 poisson_meshing_button_->setEnabled(
false);
662 delaunay_meshing_button_->setEnabled(
false);
663 texturing_button_->setEnabled(
false);
667 images_path_ =
JoinPaths(workspace_path,
"images");
668 depth_maps_path_ =
JoinPaths(workspace_path,
"stereo/depth_maps");
669 normal_maps_path_ =
JoinPaths(workspace_path,
"stereo/normal_maps");
670 const std::string config_path =
671 JoinPaths(workspace_path,
"stereo/patch-match.cfg");
678 stereo_button_->setEnabled(
true);
680 stereo_button_->setEnabled(
false);
681 fusion_button_->setEnabled(
false);
682 poisson_meshing_button_->setEnabled(
false);
683 delaunay_meshing_button_->setEnabled(
false);
684 texturing_button_->setEnabled(
false);
688 const auto images = ReadPatchMatchConfig(config_path);
689 table_widget_->setRowCount(images.size());
691 for (
size_t i = 0; i < images.size(); ++i) {
692 const std::string image_name = images[i].first;
693 const std::string src_images = images[i].second;
694 const std::string image_path =
JoinPaths(images_path_, image_name);
696 QTableWidgetItem* image_name_item =
697 new QTableWidgetItem(QString::fromStdString(image_name));
698 table_widget_->setItem(i, 0, image_name_item);
700 QPushButton* image_button =
new QPushButton(
"Image");
701 connect(image_button, &QPushButton::released,
702 [
this, image_name, image_path]() {
703 image_viewer_widget_->setWindowTitle(
704 QString(
"Image for %1").arg(image_name.c_str()));
707 table_widget_->setCellWidget(i, 1, image_button);
709 table_widget_->setCellWidget(
710 i, 2, GenerateTableButtonWidget(image_name,
"photometric"));
711 table_widget_->setCellWidget(
712 i, 3, GenerateTableButtonWidget(image_name,
"geometric"));
714 QTableWidgetItem* src_images_item =
715 new QTableWidgetItem(QString::fromStdString(src_images));
716 table_widget_->setItem(i, 4, src_images_item);
719 table_widget_->resizeColumnsToContents();
721 fusion_button_->setEnabled(photometric_done_ || geometric_done_);
722 poisson_meshing_button_->setEnabled(
724 delaunay_meshing_button_->setEnabled(
726 texturing_button_->setEnabled(
731 void DenseReconstructionWidget::WriteFusedPoints() {
732 const int reply = QMessageBox::question(
734 tr(
"Do you want to visualize the point cloud? Otherwise, to visualize "
735 "the reconstructed dense point cloud later, navigate to the "
736 "<i>dense</i> sub-folder in your workspace with <i>File > Import "
737 "model from...</i>."),
738 QMessageBox::Yes | QMessageBox::No);
739 if (reply == QMessageBox::Yes) {
740 const size_t reconstruction_idx =
741 main_window_->reconstruction_manager_.
Add();
742 auto& reconstruction =
743 main_window_->reconstruction_manager_.
Get(reconstruction_idx);
745 for (
const auto& point : fused_points_) {
746 const Eigen::Vector3d xyz(point.x, point.y, point.z);
747 reconstruction.AddPoint3D(xyz, Track(),
751 options_->
render->min_track_len = 0;
752 main_window_->reconstruction_manager_widget_->
Update();
755 main_window_->RenderNow();
758 const std::string workspace_path =
759 workspace_path_text_->text().toUtf8().constData();
760 if (workspace_path.empty()) {
762 fused_points_visibility_ = {};
768 const std::string output_path =
JoinPaths(workspace_path, kFusedFileName);
772 fused_points_visibility_ = {};
773 poisson_meshing_button_->setEnabled(
true);
774 delaunay_meshing_button_->setEnabled(
true);
778 void DenseReconstructionWidget::ShowMeshingInfo() {
779 QMessageBox::information(
781 tr(
"To visualize the meshed model, you must use an external viewer such "
782 "as Meshlab. The model is located in the workspace folder."));
785 QWidget* DenseReconstructionWidget::GenerateTableButtonWidget(
786 const std::string& image_name,
const std::string&
type) {
787 CHECK(
type ==
"photometric" ||
type ==
"geometric");
788 const bool photometric =
type ==
"photometric";
791 photometric_done_ =
true;
793 geometric_done_ =
true;
796 const std::string depth_map_path =
799 const std::string normal_map_path =
803 QWidget* button_widget =
new QWidget();
804 QGridLayout* button_layout =
new QGridLayout(button_widget);
805 button_layout->setContentsMargins(0, 0, 0, 0);
807 QPushButton* depth_map_button =
new QPushButton(
"Depth map", button_widget);
809 connect(depth_map_button, &QPushButton::released,
810 [
this, image_name, depth_map_path]() {
811 mvs::DepthMap depth_map;
812 depth_map.Read(depth_map_path);
813 image_viewer_widget_->setWindowTitle(
814 QString(
"Depth map for %1").arg(image_name.c_str()));
815 image_viewer_widget_->
ShowBitmap(depth_map.ToBitmap(2, 98));
818 depth_map_button->setEnabled(
false);
820 photometric_done_ =
false;
822 geometric_done_ =
false;
825 button_layout->addWidget(depth_map_button, 0, 1, Qt::AlignLeft);
827 QPushButton* normal_map_button =
new QPushButton(
"Normal map", button_widget);
829 connect(normal_map_button, &QPushButton::released,
830 [
this, image_name, normal_map_path]() {
831 mvs::NormalMap normal_map;
832 normal_map.Read(normal_map_path);
833 image_viewer_widget_->setWindowTitle(
834 QString(
"Normal map for %1").arg(image_name.c_str()));
835 image_viewer_widget_->
ShowBitmap(normal_map.ToBitmap());
838 normal_map_button->setEnabled(
false);
840 photometric_done_ =
false;
842 geometric_done_ =
false;
845 button_layout->addWidget(normal_map_button, 0, 2, Qt::AlignLeft);
847 return button_widget;
std::shared_ptr< mvs::PoissonMeshingOptions > poisson_meshing
std::shared_ptr< mvs::DelaunayMeshingOptions > delaunay_meshing
std::shared_ptr< TexturingOptions > texturing
std::shared_ptr< mvs::PatchMatchOptions > patch_match_stereo
std::shared_ptr< RenderOptions > render
std::shared_ptr< std::string > database_path
std::shared_ptr< mvs::StereoFusionOptions > stereo_fusion
std::shared_ptr< std::string > project_path
std::shared_ptr< std::string > image_path
const Reconstruction & Get(const size_t idx) const
size_t NumRegImages() const
Matrix< uint8_t, 3, 1 > Vector3ub
QTextStream & endl(QTextStream &stream)
colmap::OptionManager OptionManager
void WritePointsVisibility(const std::string &path, const std::vector< std::vector< int >> &points_visibility)
bool PoissonMeshing(const PoissonMeshingOptions &options, const std::string &input_path, const std::string &output_path)
void StringTrim(std::string *str)
void WriteBinaryPlyPoints(const std::string &path, const std::vector< PlyPoint > &points, const bool write_normal, const bool write_rgb)
bool ExistsDir(const std::string &path)
bool ExistsFile(const std::string &path)
std::string JoinPaths(T const &... paths)
std::string StringPrintf(const char *format,...)
std::string GetParentDir(const std::string &path)
std::string toString(T x)