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