ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
DatabaseManagementWidget.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include "base/camera_models.h"
11 #include "util/misc.h"
12 #include "util/option_manager.h"
13 
14 namespace cloudViewer {
15 
16 using namespace colmap;
17 
18 TwoViewInfoTab::TwoViewInfoTab(QWidget* parent,
19  OptionManager* options,
20  Database* database)
21  : QWidget(parent),
22  options_(options),
23  database_(database),
24  matches_viewer_widget_(new FeatureImageViewerWidget(parent, "matches")) {}
25 
26 void TwoViewInfoTab::Clear() {
27  table_widget_->clearContents();
28  matches_.clear();
29  configs_.clear();
30  sorted_matches_idxs_.clear();
31 }
32 
33 void TwoViewInfoTab::InitializeTable(const QStringList& table_header) {
34  QGridLayout* grid = new QGridLayout(this);
35 
36  info_label_ = new QLabel(this);
37  grid->addWidget(info_label_, 0, 0);
38 
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);
43 
44  table_widget_ = new QTableWidget(this);
45  table_widget_->setColumnCount(table_header.size());
46  table_widget_->setHorizontalHeaderLabels(table_header);
47 
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);
55 
56  grid->addWidget(table_widget_, 1, 0, 1, 2);
57 }
58 
59 void TwoViewInfoTab::ShowMatches() {
60  QItemSelectionModel* select = table_widget_->selectionModel();
61 
62  if (!select->hasSelection()) {
63  QMessageBox::critical(this, "", tr("No image pair selected."));
64  return;
65  }
66 
67  if (select->selectedRows().size() > 1) {
68  QMessageBox::critical(this, "",
69  tr("Only one image pair may be selected."));
70  return;
71  }
72 
73  const size_t idx =
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());
82 
83  matches_viewer_widget_->setWindowTitle(QString::fromStdString(
84  "Matches for image pair " + std::to_string(image_->ImageId()) +
85  " - " + std::to_string(selection.first->ImageId())));
86  matches_viewer_widget_->ReadAndShowWithMatches(
87  path1, path2, keypoints1, keypoints2, selection.second);
88 }
89 
90 void TwoViewInfoTab::FillTable() {
91  // Sort the matched pairs according to number of matches in descending
92  // order.
93  sorted_matches_idxs_.resize(matches_.size());
94  std::iota(sorted_matches_idxs_.begin(), sorted_matches_idxs_.end(), 0);
95 
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();
100  });
101 
102  QString info;
103  info += QString("Matched images: ") + QString::number(matches_.size());
104  info_label_->setText(info);
105 
106  table_widget_->clearContents();
107  table_widget_->setRowCount(matches_.size());
108 
109  for (size_t i = 0; i < sorted_matches_idxs_.size(); ++i) {
110  const size_t idx = sorted_matches_idxs_[i];
111 
112  QTableWidgetItem* image_id_item = new QTableWidgetItem(
113  QString::number(matches_[idx].first->ImageId()));
114  table_widget_->setItem(i, 0, image_id_item);
115 
116  QTableWidgetItem* num_matches_item = new QTableWidgetItem(
117  QString::number(matches_[idx].second.size()));
118  table_widget_->setItem(i, 1, num_matches_item);
119 
120  // config for inlier matches tab
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);
125  }
126  }
127 
128  table_widget_->resizeColumnsToContents();
129 }
130 
131 MatchesTab::MatchesTab(QWidget* parent,
132  OptionManager* options,
133  Database* database)
134  : TwoViewInfoTab(parent, options, database) {
135  QStringList table_header;
136  table_header << "image_id"
137  << "num_matches";
138  InitializeTable(table_header);
139 }
140 
141 void MatchesTab::Reload(const std::vector<Image>& images,
142  const image_t image_id) {
143  matches_.clear();
144 
145  // Find all matched images
146 
147  for (const auto& image : images) {
148  if (image.ImageId() == image_id) {
149  image_ = &image;
150  continue;
151  }
152 
153  if (database_->ExistsMatches(image_id, image.ImageId())) {
154  const auto matches =
155  database_->ReadMatches(image_id, image.ImageId());
156 
157  if (matches.size() > 0) {
158  matches_.emplace_back(&image, matches);
159  }
160  }
161  }
162 
163  FillTable();
164 }
165 
167  OptionManager* options,
168  Database* database)
169  : TwoViewInfoTab(parent, options, database) {
170  QStringList table_header;
171  table_header << "image_id"
172  << "num_matches"
173  << "config";
174  InitializeTable(table_header);
175 }
176 
177 void TwoViewGeometriesTab::Reload(const std::vector<Image>& images,
178  const image_t image_id) {
179  matches_.clear();
180  configs_.clear();
181 
182  // Find all matched images.
183 
184  for (const auto& image : images) {
185  if (image.ImageId() == image_id) {
186  image_ = &image;
187  continue;
188  }
189 
190  if (database_->ExistsInlierMatches(image_id, image.ImageId())) {
191  const auto two_view_geometry =
192  database_->ReadTwoViewGeometry(image_id, image.ImageId());
193 
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);
197  }
198  }
199  }
200 
201  FillTable();
202 }
203 
205  OptionManager* options,
206  Database* database)
207  : parent_(parent), options_(options) {
208  // Do not change flag, to make sure feature database is not accessed from
209  // multiple threads.
210  setWindowFlags(Qt::Window);
211  resize(parent->size().width() - 20, parent->size().height() - 20);
212 
213  QGridLayout* grid = new QGridLayout(this);
214 
215  tab_widget_ = new QTabWidget(this);
216 
217  matches_tab_ = new MatchesTab(this, options_, database);
218  tab_widget_->addTab(matches_tab_, tr("Matches"));
219 
220  two_view_geometries_tab_ =
221  new TwoViewGeometriesTab(this, options_, database);
222  tab_widget_->addTab(two_view_geometries_tab_, tr("Two-view geometries"));
223 
224  grid->addWidget(tab_widget_, 0, 0);
225 
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);
230 }
231 
232 void OverlappingImagesWidget::ShowMatches(const std::vector<Image>& images,
233  const image_t image_id) {
234  parent_->setDisabled(true);
235 
236  setWindowTitle(QString::fromStdString("Matches for image " +
237  std::to_string(image_id)));
238 
239  matches_tab_->Reload(images, image_id);
240  two_view_geometries_tab_->Reload(images, image_id);
241 }
242 
243 void OverlappingImagesWidget::closeEvent(QCloseEvent*) {
244  matches_tab_->Clear();
245  two_view_geometries_tab_->Clear();
246  parent_->setEnabled(true);
247 }
248 
249 CameraTab::CameraTab(QWidget* parent, Database* database)
250  : QWidget(parent), database_(database) {
251  QGridLayout* grid = new QGridLayout(this);
252 
253  info_label_ = new QLabel(this);
254  grid->addWidget(info_label_, 0, 0);
255 
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);
259 
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);
264 
265  table_widget_ = new QTableWidget(this);
266  table_widget_->setColumnCount(6);
267 
268  QStringList table_header;
269  table_header << "camera_id"
270  << "model"
271  << "width"
272  << "height"
273  << "params"
274  << "prior_focal_length";
275  table_widget_->setHorizontalHeaderLabels(table_header);
276 
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);
282 
283  connect(table_widget_, &QTableWidget::itemChanged, this,
284  &CameraTab::itemChanged);
285 
286  grid->addWidget(table_widget_, 1, 0, 1, 3);
287 
288  grid->setColumnStretch(0, 1);
289 }
290 
292  QString info;
293  info += QString("Cameras: ") + QString::number(database_->NumCameras());
294  info_label_->setText(info);
295 
296  cameras_ = database_->ReadAllCameras();
297 
298  // Make sure, itemChanged is not invoked, while setting up the table.
299  table_widget_->blockSignals(true);
300 
301  table_widget_->clearContents();
302  table_widget_->setRowCount(cameras_.size());
303 
304  std::sort(cameras_.begin(), cameras_.end(),
305  [](const Camera& camera1, const Camera& camera2) {
306  return camera1.CameraId() < camera2.CameraId();
307  });
308 
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);
315 
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);
320 
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())));
325 
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())));
332  }
333  table_widget_->resizeColumnsToContents();
334 
335  table_widget_->blockSignals(false);
336 }
337 
339  cameras_.clear();
340  table_widget_->clearContents();
341 }
342 
343 void CameraTab::itemChanged(QTableWidgetItem* item) {
344  Camera& camera = cameras_.at(item->row());
345  const std::vector<double> prev_params = camera.Params();
346 
347  switch (item->column()) {
348  // case 0: never change the camera ID
349  // case 1: never change the camera model
350  case 2:
351  camera.SetWidth(
352  static_cast<size_t>(item->data(Qt::DisplayRole).toInt()));
353  break;
354  case 3:
355  camera.SetHeight(
356  static_cast<size_t>(item->data(Qt::DisplayRole).toInt()));
357  break;
358  case 4:
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);
366  }
367  break;
368  case 5:
369  camera.SetPriorFocalLength(
370  static_cast<bool>(item->data(Qt::DisplayRole).toInt()));
371  break;
372  default:
373  break;
374  }
375 
376  database_->UpdateCamera(camera);
377 }
378 
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
385 
386  bool ok;
387  const QString camera_model = QInputDialog::getItem(
388  this, "", tr("Model:"), camera_models, 0, false, &ok);
389  if (!ok) {
390  return;
391  }
392 
393  // Add new camera to feature database
394  Camera camera;
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,
400  kDefaultHeight);
401  database_->WriteCamera(camera);
402 
403  // Reload all cameras
404  Reload();
405 
406  // Highlight new camera
407  table_widget_->selectRow(cameras_.size() - 1);
408 }
409 
410 void CameraTab::SetModel() {
411  QItemSelectionModel* select = table_widget_->selectionModel();
412 
413  if (!select->hasSelection()) {
414  QMessageBox::critical(this, "", tr("No camera selected."));
415  return;
416  }
417 
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
423 
424  bool ok;
425  const QString camera_model = QInputDialog::getItem(
426  this, "", tr("Model:"), camera_models, 0, false, &ok);
427  if (!ok) {
428  return;
429  }
430 
431  // Make sure, itemChanged is not invoked, while updating up the table
432  table_widget_->blockSignals(true);
433 
434  for (QModelIndex& index : select->selectedRows()) {
435  std::cout << index.row() << std::endl;
436  auto& camera = cameras_.at(index.row());
437  camera.InitializeWithName(camera_model.toUtf8().constData(),
438  camera.MeanFocalLength(), camera.Width(),
439  camera.Height());
440  database_->UpdateCamera(camera);
441  }
442 
443  table_widget_->blockSignals(false);
444 
445  Reload();
446 }
447 
448 ImageTab::ImageTab(QWidget* parent,
449  CameraTab* camera_tab,
450  OptionManager* options,
451  Database* database)
452  : QWidget(parent),
453  camera_tab_(camera_tab),
454  options_(options),
455  database_(database) {
456  QGridLayout* grid = new QGridLayout(this);
457 
458  info_label_ = new QLabel(this);
459  grid->addWidget(info_label_, 0, 0);
460 
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);
465 
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);
471 
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);
476 
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);
482 
483  table_widget_ = new QTableWidget(this);
484  table_widget_->setColumnCount(10);
485 
486  QStringList table_header;
487  table_header << "image_id"
488  << "name"
489  << "camera_id"
490  << "qw"
491  << "qx"
492  << "qy"
493  << "qz"
494  << "tx"
495  << "ty"
496  << "tz";
497  table_widget_->setHorizontalHeaderLabels(table_header);
498 
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);
504 
505  connect(table_widget_, &QTableWidget::itemChanged, this,
506  &ImageTab::itemChanged);
507 
508  grid->addWidget(table_widget_, 1, 0, 1, 5);
509 
510  grid->setColumnStretch(0, 3);
511 
512  image_viewer_widget_ = new FeatureImageViewerWidget(parent, "keypoints");
513  overlapping_images_widget_ =
514  new OverlappingImagesWidget(parent, options, database_);
515 }
516 
518  QString info;
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);
523 
524  // use temp vector to avoid memory problem when directly assigning to member
525  // variable std::vector<Image> temp_images = database_->ReadAllImages();
526  // images_.clear();
527  // images_.swap(temp_images);
528  images_ = database_->ReadAllImages();
529 
530  // Make sure, itemChanged is not invoked, while setting up the table
531  table_widget_->blockSignals(true);
532 
533  table_widget_->clearContents();
534  table_widget_->setRowCount(images_.size());
535 
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(
543  i, 1,
544  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,
549  new QTableWidgetItem(QString::number(image.QvecPrior(0))));
550  table_widget_->setItem(
551  i, 4,
552  new QTableWidgetItem(QString::number(image.QvecPrior(1))));
553  table_widget_->setItem(
554  i, 5,
555  new QTableWidgetItem(QString::number(image.QvecPrior(2))));
556  table_widget_->setItem(
557  i, 6,
558  new QTableWidgetItem(QString::number(image.QvecPrior(3))));
559  table_widget_->setItem(
560  i, 7,
561  new QTableWidgetItem(QString::number(image.TvecPrior(0))));
562  table_widget_->setItem(
563  i, 8,
564  new QTableWidgetItem(QString::number(image.TvecPrior(1))));
565  table_widget_->setItem(
566  i, 9,
567  new QTableWidgetItem(QString::number(image.TvecPrior(2))));
568  }
569  table_widget_->resizeColumnsToContents();
570 
571  table_widget_->blockSignals(false);
572 }
573 
575  images_.clear();
576  table_widget_->clearContents();
577 }
578 
579 void ImageTab::itemChanged(QTableWidgetItem* item) {
580  Image& image = images_.at(item->row());
581  camera_t camera_id = kInvalidCameraId;
582 
583  switch (item->column()) {
584  // case 0: never change the image ID
585  case 1:
586  image.SetName(item->text().toUtf8().constData());
587  break;
588  case 2:
589  camera_id =
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);
597  } else {
598  image.SetCameraId(camera_id);
599  }
600  break;
601  case 3:
602  image.QvecPrior(0) = item->data(Qt::DisplayRole).toReal();
603  break;
604  case 4:
605  image.QvecPrior(1) = item->data(Qt::DisplayRole).toReal();
606  break;
607  case 5:
608  image.QvecPrior(2) = item->data(Qt::DisplayRole).toReal();
609  break;
610  case 6:
611  image.QvecPrior(3) = item->data(Qt::DisplayRole).toReal();
612  break;
613  case 7:
614  image.TvecPrior(0) = item->data(Qt::DisplayRole).toReal();
615  break;
616  case 8:
617  image.TvecPrior(1) = item->data(Qt::DisplayRole).toReal();
618  break;
619  case 9:
620  image.TvecPrior(2) = item->data(Qt::DisplayRole).toReal();
621  break;
622  default:
623  break;
624  }
625 
626  database_->UpdateImage(image);
627 }
628 
629 void ImageTab::ShowImage() {
630  QItemSelectionModel* select = table_widget_->selectionModel();
631 
632  if (!select->hasSelection()) {
633  QMessageBox::critical(this, "", tr("No image selected."));
634  return;
635  }
636 
637  if (select->selectedRows().size() > 1) {
638  QMessageBox::critical(this, "", tr("Only one image may be selected."));
639  return;
640  }
641 
642  const auto& image = images_[select->selectedRows().begin()->row()];
643 
644  const auto keypoints = database_->ReadKeypoints(image.ImageId());
645  const std::vector<char> tri_mask(keypoints.size(), false);
646 
647  image_viewer_widget_->ReadAndShowWithKeypoints(
648  JoinPaths(*options_->image_path, image.Name()), keypoints,
649  tri_mask);
650  image_viewer_widget_->setWindowTitle(
651  QString::fromStdString("Image " + std::to_string(image.ImageId())));
652 }
653 
654 void ImageTab::ShowMatches() {
655  QItemSelectionModel* select = table_widget_->selectionModel();
656 
657  if (!select->hasSelection()) {
658  QMessageBox::critical(this, "", tr("No image selected."));
659  return;
660  }
661 
662  if (select->selectedRows().size() > 1) {
663  QMessageBox::critical(this, "", tr("Only one image may be selected."));
664  return;
665  }
666 
667  const auto& image = images_[select->selectedRows().begin()->row()];
668 
669  overlapping_images_widget_->ShowMatches(images_, image.ImageId());
670  overlapping_images_widget_->show();
671  overlapping_images_widget_->raise();
672 }
673 
674 void ImageTab::SetCamera() {
675  QItemSelectionModel* select = table_widget_->selectionModel();
676 
677  if (!select->hasSelection()) {
678  QMessageBox::critical(this, "", tr("No image selected."));
679  return;
680  }
681 
682  bool ok;
683  const camera_t camera_id = static_cast<camera_t>(QInputDialog::getInt(
684  this, "", tr("camera_id"), 0, 0, INT_MAX, 1, &ok));
685  if (!ok) {
686  return;
687  }
688 
689  if (!database_->ExistsCamera(camera_id)) {
690  QMessageBox::critical(this, "", tr("camera_id does not exist."));
691  return;
692  }
693 
694  // Make sure, itemChanged is not invoked, while updating up the table
695  table_widget_->blockSignals(true);
696 
697  for (QModelIndex& index : select->selectedRows()) {
698  table_widget_->setItem(
699  index.row(), 2,
700  new QTableWidgetItem(QString::number(camera_id)));
701  auto& image = images_[index.row()];
702  image.SetCameraId(camera_id);
703  database_->UpdateImage(image);
704  }
705 
706  table_widget_->blockSignals(false);
707 }
708 
709 void ImageTab::SplitCamera() {
710  QItemSelectionModel* select = table_widget_->selectionModel();
711 
712  if (!select->hasSelection()) {
713  QMessageBox::critical(this, "", tr("No image selected."));
714  return;
715  }
716 
717  bool ok;
718  const camera_t camera_id = static_cast<camera_t>(QInputDialog::getInt(
719  this, "", tr("camera_id"), 0, 0, INT_MAX, 1, &ok));
720  if (!ok) {
721  return;
722  }
723 
724  if (!database_->ExistsCamera(camera_id)) {
725  QMessageBox::critical(this, "", tr("camera_id does not exist."));
726  return;
727  }
728 
729  const auto camera = database_->ReadCamera(camera_id);
730 
731  // Make sure, itemChanged is not invoked, while updating up the table
732  table_widget_->blockSignals(true);
733 
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(
739  index.row(), 2,
740  new QTableWidgetItem(QString::number(image.CameraId())));
741  }
742 
743  table_widget_->blockSignals(false);
744 
745  camera_tab_->Reload();
746 }
747 
749  OptionManager* options)
750  : parent_(parent), options_(options) {
751  setWindowFlags(Qt::Window);
752  setWindowTitle("Database management");
753  resize(parent->size().width() - 20, parent->size().height() - 20);
754 
755  QGridLayout* grid = new QGridLayout(this);
756 
757  tab_widget_ = new QTabWidget(this);
758 
759  camera_tab_ = new CameraTab(this, &database_);
760  image_tab_ = new ImageTab(this, camera_tab_, options_, &database_);
761 
762  tab_widget_->addTab(image_tab_, tr("Images"));
763  tab_widget_->addTab(camera_tab_, tr("Cameras"));
764 
765  grid->addWidget(tab_widget_, 0, 0, 1, 4);
766 
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);
772 
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);
778 
779  grid->setColumnStretch(1, 1);
780 }
781 
782 void DatabaseManagementWidget::showEvent(QShowEvent*) {
783  parent_->setDisabled(true);
784 
785  database_.Open(*options_->database_path);
786 
787  image_tab_->Reload();
788  camera_tab_->Reload();
789 }
790 
791 void DatabaseManagementWidget::hideEvent(QHideEvent*) {
792  parent_->setEnabled(true);
793 
794  image_tab_->Clear();
795  camera_tab_->Clear();
796 
797  database_.Close();
798 }
799 
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) {
805  return;
806  }
807  database_.ClearMatches();
808 }
809 
810 void DatabaseManagementWidget::ClearTwoViewGeometries() {
811  QMessageBox::StandardButton reply = QMessageBox::question(
812  this, "",
813  tr("Do you really want to clear all two-view geometries?"),
814  QMessageBox::Yes | QMessageBox::No);
815  if (reply == QMessageBox::No) {
816  return;
817  }
818  database_.ClearTwoViewGeometries();
819 }
820 
821 } // namespace cloudViewer
std::shared_ptr< core::Tensor > image
CameraTab(QWidget *parent, colmap::Database *database)
DatabaseManagementWidget(QWidget *parent, OptionManager *options)
void ReadAndShowWithKeypoints(const std::string &path, const colmap::FeatureKeypoints &keypoints, const std::vector< char > &tri_mask)
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)
void ShowMatches(const std::vector< colmap::Image > &images, const colmap::image_t image_id)
OverlappingImagesWidget(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_
void InitializeTable(const QStringList &table_header)
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
std::string JoinPaths(T const &...paths)
Definition: FileSystem.h:23
Generic file read and write utility for python interface.
colmap::OptionManager OptionManager
std::string to_string(const T &n)
Definition: Common.h:20
struct Window Window
Definition: sqlite3.c:14678