34 QGridLayout* grid =
new QGridLayout(
this);
39 QPushButton* show_button =
new QPushButton(tr(
"Show matches"),
this);
40 connect(show_button, &QPushButton::released,
this,
42 grid->addWidget(show_button, 0, 1, Qt::AlignRight);
49 table_widget_->setSelectionBehavior(QAbstractItemView::SelectRows);
50 table_widget_->setSelectionMode(QAbstractItemView::SingleSelection);
51 table_widget_->setEditTriggers(QAbstractItemView::NoEditTriggers);
52 table_widget_->horizontalHeader()->setStretchLastSection(
true);
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."));
75 const auto& selection =
matches_[idx];
77 const std::string path2 =
80 const auto keypoints2 =
87 path1, path2, keypoints1, keypoints2, selection.second);
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());
112 QTableWidgetItem* image_id_item =
new QTableWidgetItem(
113 QString::number(
matches_[idx].first->ImageId()));
116 QTableWidgetItem* num_matches_item =
new QTableWidgetItem(
117 QString::number(
matches_[idx].second.size()));
122 QTableWidgetItem* config_item =
123 new QTableWidgetItem(QString::number(
configs_[idx]));
135 QStringList table_header;
136 table_header <<
"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"
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) {
210 setWindowFlags(Qt::Window);
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);
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);
299 table_widget_->blockSignals(
true);
301 table_widget_->clearContents();
302 table_widget_->setRowCount(cameras_.size());
304 std::sort(cameras_.begin(), cameras_.end(),
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(
329 table_widget_->setItem(i, 5,
330 new QTableWidgetItem(QString::number(
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()));
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);
370 static_cast<bool>(item->data(Qt::DisplayRole).toInt()));
379 void CameraTab::Add() {
380 QStringList camera_models;
381 #define CAMERA_MODEL_CASE(CameraModel) \
382 << QString::fromStdString(CameraModelIdToName(CameraModel::model_id))
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;
399 kDefaultFocalLength, kDefaultWidth,
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))
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());
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);
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) {
583 switch (item->column()) {
586 image.SetName(item->text().toUtf8().constData());
590 static_cast<camera_t>(item->data(Qt::DisplayRole).toInt());
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();
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()];
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."));
684 this,
"", tr(
"camera_id"), 0, 0, INT_MAX, 1, &ok));
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);
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."));
719 this,
"", tr(
"camera_id"), 0, 0, INT_MAX, 1, &ok));
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()];
738 table_widget_->setItem(
740 new QTableWidgetItem(QString::number(
image.CameraId())));
743 table_widget_->blockSignals(
false);
750 : parent_(parent), options_(options) {
751 setWindowFlags(Qt::Window);
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);
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) {
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) {
std::shared_ptr< core::Tensor > image
#define CAMERA_MODEL_CASES
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)
MatchesTab(QWidget *parent, OptionManager *options, colmap::Database *database)
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)
FeatureImageViewerWidget * matches_viewer_widget_
std::vector< size_t > sorted_matches_idxs_
const colmap::Image * image_
QTableWidget * table_widget_
std::vector< int > configs_
The Image class stores image with customizable width, height, num of channels and bytes per channel.
void SetWidth(const size_t width)
void InitializeWithName(const std::string &model_name, const double focal_length, const size_t width, const size_t height)
void SetPriorFocalLength(const bool prior)
double MeanFocalLength() const
const std::vector< double > & Params() const
bool SetParamsFromString(const std::string &string)
camera_t CameraId() const
std::string ModelName() const
bool HasPriorFocalLength() const
void SetHeight(const size_t height)
FeatureMatches ReadMatches(const image_t image_id1, const image_t image_id2) const
bool ExistsMatches(const image_t image_id1, const image_t image_id2) const
FeatureKeypoints ReadKeypoints(const image_t image_id) const
size_t NumKeypoints() const
bool ExistsInlierMatches(const image_t image_id1, const image_t image_id2) const
std::vector< Camera > ReadAllCameras() const
bool ExistsCamera(const camera_t camera_id) const
void Open(const std::string &path)
void ClearTwoViewGeometries() const
void ClearMatches() const
void UpdateCamera(const Camera &camera) const
TwoViewGeometry ReadTwoViewGeometry(const image_t image_id1, const image_t image_id2) const
Camera ReadCamera(const camera_t camera_id) const
void UpdateImage(const Image &image) const
size_t NumCameras() const
std::vector< Image > ReadAllImages() const
camera_t WriteCamera(const Camera &camera, const bool use_camera_id=false) const
const std::string & Name() const
std::shared_ptr< std::string > database_path
std::shared_ptr< std::string > image_path
QTextStream & endl(QTextStream &stream)
std::string JoinPaths(T const &...paths)
Generic file read and write utility for python interface.
const camera_t kInvalidCameraId
std::string VectorToCSV(const std::vector< T > &values)
std::string to_string(const T &n)