ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
point_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/point_viewer_widget.h"
33 
34 #include "ui/model_viewer_widget.h"
35 #include "util/misc.h"
36 
37 namespace colmap {
38 
40  ModelViewerWidget* model_viewer_widget,
41  OptionManager* options)
42  : QWidget(parent),
43  model_viewer_widget_(model_viewer_widget),
44  options_(options),
45  point3D_id_(kInvalidPoint3DId),
46  zoom_(250.0 / 1024.0) {
47  setWindowFlags(Qt::Window);
48  resize(parent->size().width() - 20, parent->size().height() - 20);
49 
50  QFont font;
51  font.setPointSize(10);
52  setFont(font);
53 
54  QGridLayout* grid = new QGridLayout(this);
55  grid->setContentsMargins(5, 5, 5, 5);
56 
57  info_table_ = new QTableWidget(this);
58  info_table_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
59  info_table_->setEditTriggers(QAbstractItemView::NoEditTriggers);
60  info_table_->setSelectionMode(QAbstractItemView::SingleSelection);
61  info_table_->setShowGrid(true);
62  info_table_->horizontalHeader()->setStretchLastSection(true);
63  info_table_->horizontalHeader()->setVisible(false);
64  info_table_->verticalHeader()->setVisible(false);
65  info_table_->verticalHeader()->setDefaultSectionSize(18);
66 
67  info_table_->setColumnCount(2);
68  info_table_->setRowCount(3);
69 
70  info_table_->setItem(0, 0, new QTableWidgetItem("position"));
71  xyz_item_ = new QTableWidgetItem();
72  info_table_->setItem(0, 1, xyz_item_);
73 
74  info_table_->setItem(1, 0, new QTableWidgetItem("color"));
75  rgb_item_ = new QTableWidgetItem();
76  info_table_->setItem(1, 1, rgb_item_);
77 
78  info_table_->setItem(2, 0, new QTableWidgetItem("error"));
79  error_item_ = new QTableWidgetItem();
80  info_table_->setItem(2, 1, error_item_);
81 
82  grid->addWidget(info_table_, 0, 0);
83 
84  location_table_ = new QTableWidget(this);
85  location_table_->setColumnCount(4);
86  QStringList table_header;
87  table_header << "image_id"
88  << "reproj_error"
89  << "track_location"
90  << "image_name";
91  location_table_->setHorizontalHeaderLabels(table_header);
92  location_table_->resizeColumnsToContents();
93  location_table_->setShowGrid(true);
94  location_table_->horizontalHeader()->setStretchLastSection(true);
95  location_table_->verticalHeader()->setVisible(true);
96  location_table_->setSelectionMode(QAbstractItemView::NoSelection);
97  location_table_->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
98  location_table_->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
99  location_table_->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
100 
101  grid->addWidget(location_table_, 1, 0);
102 
103  QHBoxLayout* button_layout = new QHBoxLayout();
104 
105  zoom_in_button_ = new QPushButton(tr("+"), this);
106  zoom_in_button_->setFont(font);
107  zoom_in_button_->setFixedWidth(50);
108  button_layout->addWidget(zoom_in_button_);
109  connect(zoom_in_button_, &QPushButton::released, this,
110  &PointViewerWidget::ZoomIn);
111 
112  zoom_out_button_ = new QPushButton(tr("-"), this);
113  zoom_out_button_->setFont(font);
114  zoom_out_button_->setFixedWidth(50);
115  button_layout->addWidget(zoom_out_button_);
116  connect(zoom_out_button_, &QPushButton::released, this,
117  &PointViewerWidget::ZoomOut);
118 
119  delete_button_ = new QPushButton(tr("Delete"), this);
120  button_layout->addWidget(delete_button_);
121  connect(delete_button_, &QPushButton::released, this,
122  &PointViewerWidget::Delete);
123 
124  grid->addLayout(button_layout, 2, 0, Qt::AlignRight);
125 }
126 
127 void PointViewerWidget::Show(const point3D_t point3D_id) {
128  location_pixmaps_.clear();
129  image_ids_.clear();
130  reproj_errors_.clear();
131  image_names_.clear();
132 
133  if (model_viewer_widget_->points3D.count(point3D_id) == 0) {
134  point3D_id_ = kInvalidPoint3DId;
135  ClearLocations();
136  return;
137  }
138 
139  show();
140  raise();
141 
142  point3D_id_ = point3D_id;
143 
144  // Show some general information about the point.
145 
146  setWindowTitle(QString::fromStdString("Point " + std::to_string(point3D_id)));
147 
148  const auto& point3D = model_viewer_widget_->points3D[point3D_id];
149 
150  xyz_item_->setText(QString::number(point3D.X()) + ", " +
151  QString::number(point3D.Y()) + ", " +
152  QString::number(point3D.Z()));
153  rgb_item_->setText(QString::number(point3D.Color(0)) + ", " +
154  QString::number(point3D.Color(1)) + ", " +
155  QString::number(point3D.Color(2)));
156  error_item_->setText(QString::number(point3D.Error()));
157 
158  ResizeInfoTable();
159 
160  // Sort the track elements by the image names.
161 
162  std::vector<std::pair<TrackElement, std::string>> track_idx_image_name_pairs;
163  track_idx_image_name_pairs.reserve(point3D.Track().Length());
164  for (const auto& track_el : point3D.Track().Elements()) {
165  const Image& image = model_viewer_widget_->images[track_el.image_id];
166  track_idx_image_name_pairs.emplace_back(track_el, image.Name());
167  }
168 
169  std::sort(track_idx_image_name_pairs.begin(),
170  track_idx_image_name_pairs.end(),
171  [](const std::pair<TrackElement, std::string>& track_el1,
172  const std::pair<TrackElement, std::string>& track_el2) {
173  return track_el1.second < track_el2.second;
174  });
175 
176  // Paint features for each track element.
177 
178  for (const auto& track_el : track_idx_image_name_pairs) {
179  const Image& image = model_viewer_widget_->images[track_el.first.image_id];
180  const Camera& camera = model_viewer_widget_->cameras[image.CameraId()];
181  const Point2D& point2D = image.Point2D(track_el.first.point2D_idx);
182  const Eigen::Vector2d proj_point2D =
183  ProjectPointToImage(point3D.XYZ(), image.ProjectionMatrix(), camera);
184  const double reproj_error = (point2D.XY() - proj_point2D).norm();
185 
186  Bitmap bitmap;
187  const std::string path = JoinPaths(*options_->image_path, image.Name());
188  if (!bitmap.Read(path, true)) {
189  std::cerr << "ERROR: Cannot read image at path " << path << std::endl;
190  continue;
191  }
192 
193  QPixmap pixmap = QPixmap::fromImage(BitmapToQImageRGB(bitmap));
194 
195  // Paint feature in current image.
196  QPainter painter(&pixmap);
197  painter.setRenderHint(QPainter::Antialiasing);
198 
199  QPen pen;
200  pen.setWidth(3);
201  pen.setColor(Qt::green);
202  painter.setPen(pen);
203 
204  const int kCrossSize = 15;
205  const int x = static_cast<int>(std::round(point2D.X()));
206  const int y = static_cast<int>(std::round(point2D.Y()));
207  painter.drawLine(x - kCrossSize, y - kCrossSize, x + kCrossSize,
208  y + kCrossSize);
209  painter.drawLine(x - kCrossSize, y + kCrossSize, x + kCrossSize,
210  y - kCrossSize);
211 
212  pen.setColor(Qt::red);
213  painter.setPen(pen);
214 
215  const int proj_x = static_cast<int>(std::round(proj_point2D.x()));
216  const int proj_y = static_cast<int>(std::round(proj_point2D.y()));
217  painter.drawEllipse(proj_x - 5, proj_y - 5, 10, 10);
218  painter.drawEllipse(proj_x - 15, proj_y - 15, 30, 30);
219  painter.drawEllipse(proj_x - 45, proj_y - 45, 90, 90);
220 
221  location_pixmaps_.push_back(pixmap);
222  image_ids_.push_back(track_el.first.image_id);
223  reproj_errors_.push_back(reproj_error);
224  image_names_.push_back(image.Name());
225  }
226 
227  UpdateImages();
228 }
229 
230 void PointViewerWidget::closeEvent(QCloseEvent* event) {
231  // Release the images, since zoomed in images can use a lot of memory.
232  location_pixmaps_.clear();
233  image_ids_.clear();
234  reproj_errors_.clear();
235  image_names_.clear();
236  ClearLocations();
237 }
238 
239 void PointViewerWidget::ResizeInfoTable() {
240  // Set fixed table dimensions.
241  info_table_->resizeColumnsToContents();
242  int height =
243  info_table_->horizontalHeader()->height() + 2 * info_table_->frameWidth();
244  for (int i = 0; i < info_table_->rowCount(); i++) {
245  height += info_table_->rowHeight(i);
246  }
247  info_table_->setFixedHeight(height);
248 }
249 
250 void PointViewerWidget::ClearLocations() {
251  while (location_table_->rowCount() > 0) {
252  location_table_->removeRow(0);
253  }
254  for (auto location_label : location_labels_) {
255  delete location_label;
256  }
257  location_labels_.clear();
258 }
259 
260 void PointViewerWidget::UpdateImages() {
261  ClearLocations();
262 
263  location_table_->setRowCount(static_cast<int>(location_pixmaps_.size()));
264 
265  for (size_t i = 0; i < location_pixmaps_.size(); ++i) {
266  QLabel* image_id_label = new QLabel(QString::number(image_ids_[i]), this);
267  image_id_label->setAlignment(Qt::AlignCenter);
268  location_table_->setCellWidget(i, 0, image_id_label);
269  location_labels_.push_back(image_id_label);
270 
271  QLabel* error_label = new QLabel(QString::number(reproj_errors_[i]), this);
272  error_label->setAlignment(Qt::AlignCenter);
273  location_table_->setCellWidget(i, 1, error_label);
274  location_labels_.push_back(error_label);
275 
276  const QPixmap& pixmap = location_pixmaps_[i];
277  QLabel* image_label = new QLabel(this);
278  image_label->setPixmap(
279  pixmap.scaledToWidth(zoom_ * pixmap.width(), Qt::FastTransformation));
280  location_table_->setCellWidget(i, 2, image_label);
281  location_table_->resizeRowToContents(i);
282  location_labels_.push_back(image_label);
283 
284  QLabel* image_name_label = new QLabel(image_names_[i].c_str(), this);
285  image_name_label->setAlignment(Qt::AlignCenter);
286  location_table_->setCellWidget(i, 3, image_name_label);
287  location_labels_.push_back(image_name_label);
288  }
289  location_table_->resizeColumnToContents(2);
290 }
291 
292 void PointViewerWidget::ZoomIn() {
293  zoom_ *= 1.33;
294  UpdateImages();
295 }
296 
297 void PointViewerWidget::ZoomOut() {
298  zoom_ /= 1.3;
299  UpdateImages();
300 }
301 
302 void PointViewerWidget::Delete() {
303  QMessageBox::StandardButton reply = QMessageBox::question(
304  this, "", tr("Do you really want to delete this point?"),
305  QMessageBox::Yes | QMessageBox::No);
306  if (reply == QMessageBox::Yes) {
307  if (model_viewer_widget_->reconstruction->ExistsPoint3D(point3D_id_)) {
308  model_viewer_widget_->reconstruction->DeletePoint3D(point3D_id_);
309  }
310  model_viewer_widget_->ReloadReconstruction();
311  }
312 }
313 
314 } // 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::unordered_map< point3D_t, Point3D > points3D
std::unordered_map< camera_t, Camera > cameras
std::unordered_map< image_t, Image > images
std::shared_ptr< std::string > image_path
double X() const
Definition: point2d.h:57
double Y() const
Definition: point2d.h:59
const Eigen::Vector2d & XY() const
Definition: point2d.h:53
PointViewerWidget(QWidget *parent, ModelViewerWidget *model_viewer_widget, OptionManager *option)
void Show(const point3D_t point3D_id)
void DeletePoint3D(const point3D_t point3D_id)
bool ExistsPoint3D(const point3D_t point3D_id) const
normal_z y
normal_z x
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
uint64_t point3D_t
Definition: types.h:72
QImage BitmapToQImageRGB(const Bitmap &bitmap)
Definition: qt_utils.cc:59
std::string JoinPaths(T const &... paths)
Definition: misc.h:128
const point3D_t kInvalidPoint3DId
Definition: types.h:80
Eigen::Vector2d ProjectPointToImage(const Eigen::Vector3d &point3D, const Eigen::Matrix3x4d &proj_matrix, const Camera &camera)
Definition: projection.cc:104
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb green(0, MAX, 0)
std::string to_string(const T &n)
Definition: Common.h:20