10 #include "base/camera_models.h"
11 #include "util/misc.h"
12 #include "util/option_manager.h"
24 matches_viewer_widget_(new FeatureImageViewerWidget(parent,
"matches")) {}
26 void TwoViewInfoTab::Clear() {
27 table_widget_->clearContents();
30 sorted_matches_idxs_.clear();
33 void TwoViewInfoTab::InitializeTable(
const QStringList& table_header) {
34 QGridLayout* grid =
new QGridLayout(
this);
36 info_label_ =
new QLabel(
this);
37 grid->addWidget(info_label_, 0, 0);
39 QPushButton* show_button =
new QPushButton(tr(
"Show matches"),
this);
40 connect(show_button, &QPushButton::released,
this,
41 &TwoViewInfoTab::ShowMatches);
42 grid->addWidget(show_button, 0, 1, Qt::AlignRight);
44 table_widget_ =
new QTableWidget(
this);
45 table_widget_->setColumnCount(table_header.size());
46 table_widget_->setHorizontalHeaderLabels(table_header);
48 table_widget_->setShowGrid(
true);
49 table_widget_->setSelectionBehavior(QAbstractItemView::SelectRows);
50 table_widget_->setSelectionMode(QAbstractItemView::SingleSelection);
51 table_widget_->setEditTriggers(QAbstractItemView::NoEditTriggers);
52 table_widget_->horizontalHeader()->setStretchLastSection(
true);
53 table_widget_->verticalHeader()->setVisible(
false);
54 table_widget_->verticalHeader()->setDefaultSectionSize(20);
56 grid->addWidget(table_widget_, 1, 0, 1, 2);
59 void TwoViewInfoTab::ShowMatches() {
60 QItemSelectionModel* select = table_widget_->selectionModel();
62 if (!select->hasSelection()) {
63 QMessageBox::critical(
this,
"", tr(
"No image pair selected."));
67 if (select->selectedRows().size() > 1) {
68 QMessageBox::critical(
this,
"",
69 tr(
"Only one image pair may be selected."));
74 sorted_matches_idxs_[select->selectedRows().begin()->row()];
75 const auto& selection = matches_[idx];
76 const std::string path1 =
JoinPaths(*options_->image_path, image_->Name());
77 const std::string path2 =
78 JoinPaths(*options_->image_path, selection.first->Name());
79 const auto keypoints1 = database_->ReadKeypoints(image_->ImageId());
80 const auto keypoints2 =
81 database_->ReadKeypoints(selection.first->ImageId());
83 matches_viewer_widget_->setWindowTitle(QString::fromStdString(
86 matches_viewer_widget_->ReadAndShowWithMatches(
87 path1, path2, keypoints1, keypoints2, selection.second);
90 void TwoViewInfoTab::FillTable() {
93 sorted_matches_idxs_.resize(matches_.size());
94 std::iota(sorted_matches_idxs_.begin(), sorted_matches_idxs_.end(), 0);
96 std::sort(sorted_matches_idxs_.begin(), sorted_matches_idxs_.end(),
97 [&](
const size_t idx1,
const size_t idx2) {
98 return matches_[idx1].second.size() >
99 matches_[idx2].second.size();
103 info += QString(
"Matched images: ") + QString::number(matches_.size());
104 info_label_->setText(info);
106 table_widget_->clearContents();
107 table_widget_->setRowCount(matches_.size());
109 for (
size_t i = 0; i < sorted_matches_idxs_.size(); ++i) {
110 const size_t idx = sorted_matches_idxs_[i];
112 QTableWidgetItem* image_id_item =
new QTableWidgetItem(
113 QString::number(matches_[idx].first->ImageId()));
114 table_widget_->setItem(i, 0, image_id_item);
116 QTableWidgetItem* num_matches_item =
new QTableWidgetItem(
117 QString::number(matches_[idx].second.size()));
118 table_widget_->setItem(i, 1, num_matches_item);
121 if (table_widget_->columnCount() == 3) {
122 QTableWidgetItem* config_item =
123 new QTableWidgetItem(QString::number(configs_[idx]));
124 table_widget_->setItem(i, 2, config_item);
128 table_widget_->resizeColumnsToContents();
131 MatchesTab::MatchesTab(QWidget* parent,
135 QStringList table_header;
136 table_header <<
"image_id"
142 const image_t image_id) {
147 for (
const auto&
image : images) {
148 if (
image.ImageId() == image_id) {
157 if (matches.size() > 0) {
170 QStringList table_header;
171 table_header <<
"image_id"
178 const image_t image_id) {
184 for (
const auto&
image : images) {
185 if (
image.ImageId() == image_id) {
191 const auto two_view_geometry =
194 if (two_view_geometry.inlier_matches.size() > 0) {
195 matches_.emplace_back(&
image, two_view_geometry.inlier_matches);
196 configs_.push_back(two_view_geometry.config);
207 : parent_(parent), options_(options) {
211 resize(parent->size().width() - 20, parent->size().height() - 20);
213 QGridLayout* grid =
new QGridLayout(
this);
215 tab_widget_ =
new QTabWidget(
this);
217 matches_tab_ =
new MatchesTab(
this, options_, database);
218 tab_widget_->addTab(matches_tab_, tr(
"Matches"));
220 two_view_geometries_tab_ =
222 tab_widget_->addTab(two_view_geometries_tab_, tr(
"Two-view geometries"));
224 grid->addWidget(tab_widget_, 0, 0);
226 QPushButton* close_button =
new QPushButton(tr(
"Close"),
this);
227 connect(close_button, &QPushButton::released,
this,
228 &OverlappingImagesWidget::close);
229 grid->addWidget(close_button, 1, 0, Qt::AlignRight);
233 const image_t image_id) {
234 parent_->setDisabled(
true);
236 setWindowTitle(QString::fromStdString(
"Matches for image " +
239 matches_tab_->
Reload(images, image_id);
240 two_view_geometries_tab_->
Reload(images, image_id);
243 void OverlappingImagesWidget::closeEvent(QCloseEvent*) {
244 matches_tab_->
Clear();
245 two_view_geometries_tab_->
Clear();
246 parent_->setEnabled(
true);
250 : QWidget(parent), database_(database) {
251 QGridLayout* grid =
new QGridLayout(
this);
253 info_label_ =
new QLabel(
this);
254 grid->addWidget(info_label_, 0, 0);
256 QPushButton* add_camera_button =
new QPushButton(tr(
"Add camera"),
this);
257 connect(add_camera_button, &QPushButton::released,
this, &CameraTab::Add);
258 grid->addWidget(add_camera_button, 0, 1, Qt::AlignRight);
260 QPushButton* set_model_button =
new QPushButton(tr(
"Set model"),
this);
261 connect(set_model_button, &QPushButton::released,
this,
262 &CameraTab::SetModel);
263 grid->addWidget(set_model_button, 0, 2, Qt::AlignRight);
265 table_widget_ =
new QTableWidget(
this);
266 table_widget_->setColumnCount(6);
268 QStringList table_header;
269 table_header <<
"camera_id"
274 <<
"prior_focal_length";
275 table_widget_->setHorizontalHeaderLabels(table_header);
277 table_widget_->setShowGrid(
true);
278 table_widget_->setSelectionBehavior(QAbstractItemView::SelectRows);
279 table_widget_->horizontalHeader()->setStretchLastSection(
true);
280 table_widget_->verticalHeader()->setVisible(
false);
281 table_widget_->verticalHeader()->setDefaultSectionSize(20);
283 connect(table_widget_, &QTableWidget::itemChanged,
this,
284 &CameraTab::itemChanged);
286 grid->addWidget(table_widget_, 1, 0, 1, 3);
288 grid->setColumnStretch(0, 1);
293 info += QString(
"Cameras: ") + QString::number(database_->NumCameras());
294 info_label_->setText(info);
296 cameras_ = database_->ReadAllCameras();
299 table_widget_->blockSignals(
true);
301 table_widget_->clearContents();
302 table_widget_->setRowCount(cameras_.size());
304 std::sort(cameras_.begin(), cameras_.end(),
305 [](
const Camera& camera1,
const Camera& camera2) {
306 return camera1.CameraId() < camera2.CameraId();
309 for (
size_t i = 0; i < cameras_.size(); ++i) {
310 const Camera& camera = cameras_[i];
311 QTableWidgetItem* id_item =
312 new QTableWidgetItem(QString::number(camera.CameraId()));
313 id_item->setFlags(Qt::ItemIsSelectable);
314 table_widget_->setItem(i, 0, id_item);
316 QTableWidgetItem* model_item =
new QTableWidgetItem(
317 QString::fromStdString(camera.ModelName()));
318 model_item->setFlags(Qt::ItemIsSelectable);
319 table_widget_->setItem(i, 1, model_item);
321 table_widget_->setItem(
322 i, 2,
new QTableWidgetItem(QString::number(camera.Width())));
323 table_widget_->setItem(
324 i, 3,
new QTableWidgetItem(QString::number(camera.Height())));
326 table_widget_->setItem(i, 4,
327 new QTableWidgetItem(QString::fromStdString(
328 VectorToCSV(camera.Params()))));
329 table_widget_->setItem(i, 5,
330 new QTableWidgetItem(QString::number(
331 camera.HasPriorFocalLength())));
333 table_widget_->resizeColumnsToContents();
335 table_widget_->blockSignals(
false);
340 table_widget_->clearContents();
343 void CameraTab::itemChanged(QTableWidgetItem* item) {
344 Camera& camera = cameras_.at(item->row());
345 const std::vector<double> prev_params = camera.Params();
347 switch (item->column()) {
352 static_cast<size_t>(item->data(Qt::DisplayRole).toInt()));
356 static_cast<size_t>(item->data(Qt::DisplayRole).toInt()));
359 if (!camera.SetParamsFromString(
360 item->text().toUtf8().constData())) {
361 QMessageBox::critical(
this,
"",
362 tr(
"Invalid camera parameters."));
363 table_widget_->blockSignals(
true);
364 item->setText(QString::fromStdString(VectorToCSV(prev_params)));
365 table_widget_->blockSignals(
false);
369 camera.SetPriorFocalLength(
370 static_cast<bool>(item->data(Qt::DisplayRole).toInt()));
376 database_->UpdateCamera(camera);
379 void CameraTab::Add() {
380 QStringList camera_models;
381 #define CAMERA_MODEL_CASE(CameraModel) \
382 << QString::fromStdString(CameraModelIdToName(CameraModel::model_id))
383 camera_models CAMERA_MODEL_CASES;
384 #undef CAMERA_MODEL_CASE
387 const QString camera_model = QInputDialog::getItem(
388 this,
"", tr(
"Model:"), camera_models, 0,
false, &ok);
395 const double kDefaultFocalLength = 1.0;
396 const size_t kDefaultWidth = 1;
397 const size_t kDefaultHeight = 1;
398 camera.InitializeWithName(camera_model.toUtf8().constData(),
399 kDefaultFocalLength, kDefaultWidth,
401 database_->WriteCamera(camera);
407 table_widget_->selectRow(cameras_.size() - 1);
410 void CameraTab::SetModel() {
411 QItemSelectionModel* select = table_widget_->selectionModel();
413 if (!select->hasSelection()) {
414 QMessageBox::critical(
this,
"", tr(
"No camera selected."));
418 QStringList camera_models;
419 #define CAMERA_MODEL_CASE(CameraModel) \
420 << QString::fromStdString(CameraModelIdToName(CameraModel::model_id))
421 camera_models CAMERA_MODEL_CASES;
422 #undef CAMERA_MODEL_CASE
425 const QString camera_model = QInputDialog::getItem(
426 this,
"", tr(
"Model:"), camera_models, 0,
false, &ok);
432 table_widget_->blockSignals(
true);
434 for (QModelIndex& index : select->selectedRows()) {
436 auto& camera = cameras_.at(index.row());
437 camera.InitializeWithName(camera_model.toUtf8().constData(),
438 camera.MeanFocalLength(), camera.Width(),
440 database_->UpdateCamera(camera);
443 table_widget_->blockSignals(
false);
453 camera_tab_(camera_tab),
455 database_(database) {
456 QGridLayout* grid =
new QGridLayout(
this);
458 info_label_ =
new QLabel(
this);
459 grid->addWidget(info_label_, 0, 0);
461 QPushButton* set_camera_button =
new QPushButton(tr(
"Set camera"),
this);
462 connect(set_camera_button, &QPushButton::released,
this,
463 &ImageTab::SetCamera);
464 grid->addWidget(set_camera_button, 0, 1, Qt::AlignRight);
466 QPushButton* split_camera_button =
467 new QPushButton(tr(
"Split camera"),
this);
468 connect(split_camera_button, &QPushButton::released,
this,
469 &ImageTab::SplitCamera);
470 grid->addWidget(split_camera_button, 0, 2, Qt::AlignRight);
472 QPushButton* show_image_button =
new QPushButton(tr(
"Show image"),
this);
473 connect(show_image_button, &QPushButton::released,
this,
474 &ImageTab::ShowImage);
475 grid->addWidget(show_image_button, 0, 3, Qt::AlignRight);
477 QPushButton* overlapping_images_button =
478 new QPushButton(tr(
"Overlapping images"),
this);
479 connect(overlapping_images_button, &QPushButton::released,
this,
480 &ImageTab::ShowMatches);
481 grid->addWidget(overlapping_images_button, 0, 4, Qt::AlignRight);
483 table_widget_ =
new QTableWidget(
this);
484 table_widget_->setColumnCount(10);
486 QStringList table_header;
487 table_header <<
"image_id"
497 table_widget_->setHorizontalHeaderLabels(table_header);
499 table_widget_->setShowGrid(
true);
500 table_widget_->setSelectionBehavior(QAbstractItemView::SelectRows);
501 table_widget_->horizontalHeader()->setStretchLastSection(
true);
502 table_widget_->verticalHeader()->setVisible(
false);
503 table_widget_->verticalHeader()->setDefaultSectionSize(20);
505 connect(table_widget_, &QTableWidget::itemChanged,
this,
506 &ImageTab::itemChanged);
508 grid->addWidget(table_widget_, 1, 0, 1, 5);
510 grid->setColumnStretch(0, 3);
513 overlapping_images_widget_ =
519 info += QString(
"Images: ") + QString::number(database_->NumImages());
520 info += QString(
"\n");
521 info += QString(
"Features: ") + QString::number(database_->NumKeypoints());
522 info_label_->setText(info);
528 images_ = database_->ReadAllImages();
531 table_widget_->blockSignals(
true);
533 table_widget_->clearContents();
534 table_widget_->setRowCount(images_.size());
536 for (
size_t i = 0; i < images_.size(); ++i) {
537 const auto&
image = images_[i];
538 QTableWidgetItem* id_item =
539 new QTableWidgetItem(QString::number(
image.ImageId()));
540 id_item->setFlags(Qt::ItemIsSelectable);
541 table_widget_->setItem(i, 0, id_item);
542 table_widget_->setItem(
544 new QTableWidgetItem(QString::fromStdString(
image.Name())));
545 table_widget_->setItem(
546 i, 2,
new QTableWidgetItem(QString::number(
image.CameraId())));
547 table_widget_->setItem(
549 new QTableWidgetItem(QString::number(
image.QvecPrior(0))));
550 table_widget_->setItem(
552 new QTableWidgetItem(QString::number(
image.QvecPrior(1))));
553 table_widget_->setItem(
555 new QTableWidgetItem(QString::number(
image.QvecPrior(2))));
556 table_widget_->setItem(
558 new QTableWidgetItem(QString::number(
image.QvecPrior(3))));
559 table_widget_->setItem(
561 new QTableWidgetItem(QString::number(
image.TvecPrior(0))));
562 table_widget_->setItem(
564 new QTableWidgetItem(QString::number(
image.TvecPrior(1))));
565 table_widget_->setItem(
567 new QTableWidgetItem(QString::number(
image.TvecPrior(2))));
569 table_widget_->resizeColumnsToContents();
571 table_widget_->blockSignals(
false);
576 table_widget_->clearContents();
579 void ImageTab::itemChanged(QTableWidgetItem* item) {
580 Image&
image = images_.at(item->row());
581 camera_t camera_id = kInvalidCameraId;
583 switch (item->column()) {
586 image.SetName(item->text().toUtf8().constData());
590 static_cast<camera_t
>(item->data(Qt::DisplayRole).toInt());
591 if (!database_->ExistsCamera(camera_id)) {
592 QMessageBox::critical(
this,
"",
593 tr(
"camera_id does not exist."));
594 table_widget_->blockSignals(
true);
595 item->setText(QString::number(
image.CameraId()));
596 table_widget_->blockSignals(
false);
598 image.SetCameraId(camera_id);
602 image.QvecPrior(0) = item->data(Qt::DisplayRole).toReal();
605 image.QvecPrior(1) = item->data(Qt::DisplayRole).toReal();
608 image.QvecPrior(2) = item->data(Qt::DisplayRole).toReal();
611 image.QvecPrior(3) = item->data(Qt::DisplayRole).toReal();
614 image.TvecPrior(0) = item->data(Qt::DisplayRole).toReal();
617 image.TvecPrior(1) = item->data(Qt::DisplayRole).toReal();
620 image.TvecPrior(2) = item->data(Qt::DisplayRole).toReal();
626 database_->UpdateImage(
image);
629 void ImageTab::ShowImage() {
630 QItemSelectionModel* select = table_widget_->selectionModel();
632 if (!select->hasSelection()) {
633 QMessageBox::critical(
this,
"", tr(
"No image selected."));
637 if (select->selectedRows().size() > 1) {
638 QMessageBox::critical(
this,
"", tr(
"Only one image may be selected."));
642 const auto&
image = images_[select->selectedRows().begin()->row()];
644 const auto keypoints = database_->ReadKeypoints(
image.ImageId());
645 const std::vector<char> tri_mask(keypoints.size(),
false);
650 image_viewer_widget_->setWindowTitle(
654 void ImageTab::ShowMatches() {
655 QItemSelectionModel* select = table_widget_->selectionModel();
657 if (!select->hasSelection()) {
658 QMessageBox::critical(
this,
"", tr(
"No image selected."));
662 if (select->selectedRows().size() > 1) {
663 QMessageBox::critical(
this,
"", tr(
"Only one image may be selected."));
667 const auto&
image = images_[select->selectedRows().begin()->row()];
670 overlapping_images_widget_->show();
671 overlapping_images_widget_->raise();
674 void ImageTab::SetCamera() {
675 QItemSelectionModel* select = table_widget_->selectionModel();
677 if (!select->hasSelection()) {
678 QMessageBox::critical(
this,
"", tr(
"No image selected."));
683 const camera_t camera_id =
static_cast<camera_t
>(QInputDialog::getInt(
684 this,
"", tr(
"camera_id"), 0, 0, INT_MAX, 1, &ok));
689 if (!database_->ExistsCamera(camera_id)) {
690 QMessageBox::critical(
this,
"", tr(
"camera_id does not exist."));
695 table_widget_->blockSignals(
true);
697 for (QModelIndex& index : select->selectedRows()) {
698 table_widget_->setItem(
700 new QTableWidgetItem(QString::number(camera_id)));
701 auto&
image = images_[index.row()];
702 image.SetCameraId(camera_id);
703 database_->UpdateImage(
image);
706 table_widget_->blockSignals(
false);
709 void ImageTab::SplitCamera() {
710 QItemSelectionModel* select = table_widget_->selectionModel();
712 if (!select->hasSelection()) {
713 QMessageBox::critical(
this,
"", tr(
"No image selected."));
718 const camera_t camera_id =
static_cast<camera_t
>(QInputDialog::getInt(
719 this,
"", tr(
"camera_id"), 0, 0, INT_MAX, 1, &ok));
724 if (!database_->ExistsCamera(camera_id)) {
725 QMessageBox::critical(
this,
"", tr(
"camera_id does not exist."));
729 const auto camera = database_->ReadCamera(camera_id);
732 table_widget_->blockSignals(
true);
734 for (QModelIndex& index : select->selectedRows()) {
735 auto&
image = images_[index.row()];
736 image.SetCameraId(database_->WriteCamera(camera));
737 database_->UpdateImage(
image);
738 table_widget_->setItem(
740 new QTableWidgetItem(QString::number(
image.CameraId())));
743 table_widget_->blockSignals(
false);
750 : parent_(parent), options_(options) {
752 setWindowTitle(
"Database management");
753 resize(parent->size().width() - 20, parent->size().height() - 20);
755 QGridLayout* grid =
new QGridLayout(
this);
757 tab_widget_ =
new QTabWidget(
this);
759 camera_tab_ =
new CameraTab(
this, &database_);
760 image_tab_ =
new ImageTab(
this, camera_tab_, options_, &database_);
762 tab_widget_->addTab(image_tab_, tr(
"Images"));
763 tab_widget_->addTab(camera_tab_, tr(
"Cameras"));
765 grid->addWidget(tab_widget_, 0, 0, 1, 4);
767 QPushButton* clear_matches_button =
768 new QPushButton(tr(
"Clear Matches"),
this);
769 connect(clear_matches_button, &QPushButton::released,
this,
770 &DatabaseManagementWidget::ClearMatches);
771 grid->addWidget(clear_matches_button, 1, 0, Qt::AlignLeft);
773 QPushButton* clear_two_view_geometries_button =
774 new QPushButton(tr(
"Clear two-view geometries"),
this);
775 connect(clear_two_view_geometries_button, &QPushButton::released,
this,
776 &DatabaseManagementWidget::ClearTwoViewGeometries);
777 grid->addWidget(clear_two_view_geometries_button, 1, 1, Qt::AlignLeft);
779 grid->setColumnStretch(1, 1);
782 void DatabaseManagementWidget::showEvent(QShowEvent*) {
783 parent_->setDisabled(
true);
785 database_.Open(*options_->database_path);
791 void DatabaseManagementWidget::hideEvent(QHideEvent*) {
792 parent_->setEnabled(
true);
795 camera_tab_->
Clear();
800 void DatabaseManagementWidget::ClearMatches() {
801 QMessageBox::StandardButton reply = QMessageBox::question(
802 this,
"", tr(
"Do you really want to clear all matches?"),
803 QMessageBox::Yes | QMessageBox::No);
804 if (reply == QMessageBox::No) {
807 database_.ClearMatches();
810 void DatabaseManagementWidget::ClearTwoViewGeometries() {
811 QMessageBox::StandardButton reply = QMessageBox::question(
813 tr(
"Do you really want to clear all two-view geometries?"),
814 QMessageBox::Yes | QMessageBox::No);
815 if (reply == QMessageBox::No) {
818 database_.ClearTwoViewGeometries();
std::shared_ptr< core::Tensor > image
CameraTab(QWidget *parent, colmap::Database *database)
ImageTab(QWidget *parent, CameraTab *camera_tab, OptionManager *options, colmap::Database *database)
void Reload(const std::vector< colmap::Image > &images, const colmap::image_t image_id)
TwoViewGeometriesTab(QWidget *parent, OptionManager *options, colmap::Database *database)
void Reload(const std::vector< colmap::Image > &images, const colmap::image_t image_id)
std::vector< std::pair< const colmap::Image *, colmap::FeatureMatches > > matches_
colmap::Database * database_
void InitializeTable(const QStringList &table_header)
const colmap::Image * image_
std::vector< int > configs_
QTextStream & endl(QTextStream &stream)
std::string JoinPaths(T const &...paths)
Generic file read and write utility for python interface.
colmap::OptionManager OptionManager
std::string to_string(const T &n)