ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
database_management_widget.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
33 
34 #include "base/camera_models.h"
35 
36 namespace colmap {
37 
39  Database* database)
40  : QWidget(parent),
41  options_(options),
42  database_(database),
43  matches_viewer_widget_(new FeatureImageViewerWidget(parent, "matches")) {}
44 
46  table_widget_->clearContents();
47  matches_.clear();
48  configs_.clear();
49  sorted_matches_idxs_.clear();
50 }
51 
52 void TwoViewInfoTab::InitializeTable(const QStringList& table_header) {
53  QGridLayout* grid = new QGridLayout(this);
54 
55  info_label_ = new QLabel(this);
56  grid->addWidget(info_label_, 0, 0);
57 
58  QPushButton* show_button = new QPushButton(tr("Show matches"), this);
59  connect(show_button, &QPushButton::released, this,
61  grid->addWidget(show_button, 0, 1, Qt::AlignRight);
62 
63  table_widget_ = new QTableWidget(this);
64  table_widget_->setColumnCount(table_header.size());
65  table_widget_->setHorizontalHeaderLabels(table_header);
66 
67  table_widget_->setShowGrid(true);
68  table_widget_->setSelectionBehavior(QAbstractItemView::SelectRows);
69  table_widget_->setSelectionMode(QAbstractItemView::SingleSelection);
70  table_widget_->setEditTriggers(QAbstractItemView::NoEditTriggers);
71  table_widget_->horizontalHeader()->setStretchLastSection(true);
72  table_widget_->verticalHeader()->setVisible(false);
73  table_widget_->verticalHeader()->setDefaultSectionSize(20);
74 
75  grid->addWidget(table_widget_, 1, 0, 1, 2);
76 }
77 
79  QItemSelectionModel* select = table_widget_->selectionModel();
80 
81  if (!select->hasSelection()) {
82  QMessageBox::critical(this, "", tr("No image pair selected."));
83  return;
84  }
85 
86  if (select->selectedRows().size() > 1) {
87  QMessageBox::critical(this, "", tr("Only one image pair may be selected."));
88  return;
89  }
90 
91  const size_t idx =
92  sorted_matches_idxs_[select->selectedRows().begin()->row()];
93  const auto& selection = matches_[idx];
94  const std::string path1 = JoinPaths(*options_->image_path, image_->Name());
95  const std::string path2 =
96  JoinPaths(*options_->image_path, selection.first->Name());
97  const auto keypoints1 = database_->ReadKeypoints(image_->ImageId());
98  const auto keypoints2 = database_->ReadKeypoints(selection.first->ImageId());
99 
100  matches_viewer_widget_->setWindowTitle(QString::fromStdString(
101  "Matches for image pair " + std::to_string(image_->ImageId()) + " - " +
102  std::to_string(selection.first->ImageId())));
103  matches_viewer_widget_->ReadAndShowWithMatches(path1, path2, keypoints1,
104  keypoints2, selection.second);
105 }
106 
108  // Sort the matched pairs according to number of matches in descending order.
109  sorted_matches_idxs_.resize(matches_.size());
110  std::iota(sorted_matches_idxs_.begin(), sorted_matches_idxs_.end(), 0);
111 
112  std::sort(sorted_matches_idxs_.begin(), sorted_matches_idxs_.end(),
113  [&](const size_t idx1, const size_t idx2) {
114  return matches_[idx1].second.size() >
115  matches_[idx2].second.size();
116  });
117 
118  QString info;
119  info += QString("Matched images: ") + QString::number(matches_.size());
120  info_label_->setText(info);
121 
122  table_widget_->clearContents();
123  table_widget_->setRowCount(matches_.size());
124 
125  for (size_t i = 0; i < sorted_matches_idxs_.size(); ++i) {
126  const size_t idx = sorted_matches_idxs_[i];
127 
128  QTableWidgetItem* image_id_item =
129  new QTableWidgetItem(QString::number(matches_[idx].first->ImageId()));
130  table_widget_->setItem(i, 0, image_id_item);
131 
132  QTableWidgetItem* num_matches_item =
133  new QTableWidgetItem(QString::number(matches_[idx].second.size()));
134  table_widget_->setItem(i, 1, num_matches_item);
135 
136  // config for inlier matches tab
137  if (table_widget_->columnCount() == 3) {
138  QTableWidgetItem* config_item =
139  new QTableWidgetItem(QString::number(configs_[idx]));
140  table_widget_->setItem(i, 2, config_item);
141  }
142  }
143 
144  table_widget_->resizeColumnsToContents();
145 }
146 
147 MatchesTab::MatchesTab(QWidget* parent, OptionManager* options,
148  Database* database)
149  : TwoViewInfoTab(parent, options, database) {
150  QStringList table_header;
151  table_header << "image_id"
152  << "num_matches";
153  InitializeTable(table_header);
154 }
155 
156 void MatchesTab::Reload(const std::vector<Image>& images,
157  const image_t image_id) {
158  matches_.clear();
159 
160  // Find all matched images
161 
162  for (const auto& image : images) {
163  if (image.ImageId() == image_id) {
164  image_ = &image;
165  continue;
166  }
167 
168  if (database_->ExistsMatches(image_id, image.ImageId())) {
169  const auto matches = database_->ReadMatches(image_id, image.ImageId());
170 
171  if (matches.size() > 0) {
172  matches_.emplace_back(&image, matches);
173  }
174  }
175  }
176 
177  FillTable();
178 }
179 
181  OptionManager* options,
182  Database* database)
183  : TwoViewInfoTab(parent, options, database) {
184  QStringList table_header;
185  table_header << "image_id"
186  << "num_matches"
187  << "config";
188  InitializeTable(table_header);
189 }
190 
191 void TwoViewGeometriesTab::Reload(const std::vector<Image>& images,
192  const image_t image_id) {
193  matches_.clear();
194  configs_.clear();
195 
196  // Find all matched images.
197 
198  for (const auto& image : images) {
199  if (image.ImageId() == image_id) {
200  image_ = &image;
201  continue;
202  }
203 
204  if (database_->ExistsInlierMatches(image_id, image.ImageId())) {
205  const auto two_view_geometry =
206  database_->ReadTwoViewGeometry(image_id, image.ImageId());
207 
208  if (two_view_geometry.inlier_matches.size() > 0) {
209  matches_.emplace_back(&image, two_view_geometry.inlier_matches);
210  configs_.push_back(two_view_geometry.config);
211  }
212  }
213  }
214 
215  FillTable();
216 }
217 
219  OptionManager* options,
220  Database* database)
221  : parent_(parent), options_(options) {
222  // Do not change flag, to make sure feature database is not accessed from
223  // multiple threads.
224  setWindowFlags(Qt::Window);
225  resize(parent->size().width() - 20, parent->size().height() - 20);
226 
227  QGridLayout* grid = new QGridLayout(this);
228 
229  tab_widget_ = new QTabWidget(this);
230 
231  matches_tab_ = new MatchesTab(this, options_, database);
232  tab_widget_->addTab(matches_tab_, tr("Matches"));
233 
234  two_view_geometries_tab_ = new TwoViewGeometriesTab(this, options_, database);
235  tab_widget_->addTab(two_view_geometries_tab_, tr("Two-view geometries"));
236 
237  grid->addWidget(tab_widget_, 0, 0);
238 
239  QPushButton* close_button = new QPushButton(tr("Close"), this);
240  connect(close_button, &QPushButton::released, this,
241  &OverlappingImagesWidget::close);
242  grid->addWidget(close_button, 1, 0, Qt::AlignRight);
243 }
244 
245 void OverlappingImagesWidget::ShowMatches(const std::vector<Image>& images,
246  const image_t image_id) {
247  parent_->setDisabled(true);
248 
249  setWindowTitle(
250  QString::fromStdString("Matches for image " + std::to_string(image_id)));
251 
252  matches_tab_->Reload(images, image_id);
253  two_view_geometries_tab_->Reload(images, image_id);
254 }
255 
256 void OverlappingImagesWidget::closeEvent(QCloseEvent*) {
257  matches_tab_->Clear();
258  two_view_geometries_tab_->Clear();
259  parent_->setEnabled(true);
260 }
261 
262 CameraTab::CameraTab(QWidget* parent, Database* database)
263  : QWidget(parent), database_(database) {
264  QGridLayout* grid = new QGridLayout(this);
265 
266  info_label_ = new QLabel(this);
267  grid->addWidget(info_label_, 0, 0);
268 
269  QPushButton* add_camera_button = new QPushButton(tr("Add camera"), this);
270  connect(add_camera_button, &QPushButton::released, this, &CameraTab::Add);
271  grid->addWidget(add_camera_button, 0, 1, Qt::AlignRight);
272 
273  QPushButton* set_model_button = new QPushButton(tr("Set model"), this);
274  connect(set_model_button, &QPushButton::released, this, &CameraTab::SetModel);
275  grid->addWidget(set_model_button, 0, 2, Qt::AlignRight);
276 
277  table_widget_ = new QTableWidget(this);
278  table_widget_->setColumnCount(6);
279 
280  QStringList table_header;
281  table_header << "camera_id"
282  << "model"
283  << "width"
284  << "height"
285  << "params"
286  << "prior_focal_length";
287  table_widget_->setHorizontalHeaderLabels(table_header);
288 
289  table_widget_->setShowGrid(true);
290  table_widget_->setSelectionBehavior(QAbstractItemView::SelectRows);
291  table_widget_->horizontalHeader()->setStretchLastSection(true);
292  table_widget_->verticalHeader()->setVisible(false);
293  table_widget_->verticalHeader()->setDefaultSectionSize(20);
294 
295  connect(table_widget_, &QTableWidget::itemChanged, this,
296  &CameraTab::itemChanged);
297 
298  grid->addWidget(table_widget_, 1, 0, 1, 3);
299 
300  grid->setColumnStretch(0, 1);
301 }
302 
304  QString info;
305  info += QString("Cameras: ") + QString::number(database_->NumCameras());
306  info_label_->setText(info);
307 
308  cameras_ = database_->ReadAllCameras();
309 
310  // Make sure, itemChanged is not invoked, while setting up the table.
311  table_widget_->blockSignals(true);
312 
313  table_widget_->clearContents();
314  table_widget_->setRowCount(cameras_.size());
315 
316  std::sort(cameras_.begin(), cameras_.end(),
317  [](const Camera& camera1, const Camera& camera2) {
318  return camera1.CameraId() < camera2.CameraId();
319  });
320 
321  for (size_t i = 0; i < cameras_.size(); ++i) {
322  const Camera& camera = cameras_[i];
323  QTableWidgetItem* id_item =
324  new QTableWidgetItem(QString::number(camera.CameraId()));
325  id_item->setFlags(Qt::ItemIsSelectable);
326  table_widget_->setItem(i, 0, id_item);
327 
328  QTableWidgetItem* model_item =
329  new QTableWidgetItem(QString::fromStdString(camera.ModelName()));
330  model_item->setFlags(Qt::ItemIsSelectable);
331  table_widget_->setItem(i, 1, model_item);
332 
333  table_widget_->setItem(
334  i, 2, new QTableWidgetItem(QString::number(camera.Width())));
335  table_widget_->setItem(
336  i, 3, new QTableWidgetItem(QString::number(camera.Height())));
337 
338  table_widget_->setItem(i, 4,
339  new QTableWidgetItem(QString::fromStdString(
340  VectorToCSV(camera.Params()))));
341  table_widget_->setItem(
342  i, 5,
343  new QTableWidgetItem(QString::number(camera.HasPriorFocalLength())));
344  }
345  table_widget_->resizeColumnsToContents();
346 
347  table_widget_->blockSignals(false);
348 }
349 
351  cameras_.clear();
352  table_widget_->clearContents();
353 }
354 
355 void CameraTab::itemChanged(QTableWidgetItem* item) {
356  Camera& camera = cameras_.at(item->row());
357  const std::vector<double> prev_params = camera.Params();
358 
359  switch (item->column()) {
360  // case 0: never change the camera ID
361  // case 1: never change the camera model
362  case 2:
363  camera.SetWidth(static_cast<size_t>(item->data(Qt::DisplayRole).toInt()));
364  break;
365  case 3:
366  camera.SetHeight(
367  static_cast<size_t>(item->data(Qt::DisplayRole).toInt()));
368  break;
369  case 4:
370  if (!camera.SetParamsFromString(item->text().toUtf8().constData())) {
371  QMessageBox::critical(this, "", tr("Invalid camera parameters."));
372  table_widget_->blockSignals(true);
373  item->setText(QString::fromStdString(VectorToCSV(prev_params)));
374  table_widget_->blockSignals(false);
375  }
376  break;
377  case 5:
378  camera.SetPriorFocalLength(
379  static_cast<bool>(item->data(Qt::DisplayRole).toInt()));
380  break;
381  default:
382  break;
383  }
384 
385  database_->UpdateCamera(camera);
386 }
387 
388 void CameraTab::Add() {
389  QStringList camera_models;
390 #define CAMERA_MODEL_CASE(CameraModel) \
391  << QString::fromStdString(CameraModelIdToName(CameraModel::model_id))
392  camera_models CAMERA_MODEL_CASES;
393 #undef CAMERA_MODEL_CASE
394 
395  bool ok;
396  const QString camera_model = QInputDialog::getItem(
397  this, "", tr("Model:"), camera_models, 0, false, &ok);
398  if (!ok) {
399  return;
400  }
401 
402  // Add new camera to feature database
403  Camera camera;
404  const double kDefaultFocalLength = 1.0;
405  const size_t kDefaultWidth = 1;
406  const size_t kDefaultHeight = 1;
407  camera.InitializeWithName(camera_model.toUtf8().constData(),
408  kDefaultFocalLength, kDefaultWidth, kDefaultHeight);
409  database_->WriteCamera(camera);
410 
411  // Reload all cameras
412  Reload();
413 
414  // Highlight new camera
415  table_widget_->selectRow(cameras_.size() - 1);
416 }
417 
418 void CameraTab::SetModel() {
419  QItemSelectionModel* select = table_widget_->selectionModel();
420 
421  if (!select->hasSelection()) {
422  QMessageBox::critical(this, "", tr("No camera selected."));
423  return;
424  }
425 
426  QStringList camera_models;
427 #define CAMERA_MODEL_CASE(CameraModel) \
428  << QString::fromStdString(CameraModelIdToName(CameraModel::model_id))
429  camera_models CAMERA_MODEL_CASES;
430 #undef CAMERA_MODEL_CASE
431 
432  bool ok;
433  const QString camera_model = QInputDialog::getItem(
434  this, "", tr("Model:"), camera_models, 0, false, &ok);
435  if (!ok) {
436  return;
437  }
438 
439  // Make sure, itemChanged is not invoked, while updating up the table
440  table_widget_->blockSignals(true);
441 
442  for (QModelIndex& index : select->selectedRows()) {
443  std::cout << index.row() << std::endl;
444  auto& camera = cameras_.at(index.row());
445  camera.InitializeWithName(camera_model.toUtf8().constData(),
446  camera.MeanFocalLength(), camera.Width(),
447  camera.Height());
448  database_->UpdateCamera(camera);
449  }
450 
451  table_widget_->blockSignals(false);
452 
453  Reload();
454 }
455 
456 ImageTab::ImageTab(QWidget* parent, CameraTab* camera_tab,
457  OptionManager* options, Database* database)
458  : QWidget(parent),
459  camera_tab_(camera_tab),
460  options_(options),
461  database_(database) {
462  QGridLayout* grid = new QGridLayout(this);
463 
464  info_label_ = new QLabel(this);
465  grid->addWidget(info_label_, 0, 0);
466 
467  QPushButton* set_camera_button = new QPushButton(tr("Set camera"), this);
468  connect(set_camera_button, &QPushButton::released, this,
469  &ImageTab::SetCamera);
470  grid->addWidget(set_camera_button, 0, 1, Qt::AlignRight);
471 
472  QPushButton* split_camera_button = new QPushButton(tr("Split camera"), this);
473  connect(split_camera_button, &QPushButton::released, this,
474  &ImageTab::SplitCamera);
475  grid->addWidget(split_camera_button, 0, 2, Qt::AlignRight);
476 
477  QPushButton* show_image_button = new QPushButton(tr("Show image"), this);
478  connect(show_image_button, &QPushButton::released, this,
479  &ImageTab::ShowImage);
480  grid->addWidget(show_image_button, 0, 3, Qt::AlignRight);
481 
482  QPushButton* overlapping_images_button =
483  new QPushButton(tr("Overlapping images"), this);
484  connect(overlapping_images_button, &QPushButton::released, this,
485  &ImageTab::ShowMatches);
486  grid->addWidget(overlapping_images_button, 0, 4, Qt::AlignRight);
487 
488  table_widget_ = new QTableWidget(this);
489  table_widget_->setColumnCount(10);
490 
491  QStringList table_header;
492  table_header << "image_id"
493  << "name"
494  << "camera_id"
495  << "qw"
496  << "qx"
497  << "qy"
498  << "qz"
499  << "tx"
500  << "ty"
501  << "tz";
502  table_widget_->setHorizontalHeaderLabels(table_header);
503 
504  table_widget_->setShowGrid(true);
505  table_widget_->setSelectionBehavior(QAbstractItemView::SelectRows);
506  table_widget_->horizontalHeader()->setStretchLastSection(true);
507  table_widget_->verticalHeader()->setVisible(false);
508  table_widget_->verticalHeader()->setDefaultSectionSize(20);
509 
510  connect(table_widget_, &QTableWidget::itemChanged, this,
511  &ImageTab::itemChanged);
512 
513  grid->addWidget(table_widget_, 1, 0, 1, 5);
514 
515  grid->setColumnStretch(0, 3);
516 
517  image_viewer_widget_ = new FeatureImageViewerWidget(parent, "keypoints");
518  overlapping_images_widget_ =
519  new OverlappingImagesWidget(parent, options, database_);
520 }
521 
523  QString info;
524  info += QString("Images: ") + QString::number(database_->NumImages());
525  info += QString("\n");
526  info += QString("Features: ") + QString::number(database_->NumKeypoints());
527  info_label_->setText(info);
528 
529  images_ = database_->ReadAllImages();
530 
531  // Make sure, itemChanged is not invoked, while setting up the table
532  table_widget_->blockSignals(true);
533 
534  table_widget_->clearContents();
535  table_widget_->setRowCount(images_.size());
536 
537  for (size_t i = 0; i < images_.size(); ++i) {
538  const auto& image = images_[i];
539  QTableWidgetItem* id_item =
540  new QTableWidgetItem(QString::number(image.ImageId()));
541  id_item->setFlags(Qt::ItemIsSelectable);
542  table_widget_->setItem(i, 0, id_item);
543  table_widget_->setItem(
544  i, 1, new QTableWidgetItem(QString::fromStdString(image.Name())));
545  table_widget_->setItem(
546  i, 2, new QTableWidgetItem(QString::number(image.CameraId())));
547  table_widget_->setItem(
548  i, 3, new QTableWidgetItem(QString::number(image.QvecPrior(0))));
549  table_widget_->setItem(
550  i, 4, new QTableWidgetItem(QString::number(image.QvecPrior(1))));
551  table_widget_->setItem(
552  i, 5, new QTableWidgetItem(QString::number(image.QvecPrior(2))));
553  table_widget_->setItem(
554  i, 6, new QTableWidgetItem(QString::number(image.QvecPrior(3))));
555  table_widget_->setItem(
556  i, 7, new QTableWidgetItem(QString::number(image.TvecPrior(0))));
557  table_widget_->setItem(
558  i, 8, new QTableWidgetItem(QString::number(image.TvecPrior(1))));
559  table_widget_->setItem(
560  i, 9, new QTableWidgetItem(QString::number(image.TvecPrior(2))));
561  }
562  table_widget_->resizeColumnsToContents();
563 
564  table_widget_->blockSignals(false);
565 }
566 
568  images_.clear();
569  table_widget_->clearContents();
570 }
571 
572 void ImageTab::itemChanged(QTableWidgetItem* item) {
573  Image& image = images_.at(item->row());
574  camera_t camera_id = kInvalidCameraId;
575 
576  switch (item->column()) {
577  // case 0: never change the image ID
578  case 1:
579  image.SetName(item->text().toUtf8().constData());
580  break;
581  case 2:
582  camera_id = static_cast<camera_t>(item->data(Qt::DisplayRole).toInt());
583  if (!database_->ExistsCamera(camera_id)) {
584  QMessageBox::critical(this, "", tr("camera_id does not exist."));
585  table_widget_->blockSignals(true);
586  item->setText(QString::number(image.CameraId()));
587  table_widget_->blockSignals(false);
588  } else {
589  image.SetCameraId(camera_id);
590  }
591  break;
592  case 3:
593  image.QvecPrior(0) = item->data(Qt::DisplayRole).toReal();
594  break;
595  case 4:
596  image.QvecPrior(1) = item->data(Qt::DisplayRole).toReal();
597  break;
598  case 5:
599  image.QvecPrior(2) = item->data(Qt::DisplayRole).toReal();
600  break;
601  case 6:
602  image.QvecPrior(3) = item->data(Qt::DisplayRole).toReal();
603  break;
604  case 7:
605  image.TvecPrior(0) = item->data(Qt::DisplayRole).toReal();
606  break;
607  case 8:
608  image.TvecPrior(1) = item->data(Qt::DisplayRole).toReal();
609  break;
610  case 9:
611  image.TvecPrior(2) = item->data(Qt::DisplayRole).toReal();
612  break;
613  default:
614  break;
615  }
616 
617  database_->UpdateImage(image);
618 }
619 
620 void ImageTab::ShowImage() {
621  QItemSelectionModel* select = table_widget_->selectionModel();
622 
623  if (!select->hasSelection()) {
624  QMessageBox::critical(this, "", tr("No image selected."));
625  return;
626  }
627 
628  if (select->selectedRows().size() > 1) {
629  QMessageBox::critical(this, "", tr("Only one image may be selected."));
630  return;
631  }
632 
633  const auto& image = images_[select->selectedRows().begin()->row()];
634 
635  const auto keypoints = database_->ReadKeypoints(image.ImageId());
636  const std::vector<char> tri_mask(keypoints.size(), false);
637 
638  image_viewer_widget_->ReadAndShowWithKeypoints(
639  JoinPaths(*options_->image_path, image.Name()), keypoints, tri_mask);
640  image_viewer_widget_->setWindowTitle(
641  QString::fromStdString("Image " + std::to_string(image.ImageId())));
642 }
643 
644 void ImageTab::ShowMatches() {
645  QItemSelectionModel* select = table_widget_->selectionModel();
646 
647  if (!select->hasSelection()) {
648  QMessageBox::critical(this, "", tr("No image selected."));
649  return;
650  }
651 
652  if (select->selectedRows().size() > 1) {
653  QMessageBox::critical(this, "", tr("Only one image may be selected."));
654  return;
655  }
656 
657  const auto& image = images_[select->selectedRows().begin()->row()];
658 
659  overlapping_images_widget_->ShowMatches(images_, image.ImageId());
660  overlapping_images_widget_->show();
661  overlapping_images_widget_->raise();
662 }
663 
664 void ImageTab::SetCamera() {
665  QItemSelectionModel* select = table_widget_->selectionModel();
666 
667  if (!select->hasSelection()) {
668  QMessageBox::critical(this, "", tr("No image selected."));
669  return;
670  }
671 
672  bool ok;
673  const camera_t camera_id = static_cast<camera_t>(
674  QInputDialog::getInt(this, "", tr("camera_id"), 0, 0, INT_MAX, 1, &ok));
675  if (!ok) {
676  return;
677  }
678 
679  if (!database_->ExistsCamera(camera_id)) {
680  QMessageBox::critical(this, "", tr("camera_id does not exist."));
681  return;
682  }
683 
684  // Make sure, itemChanged is not invoked, while updating up the table
685  table_widget_->blockSignals(true);
686 
687  for (QModelIndex& index : select->selectedRows()) {
688  table_widget_->setItem(index.row(), 2,
689  new QTableWidgetItem(QString::number(camera_id)));
690  auto& image = images_[index.row()];
691  image.SetCameraId(camera_id);
692  database_->UpdateImage(image);
693  }
694 
695  table_widget_->blockSignals(false);
696 }
697 
698 void ImageTab::SplitCamera() {
699  QItemSelectionModel* select = table_widget_->selectionModel();
700 
701  if (!select->hasSelection()) {
702  QMessageBox::critical(this, "", tr("No image selected."));
703  return;
704  }
705 
706  bool ok;
707  const camera_t camera_id = static_cast<camera_t>(
708  QInputDialog::getInt(this, "", tr("camera_id"), 0, 0, INT_MAX, 1, &ok));
709  if (!ok) {
710  return;
711  }
712 
713  if (!database_->ExistsCamera(camera_id)) {
714  QMessageBox::critical(this, "", tr("camera_id does not exist."));
715  return;
716  }
717 
718  const auto camera = database_->ReadCamera(camera_id);
719 
720  // Make sure, itemChanged is not invoked, while updating up the table
721  table_widget_->blockSignals(true);
722 
723  for (QModelIndex& index : select->selectedRows()) {
724  auto& image = images_[index.row()];
725  image.SetCameraId(database_->WriteCamera(camera));
726  database_->UpdateImage(image);
727  table_widget_->setItem(
728  index.row(), 2,
729  new QTableWidgetItem(QString::number(image.CameraId())));
730  }
731 
732  table_widget_->blockSignals(false);
733 
734  camera_tab_->Reload();
735 }
736 
738  OptionManager* options)
739  : parent_(parent), options_(options) {
740  setWindowFlags(Qt::Window);
741  setWindowTitle("Database management");
742  resize(parent->size().width() - 20, parent->size().height() - 20);
743 
744  QGridLayout* grid = new QGridLayout(this);
745 
746  tab_widget_ = new QTabWidget(this);
747 
748  camera_tab_ = new CameraTab(this, &database_);
749  image_tab_ = new ImageTab(this, camera_tab_, options_, &database_);
750 
751  tab_widget_->addTab(image_tab_, tr("Images"));
752  tab_widget_->addTab(camera_tab_, tr("Cameras"));
753 
754  grid->addWidget(tab_widget_, 0, 0, 1, 4);
755 
756  QPushButton* clear_matches_button =
757  new QPushButton(tr("Clear Matches"), this);
758  connect(clear_matches_button, &QPushButton::released, this,
759  &DatabaseManagementWidget::ClearMatches);
760  grid->addWidget(clear_matches_button, 1, 0, Qt::AlignLeft);
761 
762  QPushButton* clear_two_view_geometries_button =
763  new QPushButton(tr("Clear two-view geometries"), this);
764  connect(clear_two_view_geometries_button, &QPushButton::released, this,
765  &DatabaseManagementWidget::ClearTwoViewGeometries);
766  grid->addWidget(clear_two_view_geometries_button, 1, 1, Qt::AlignLeft);
767 
768  grid->setColumnStretch(1, 1);
769 }
770 
771 void DatabaseManagementWidget::showEvent(QShowEvent*) {
772  parent_->setDisabled(true);
773 
774  database_.Open(*options_->database_path);
775 
776  image_tab_->Reload();
777  camera_tab_->Reload();
778 }
779 
780 void DatabaseManagementWidget::hideEvent(QHideEvent*) {
781  parent_->setEnabled(true);
782 
783  image_tab_->Clear();
784  camera_tab_->Clear();
785 
786  database_.Close();
787 }
788 
789 void DatabaseManagementWidget::ClearMatches() {
790  QMessageBox::StandardButton reply = QMessageBox::question(
791  this, "", tr("Do you really want to clear all matches?"),
792  QMessageBox::Yes | QMessageBox::No);
793  if (reply == QMessageBox::No) {
794  return;
795  }
796  database_.ClearMatches();
797 }
798 
799 void DatabaseManagementWidget::ClearTwoViewGeometries() {
800  QMessageBox::StandardButton reply = QMessageBox::question(
801  this, "", tr("Do you really want to clear all two-view geometries?"),
802  QMessageBox::Yes | QMessageBox::No);
803  if (reply == QMessageBox::No) {
804  return;
805  }
806  database_.ClearTwoViewGeometries();
807 }
808 
809 } // namespace colmap
std::shared_ptr< core::Tensor > image
#define CAMERA_MODEL_CASES
Definition: camera_models.h:95
CameraTab(QWidget *parent, Database *database)
void SetWidth(const size_t width)
Definition: camera.h:164
void InitializeWithName(const std::string &model_name, const double focal_length, const size_t width, const size_t height)
Definition: camera.cc:212
void SetPriorFocalLength(const bool prior)
Definition: camera.h:170
const std::vector< double > & Params() const
Definition: camera.h:176
bool SetParamsFromString(const std::string &string)
Definition: camera.cc:172
camera_t CameraId() const
Definition: camera.h:154
std::string ModelName() const
Definition: camera.cc:49
bool HasPriorFocalLength() const
Definition: camera.h:168
size_t Width() const
Definition: camera.h:160
size_t Height() const
Definition: camera.h:162
void SetHeight(const size_t height)
Definition: camera.h:166
DatabaseManagementWidget(QWidget *parent, OptionManager *options)
FeatureMatches ReadMatches(const image_t image_id1, const image_t image_id2) const
Definition: database.cc:461
size_t NumImages() const
Definition: database.cc:329
bool ExistsMatches(const image_t image_id1, const image_t image_id2) const
Definition: database.cc:315
FeatureKeypoints ReadKeypoints(const image_t image_id) const
Definition: database.cc:436
size_t NumKeypoints() const
Definition: database.cc:331
bool ExistsInlierMatches(const image_t image_id1, const image_t image_id2) const
Definition: database.cc:321
std::vector< Camera > ReadAllCameras() const
Definition: database.cc:380
bool ExistsCamera(const camera_t camera_id) const
Definition: database.cc:295
void Open(const std::string &path)
Definition: database.cc:254
void ClearTwoViewGeometries() const
Definition: database.cc:861
void ClearMatches() const
Definition: database.cc:856
void UpdateCamera(const Camera &camera) const
Definition: database.cc:760
TwoViewGeometry ReadTwoViewGeometry(const image_t image_id1, const image_t image_id2) const
Definition: database.cc:498
Camera ReadCamera(const camera_t camera_id) const
Definition: database.cc:365
void UpdateImage(const Image &image) const
Definition: database.cc:783
size_t NumCameras() const
Definition: database.cc:327
std::vector< Image > ReadAllImages() const
Definition: database.cc:423
camera_t WriteCamera(const Camera &camera, const bool use_camera_id=false) const
Definition: database.cc:603
void ReadAndShowWithKeypoints(const std::string &path, const FeatureKeypoints &keypoints, const std::vector< char > &tri_mask)
void ReadAndShowWithMatches(const std::string &path1, const std::string &path2, const FeatureKeypoints &keypoints1, const FeatureKeypoints &keypoints2, const FeatureMatches &matches)
ImageTab(QWidget *parent, CameraTab *camera_tab, OptionManager *options, Database *database)
image_t ImageId() const
Definition: image.h:238
const std::string & Name() const
Definition: image.h:242
void Reload(const std::vector< Image > &images, const image_t image_id)
MatchesTab(QWidget *parent, OptionManager *options, Database *database)
std::shared_ptr< std::string > database_path
std::shared_ptr< std::string > image_path
OverlappingImagesWidget(QWidget *parent, OptionManager *options, Database *database)
void ShowMatches(const std::vector< Image > &images, const image_t image_id)
TwoViewGeometriesTab(QWidget *parent, OptionManager *options, Database *database)
void Reload(const std::vector< Image > &images, const image_t image_id)
void InitializeTable(const QStringList &table_header)
std::vector< size_t > sorted_matches_idxs_
std::vector< std::pair< const Image *, FeatureMatches > > matches_
FeatureImageViewerWidget * matches_viewer_widget_
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
const camera_t kInvalidCameraId
Definition: types.h:75
std::string JoinPaths(T const &... paths)
Definition: misc.h:128
uint32_t image_t
Definition: types.h:61
std::string VectorToCSV(const std::vector< T > &values)
Definition: misc.h:150
uint32_t camera_t
Definition: types.h:58
std::string to_string(const T &n)
Definition: Common.h:20