ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ImageViewerWidget.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 
8 #include "ImageViewerWidget.h"
9 
10 #include "ModelViewerWidget.h"
11 #include "base/database.h"
12 #include "base/projection.h"
13 #include "base/reconstruction.h"
14 #include "ui/qt_utils.h"
15 #include "util/misc.h"
16 #include "util/option_manager.h"
17 
18 namespace cloudViewer {
19 
20 using namespace colmap;
21 
22 const double ImageViewerWidget::kZoomFactor = 1.20;
23 
25  setSceneRect(0, 0, 0, 0);
26  image_pixmap_item_ = addPixmap(QPixmap::fromImage(QImage()));
27  image_pixmap_item_->setZValue(-1);
28 }
29 
30 QGraphicsPixmapItem* ImageViewerGraphicsScene::ImagePixmapItem() const {
31  return image_pixmap_item_;
32 }
33 
34 ImageViewerWidget::ImageViewerWidget(QWidget* parent) : QWidget(parent) {
35  setWindowFlags(Qt::Window | Qt::WindowTitleHint |
36  Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint |
37  Qt::WindowCloseButtonHint);
38 
39  resize(parent->width() - 20, parent->height() - 20);
40 
41  QFont font;
42  font.setPointSize(10);
43  setFont(font);
44 
45  grid_layout_ = new QGridLayout(this);
46  grid_layout_->setContentsMargins(5, 5, 5, 5);
47 
48  graphics_view_ = new QGraphicsView();
49  graphics_view_->setSizePolicy(QSizePolicy::Expanding,
50  QSizePolicy::Expanding);
51 
52  graphics_view_->setScene(&graphics_scene_);
53  graphics_view_->setAlignment(Qt::AlignLeft | Qt::AlignTop);
54 
55  grid_layout_->addWidget(graphics_view_, 1, 0);
56 
57  button_layout_ = new QHBoxLayout();
58 
59  QPushButton* zoom_in_button = new QPushButton("+", this);
60  zoom_in_button->setFont(font);
61  zoom_in_button->setFixedWidth(50);
62  button_layout_->addWidget(zoom_in_button);
63  connect(zoom_in_button, &QPushButton::released, this,
65 
66  QPushButton* zoom_out_button = new QPushButton("-", this);
67  zoom_out_button->setFont(font);
68  zoom_out_button->setFixedWidth(50);
69  button_layout_->addWidget(zoom_out_button);
70  connect(zoom_out_button, &QPushButton::released, this,
72 
73  QPushButton* save_button = new QPushButton("Save image", this);
74  save_button->setFont(font);
75  button_layout_->addWidget(save_button);
76  connect(save_button, &QPushButton::released, this,
78 
79  grid_layout_->addLayout(button_layout_, 2, 0, Qt::AlignRight);
80 }
81 
83  QWidget::resizeEvent(event);
84 
85  graphics_view_->fitInView(graphics_scene_.sceneRect(), Qt::KeepAspectRatio);
86 }
87 
89  graphics_scene_.ImagePixmapItem()->setPixmap(QPixmap());
90 }
91 
92 void ImageViewerWidget::ShowBitmap(const Bitmap& bitmap) {
93  ShowPixmap(QPixmap::fromImage(BitmapToQImageRGB(bitmap)));
94 }
95 
96 void ImageViewerWidget::ShowPixmap(const QPixmap& pixmap) {
97  graphics_scene_.ImagePixmapItem()->setPixmap(pixmap);
98  graphics_scene_.setSceneRect(pixmap.rect());
99 
100  show();
101  graphics_view_->fitInView(graphics_scene_.sceneRect(), Qt::KeepAspectRatio);
102 
103  raise();
104 }
105 
106 void ImageViewerWidget::ReadAndShow(const std::string& path) {
107  Bitmap bitmap;
108  if (!bitmap.Read(path, true)) {
109  std::cerr << "ERROR: Cannot read image at path " << path << std::endl;
110  }
111 
112  ShowBitmap(bitmap);
113 }
114 
116  graphics_view_->scale(kZoomFactor, kZoomFactor);
117 }
118 
120  graphics_view_->scale(1.0 / kZoomFactor, 1.0 / kZoomFactor);
121 }
122 
124  QString filter("PNG (*.png)");
125  const QString save_path =
126  QFileDialog::getSaveFileName(
127  this, tr("Select destination..."), "",
128  "PNG (*.png);;JPEG (*.jpg);;BMP (*.bmp)", &filter)
129  .toUtf8()
130  .constData();
131 
132  // Selection canceled?
133  if (save_path == "") {
134  return;
135  }
136 
137  graphics_scene_.ImagePixmapItem()->pixmap().save(save_path);
138 }
139 
141  QWidget* parent, const std::string& switch_text)
142  : ImageViewerWidget(parent),
143  switch_state_(true),
144  switch_text_(switch_text) {
146  new QPushButton(tr(("Hide " + switch_text_).c_str()), this);
147  switch_button_->setFont(font());
148  button_layout_->addWidget(switch_button_);
149  connect(switch_button_, &QPushButton::released, this,
151 }
152 
154  const std::string& path,
155  const FeatureKeypoints& keypoints,
156  const std::vector<char>& tri_mask) {
157  Bitmap bitmap;
158  if (!bitmap.Read(path, true)) {
159  std::cerr << "ERROR: Cannot read image at path " << path << std::endl;
160  }
161 
162  image1_ = QPixmap::fromImage(BitmapToQImageRGB(bitmap));
163  image2_ = image1_;
164 
165  const size_t num_tri_keypoints =
166  std::count_if(tri_mask.begin(), tri_mask.end(),
167  [](const bool tri) { return tri; });
168 
169  FeatureKeypoints keypoints_tri(num_tri_keypoints);
170  FeatureKeypoints keypoints_not_tri(keypoints.size() - num_tri_keypoints);
171  size_t i_tri = 0;
172  size_t i_not_tri = 0;
173  for (size_t i = 0; i < tri_mask.size(); ++i) {
174  if (tri_mask[i]) {
175  keypoints_tri[i_tri] = keypoints[i];
176  i_tri += 1;
177  } else {
178  keypoints_not_tri[i_not_tri] = keypoints[i];
179  i_not_tri += 1;
180  }
181  }
182 
183  DrawKeypoints(&image2_, keypoints_tri, Qt::magenta);
184  DrawKeypoints(&image2_, keypoints_not_tri, Qt::red);
185 
186  if (switch_state_) {
188  } else {
190  }
191 }
192 
194  const std::string& path1,
195  const std::string& path2,
196  const FeatureKeypoints& keypoints1,
197  const FeatureKeypoints& keypoints2,
198  const FeatureMatches& matches) {
199  Bitmap bitmap1;
200  Bitmap bitmap2;
201  if (!bitmap1.Read(path1, true) || !bitmap2.Read(path2, true)) {
202  std::cerr << "ERROR: Cannot read images at paths " << path1 << " and "
203  << path2 << std::endl;
204  return;
205  }
206 
207  const auto image1 = QPixmap::fromImage(BitmapToQImageRGB(bitmap1));
208  const auto image2 = QPixmap::fromImage(BitmapToQImageRGB(bitmap2));
209 
210  image1_ = ShowImagesSideBySide(image1, image2);
211  image2_ = DrawMatches(image1, image2, keypoints1, keypoints2, matches);
212 
213  if (switch_state_) {
215  } else {
217  }
218 }
219 
221  if (switch_state_) {
222  switch_button_->setText(std::string("Show " + switch_text_).c_str());
224  switch_state_ = false;
225  } else {
226  switch_button_->setText(std::string("Hide " + switch_text_).c_str());
228  switch_state_ = true;
229  }
230 }
231 
233  QWidget* parent,
234  ModelViewerWidget* model_viewer_widget,
235  OptionManager* options)
236  : FeatureImageViewerWidget(parent, "keypoints"),
237  model_viewer_widget_(model_viewer_widget),
238  options_(options) {
239  setWindowTitle("Image information");
240 
241  table_widget_ = new QTableWidget(this);
242  table_widget_->setColumnCount(2);
243  table_widget_->setRowCount(11);
244 
245  QFont font;
246  font.setPointSize(10);
247  table_widget_->setFont(font);
248 
249  table_widget_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
250 
251  table_widget_->setEditTriggers(QAbstractItemView::NoEditTriggers);
252  table_widget_->setSelectionMode(QAbstractItemView::SingleSelection);
253  table_widget_->setShowGrid(true);
254 
255  table_widget_->horizontalHeader()->setStretchLastSection(true);
256  table_widget_->horizontalHeader()->setVisible(false);
257  table_widget_->verticalHeader()->setVisible(false);
258  table_widget_->verticalHeader()->setDefaultSectionSize(18);
259 
260  int table_row = 0;
261 
262  table_widget_->setItem(table_row, 0, new QTableWidgetItem("image_id"));
263  image_id_item_ = new QTableWidgetItem();
264  table_widget_->setItem(table_row, 1, image_id_item_);
265  table_row += 1;
266 
267  table_widget_->setItem(table_row, 0, new QTableWidgetItem("camera_id"));
268  camera_id_item_ = new QTableWidgetItem();
269  table_widget_->setItem(table_row, 1, camera_id_item_);
270  table_row += 1;
271 
272  table_widget_->setItem(table_row, 0, new QTableWidgetItem("camera_model"));
273  camera_model_item_ = new QTableWidgetItem();
274  table_widget_->setItem(table_row, 1, camera_model_item_);
275  table_row += 1;
276 
277  table_widget_->setItem(table_row, 0, new QTableWidgetItem("camera_params"));
278  camera_params_item_ = new QTableWidgetItem();
279  table_widget_->setItem(table_row, 1, camera_params_item_);
280  table_row += 1;
281 
282  table_widget_->setItem(table_row, 0,
283  new QTableWidgetItem("qw, qx, qy, qz"));
284  qvec_item_ = new QTableWidgetItem();
285  table_widget_->setItem(table_row, 1, qvec_item_);
286  table_row += 1;
287 
288  table_widget_->setItem(table_row, 0, new QTableWidgetItem("tx, ty, tz"));
289  tvec_item_ = new QTableWidgetItem();
290  table_widget_->setItem(table_row, 1, tvec_item_);
291  table_row += 1;
292 
293  table_widget_->setItem(table_row, 0, new QTableWidgetItem("dims"));
294  dimensions_item_ = new QTableWidgetItem();
295  table_widget_->setItem(table_row, 1, dimensions_item_);
296  table_row += 1;
297 
298  table_widget_->setItem(table_row, 0, new QTableWidgetItem("num_points2D"));
299  num_points2D_item_ = new QTableWidgetItem();
300  num_points2D_item_->setForeground(Qt::red);
301  table_widget_->setItem(table_row, 1, num_points2D_item_);
302  table_row += 1;
303 
304  table_widget_->setItem(table_row, 0, new QTableWidgetItem("num_points3D"));
305  num_points3D_item_ = new QTableWidgetItem();
306  num_points3D_item_->setForeground(Qt::magenta);
307  table_widget_->setItem(table_row, 1, num_points3D_item_);
308  table_row += 1;
309 
310  table_widget_->setItem(table_row, 0,
311  new QTableWidgetItem("num_observations"));
312  num_obs_item_ = new QTableWidgetItem();
313  table_widget_->setItem(table_row, 1, num_obs_item_);
314  table_row += 1;
315 
316  table_widget_->setItem(table_row, 0, new QTableWidgetItem("name"));
317  name_item_ = new QTableWidgetItem();
318  table_widget_->setItem(table_row, 1, name_item_);
319  table_row += 1;
320 
321  grid_layout_->addWidget(table_widget_, 0, 0);
322 
323  delete_button_ = new QPushButton(tr("Delete"), this);
324  delete_button_->setFont(font);
325  button_layout_->addWidget(delete_button_);
326  connect(delete_button_, &QPushButton::released, this,
327  &DatabaseImageViewerWidget::DeleteImage);
328 }
329 
330 void DatabaseImageViewerWidget::ShowImageWithId(const image_t image_id) {
331  if (model_viewer_widget_->images.count(image_id) == 0) {
332  return;
333  }
334 
335  image_id_ = image_id;
336 
337  const Image& image = model_viewer_widget_->images.at(image_id);
338  const Camera& camera = model_viewer_widget_->cameras.at(image.CameraId());
339 
340  image_id_item_->setText(QString::number(image_id));
341  camera_id_item_->setText(QString::number(image.CameraId()));
342  camera_model_item_->setText(QString::fromStdString(camera.ModelName()));
343  camera_params_item_->setText(
344  QString::fromStdString(camera.ParamsToString()));
345  qvec_item_->setText(QString::number(image.Qvec(0)) + ", " +
346  QString::number(image.Qvec(1)) + ", " +
347  QString::number(image.Qvec(2)) + ", " +
348  QString::number(image.Qvec(3)));
349  tvec_item_->setText(QString::number(image.Tvec(0)) + ", " +
350  QString::number(image.Tvec(1)) + ", " +
351  QString::number(image.Tvec(2)));
352  dimensions_item_->setText(QString::number(camera.Width()) + "x" +
353  QString::number(camera.Height()));
354  num_points2D_item_->setText(QString::number(image.NumPoints2D()));
355 
356  std::vector<char> tri_mask(image.NumPoints2D());
357  for (size_t i = 0; i < image.NumPoints2D(); ++i) {
358  tri_mask[i] = image.Point2D(i).HasPoint3D();
359  }
360 
361  num_points3D_item_->setText(QString::number(image.NumPoints3D()));
362  num_obs_item_->setText(QString::number(image.NumObservations()));
363  name_item_->setText(QString::fromStdString(image.Name()));
364 
365  ResizeTable();
366 
367  FeatureKeypoints keypoints(image.NumPoints2D());
368  for (point2D_t i = 0; i < image.NumPoints2D(); ++i) {
369  keypoints[i].x = static_cast<float>(image.Point2D(i).X());
370  keypoints[i].y = static_cast<float>(image.Point2D(i).Y());
371  }
372 
373  const std::string path = JoinPaths(*options_->image_path, image.Name());
374  ReadAndShowWithKeypoints(path, keypoints, tri_mask);
375 }
376 
377 void DatabaseImageViewerWidget::ResizeTable() {
378  // Set fixed table dimensions.
379  table_widget_->resizeColumnsToContents();
380  int height = table_widget_->horizontalHeader()->height() +
381  2 * table_widget_->frameWidth();
382  for (int i = 0; i < table_widget_->rowCount(); i++) {
383  height += table_widget_->rowHeight(i);
384  }
385  table_widget_->setFixedHeight(height);
386 }
387 
388 void DatabaseImageViewerWidget::DeleteImage() {
389  QMessageBox::StandardButton reply = QMessageBox::question(
390  this, "", tr("Do you really want to delete this image?"),
391  QMessageBox::Yes | QMessageBox::No);
392  if (reply == QMessageBox::Yes) {
393  if (model_viewer_widget_->reconstruction->ExistsImage(image_id_)) {
394  model_viewer_widget_->reconstruction->DeRegisterImage(image_id_);
395  }
396  model_viewer_widget_->ReloadReconstruction();
397  }
398  hide();
399 }
400 
401 } // namespace cloudViewer
MouseEvent event
std::shared_ptr< core::Tensor > image
int height
DatabaseImageViewerWidget(QWidget *parent, ModelViewerWidget *ModelViewerWidget, OptionManager *options)
void ShowImageWithId(const colmap::image_t image_id)
void ReadAndShowWithMatches(const std::string &path1, const std::string &path2, const colmap::FeatureKeypoints &keypoints1, const colmap::FeatureKeypoints &keypoints2, const colmap::FeatureMatches &matches)
void ReadAndShowWithKeypoints(const std::string &path, const colmap::FeatureKeypoints &keypoints, const std::vector< char > &tri_mask)
FeatureImageViewerWidget(QWidget *parent, const std::string &switch_text)
QGraphicsPixmapItem * ImagePixmapItem() const
void ShowBitmap(const colmap::Bitmap &bitmap)
void resizeEvent(QResizeEvent *event)
void closeEvent(QCloseEvent *event)
void ShowPixmap(const QPixmap &pixmap)
void ReadAndShow(const std::string &path)
std::unordered_map< colmap::camera_t, colmap::Camera > cameras
colmap::Reconstruction * reconstruction
std::unordered_map< colmap::image_t, colmap::Image > images
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
Tensor Minimum(const Tensor &input, const Tensor &other)
Computes the element-wise minimum of input and other. The tensors must have same data type and device...
static const std::string path
Definition: PointCloud.cpp:59
std::string JoinPaths(T const &...paths)
Definition: FileSystem.h:23
Generic file read and write utility for python interface.
colmap::OptionManager OptionManager
constexpr Rgb magenta(MAX, 0, MAX)
constexpr Rgb red(MAX, 0, 0)
struct Window Window
Definition: sqlite3.c:14678