ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
model_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/model_viewer_widget.h"
33 
34 #include "ui/main_window.h"
35 
36 #include <QtCompat.h>
37 
38 #define SELECTION_BUFFER_IMAGE_IDX 0
39 #define SELECTION_BUFFER_POINT_IDX 1
40 
41 const Eigen::Vector4f kSelectedPointColor(0.0f, 1.0f, 0.0f, 1.0f);
42 
43 const Eigen::Vector4f kSelectedImagePlaneColor(1.0f, 0.0f, 1.0f, 0.6f);
44 const Eigen::Vector4f kSelectedImageFrameColor(0.8f, 0.0f, 0.8f, 1.0f);
45 
46 const Eigen::Vector4f kMovieGrabberImagePlaneColor(0.0f, 1.0f, 1.0f, 0.6f);
47 const Eigen::Vector4f kMovieGrabberImageFrameColor(0.0f, 0.8f, 0.8f, 1.0f);
48 
49 const Eigen::Vector4f kGridColor(0.2f, 0.2f, 0.2f, 0.6f);
50 const Eigen::Vector4f kXAxisColor(0.9f, 0.0f, 0.0f, 0.5f);
51 const Eigen::Vector4f kYAxisColor(0.0f, 0.9f, 0.0f, 0.5f);
52 const Eigen::Vector4f kZAxisColor(0.0f, 0.0f, 0.9f, 0.5f);
53 
54 namespace colmap {
55 namespace {
56 
57 // Generate unique index from RGB color in the range [0, 256^3].
58 inline size_t RGBToIndex(const uint8_t r, const uint8_t g, const uint8_t b) {
59  return static_cast<size_t>(r) + static_cast<size_t>(g) * 256 +
60  static_cast<size_t>(b) * 65536;
61 }
62 
63 // Derive color from unique index, generated by `RGBToIndex`.
64 inline Eigen::Vector4f IndexToRGB(const size_t index) {
65  Eigen::Vector4f color;
66  color(0) = ((index & 0x000000FF) >> 0) / 255.0f;
67  color(1) = ((index & 0x0000FF00) >> 8) / 255.0f;
68  color(2) = ((index & 0x00FF0000) >> 16) / 255.0f;
69  color(3) = 1.0f;
70  return color;
71 }
72 
73 void BuildImageModel(const Image& image, const Camera& camera,
74  const float image_size, const Eigen::Vector4f& plane_color,
75  const Eigen::Vector4f& frame_color,
76  std::vector<TrianglePainter::Data>* triangle_data,
77  std::vector<LinePainter::Data>* line_data) {
78  // Generate camera dimensions in OpenGL (world) coordinate space.
79  const float kBaseCameraWidth = 1024.0f;
80  const float image_width = image_size * camera.Width() / kBaseCameraWidth;
81  const float image_height = image_width * static_cast<float>(camera.Height()) /
82  static_cast<float>(camera.Width());
83  const float image_extent = std::max(image_width, image_height);
84  const float camera_extent = std::max(camera.Width(), camera.Height());
85  const float camera_extent_world =
86  static_cast<float>(camera.ImageToWorldThreshold(camera_extent));
87  const float focal_length = 2.0f * image_extent / camera_extent_world;
88 
89  const Eigen::Matrix<float, 3, 4> inv_proj_matrix =
90  image.InverseProjectionMatrix().cast<float>();
91 
92  // Projection center, top-left, top-right, bottom-right, bottom-left corners.
93 
94  const Eigen::Vector3f pc = inv_proj_matrix.rightCols<1>();
95  const Eigen::Vector3f tl =
96  inv_proj_matrix *
97  Eigen::Vector4f(-image_width, image_height, focal_length, 1);
98  const Eigen::Vector3f tr =
99  inv_proj_matrix *
100  Eigen::Vector4f(image_width, image_height, focal_length, 1);
101  const Eigen::Vector3f br =
102  inv_proj_matrix *
103  Eigen::Vector4f(image_width, -image_height, focal_length, 1);
104  const Eigen::Vector3f bl =
105  inv_proj_matrix *
106  Eigen::Vector4f(-image_width, -image_height, focal_length, 1);
107 
108  // Image plane as two triangles.
109  if (triangle_data != nullptr) {
110  triangle_data->emplace_back(
111  PointPainter::Data(tl(0), tl(1), tl(2), plane_color(0), plane_color(1),
112  plane_color(2), plane_color(3)),
113  PointPainter::Data(tr(0), tr(1), tr(2), plane_color(0), plane_color(1),
114  plane_color(2), plane_color(3)),
115  PointPainter::Data(bl(0), bl(1), bl(2), plane_color(0), plane_color(1),
116  plane_color(2), plane_color(3)));
117 
118  triangle_data->emplace_back(
119  PointPainter::Data(bl(0), bl(1), bl(2), plane_color(0), plane_color(1),
120  plane_color(2), plane_color(3)),
121  PointPainter::Data(tr(0), tr(1), tr(2), plane_color(0), plane_color(1),
122  plane_color(2), plane_color(3)),
123  PointPainter::Data(br(0), br(1), br(2), plane_color(0), plane_color(1),
124  plane_color(2), plane_color(3)));
125  }
126 
127  if (line_data != nullptr) {
128  // Frame around image plane and connecting lines to projection center.
129 
130  line_data->emplace_back(
131  PointPainter::Data(pc(0), pc(1), pc(2), frame_color(0), frame_color(1),
132  frame_color(2), frame_color(3)),
133  PointPainter::Data(tl(0), tl(1), tl(2), frame_color(0), frame_color(1),
134  frame_color(2), frame_color(3)));
135 
136  line_data->emplace_back(
137  PointPainter::Data(pc(0), pc(1), pc(2), frame_color(0), frame_color(1),
138  frame_color(2), frame_color(3)),
139  PointPainter::Data(tr(0), tr(1), tr(2), frame_color(0), frame_color(1),
140  frame_color(2), frame_color(3)));
141 
142  line_data->emplace_back(
143  PointPainter::Data(pc(0), pc(1), pc(2), frame_color(0), frame_color(1),
144  frame_color(2), frame_color(3)),
145  PointPainter::Data(br(0), br(1), br(2), frame_color(0), frame_color(1),
146  frame_color(2), frame_color(3)));
147 
148  line_data->emplace_back(
149  PointPainter::Data(pc(0), pc(1), pc(2), frame_color(0), frame_color(1),
150  frame_color(2), frame_color(3)),
151  PointPainter::Data(bl(0), bl(1), bl(2), frame_color(0), frame_color(1),
152  frame_color(2), frame_color(3)));
153 
154  line_data->emplace_back(
155  PointPainter::Data(tl(0), tl(1), tl(2), frame_color(0), frame_color(1),
156  frame_color(2), frame_color(3)),
157  PointPainter::Data(tr(0), tr(1), tr(2), frame_color(0), frame_color(1),
158  frame_color(2), frame_color(3)));
159 
160  line_data->emplace_back(
161  PointPainter::Data(tr(0), tr(1), tr(2), frame_color(0), frame_color(1),
162  frame_color(2), frame_color(3)),
163  PointPainter::Data(br(0), br(1), br(2), frame_color(0), frame_color(1),
164  frame_color(2), frame_color(3)));
165 
166  line_data->emplace_back(
167  PointPainter::Data(br(0), br(1), br(2), frame_color(0), frame_color(1),
168  frame_color(2), frame_color(3)),
169  PointPainter::Data(bl(0), bl(1), bl(2), frame_color(0), frame_color(1),
170  frame_color(2), frame_color(3)));
171 
172  line_data->emplace_back(
173  PointPainter::Data(bl(0), bl(1), bl(2), frame_color(0), frame_color(1),
174  frame_color(2), frame_color(3)),
175  PointPainter::Data(tl(0), tl(1), tl(2), frame_color(0), frame_color(1),
176  frame_color(2), frame_color(3)));
177  }
178 }
179 
180 } // namespace
181 
183  : QOpenGLWidget(parent),
184  options_(options),
185  point_viewer_widget_(new PointViewerWidget(parent, this, options)),
186  image_viewer_widget_(
187  new DatabaseImageViewerWidget(parent, this, options)),
188  movie_grabber_widget_(new MovieGrabberWidget(parent, this)),
189  mouse_is_pressed_(false),
190  focus_distance_(kInitFocusDistance),
191  selected_image_id_(kInvalidImageId),
192  selected_point3D_id_(kInvalidPoint3DId),
193  coordinate_grid_enabled_(true),
194  near_plane_(kInitNearPlane) {
195  background_color_[0] = 1.0f;
196  background_color_[1] = 1.0f;
197  background_color_[2] = 1.0f;
198 
199  QSurfaceFormat format;
200  format.setDepthBufferSize(24);
201  format.setMajorVersion(3);
202  format.setMinorVersion(2);
203  format.setSamples(4);
204  format.setProfile(QSurfaceFormat::CoreProfile);
205 #ifdef DEBUG
206  format.setOption(QSurfaceFormat::DebugContext);
207 #endif
208  setFormat(format);
209  QSurfaceFormat::setDefaultFormat(format);
210 
213 
214  image_size_ = static_cast<float>(devicePixelRatio() * image_size_);
215  point_size_ = static_cast<float>(devicePixelRatio() * point_size_);
216 }
217 
219  initializeOpenGLFunctions();
220  glEnable(GL_DEPTH_TEST);
221  glEnable(GL_BLEND);
222  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
223  glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
224  SetupPainters();
225  SetupView();
226 }
227 
229  glClearColor(background_color_[0], background_color_[1], background_color_[2],
230  1.0f);
231  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
232 
233  const QMatrix4x4 pmv_matrix = projection_matrix_ * model_view_matrix_;
234 
235  // Model view matrix for center of view
236  QMatrix4x4 model_view_center_matrix = model_view_matrix_;
237  const Eigen::Vector4f rot_center =
238  QMatrixToEigen(model_view_matrix_).inverse() *
239  Eigen::Vector4f(0, 0, -focus_distance_, 1);
240  model_view_center_matrix.translate(rot_center(0), rot_center(1),
241  rot_center(2));
242 
243  // Coordinate system
244  if (coordinate_grid_enabled_) {
245  const QMatrix4x4 pmvc_matrix =
246  projection_matrix_ * model_view_center_matrix;
247  coordinate_axes_painter_.Render(pmv_matrix, width(), height(), 2);
248  coordinate_grid_painter_.Render(pmvc_matrix, width(), height(), 1);
249  }
250 
251  // Points
252  point_painter_.Render(pmv_matrix, point_size_);
253  point_connection_painter_.Render(pmv_matrix, width(), height(), 1);
254 
255  // Images
256  image_line_painter_.Render(pmv_matrix, width(), height(), 1);
257  image_triangle_painter_.Render(pmv_matrix);
258  image_connection_painter_.Render(pmv_matrix, width(), height(), 1);
259 
260  // Movie grabber cameras
261  movie_grabber_path_painter_.Render(pmv_matrix, width(), height(), 1.5);
262  movie_grabber_line_painter_.Render(pmv_matrix, width(), height(), 1);
263  movie_grabber_triangle_painter_.Render(pmv_matrix);
264 }
265 
267  glViewport(0, 0, width, height);
268  ComposeProjectionMatrix();
269  UploadCoordinateGridData();
270 }
271 
273  if (reconstruction == nullptr) {
274  return;
275  }
276 
280 
281  images.clear();
282  for (const image_t image_id : reg_image_ids) {
283  images[image_id] = reconstruction->Image(image_id);
284  }
285 
286 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
287  statusbar_status_label->setText(QString::asprintf(
288  "%d Images - %d Points", static_cast<int>(reg_image_ids.size()),
289  static_cast<int>(points3D.size())));
290 #else
291  statusbar_status_label->setText(QString().sprintf(
292  "%d Images - %d Points", static_cast<int>(reg_image_ids.size()),
293  static_cast<int>(points3D.size())));
294 #endif
295 
296  Upload();
297 }
298 
300  cameras.clear();
301  images.clear();
302  points3D.clear();
303  reg_image_ids.clear();
304  reconstruction = nullptr;
305  Upload();
306 }
307 
309  return options_->render->projection_type;
310 }
311 
313  point_colormap_.reset(colormap);
314 }
315 
317  image_colormap_.reset(colormap);
318 }
319 
321  UploadMovieGrabberData();
322  update();
323 }
324 
326  coordinate_grid_enabled_ = true;
327  update();
328 }
329 
331  coordinate_grid_enabled_ = false;
332  update();
333 }
334 
335 void ModelViewerWidget::ChangeFocusDistance(const float delta) {
336  if (delta == 0.0f) {
337  return;
338  }
339  const float prev_focus_distance = focus_distance_;
340  float diff = delta * ZoomScale() * kFocusSpeed;
341  focus_distance_ -= diff;
342  if (focus_distance_ < kMinFocusDistance) {
343  focus_distance_ = kMinFocusDistance;
344  diff = prev_focus_distance - focus_distance_;
345  } else if (focus_distance_ > kMaxFocusDistance) {
346  focus_distance_ = kMaxFocusDistance;
347  diff = prev_focus_distance - focus_distance_;
348  }
349  const Eigen::Matrix4f vm_mat = QMatrixToEigen(model_view_matrix_).inverse();
350  const Eigen::Vector3f tvec(0, 0, diff);
351  const Eigen::Vector3f tvec_rot = vm_mat.block<3, 3>(0, 0) * tvec;
352  model_view_matrix_.translate(tvec_rot(0), tvec_rot(1), tvec_rot(2));
353  ComposeProjectionMatrix();
354  UploadCoordinateGridData();
355  update();
356 }
357 
358 void ModelViewerWidget::ChangeNearPlane(const float delta) {
359  if (delta == 0.0f) {
360  return;
361  }
362  near_plane_ *= (1.0f + delta / 100.0f * kNearPlaneScaleSpeed);
363  near_plane_ = std::max(kMinNearPlane, std::min(kMaxNearPlane, near_plane_));
364  ComposeProjectionMatrix();
365  UploadCoordinateGridData();
366  update();
367 }
368 
369 void ModelViewerWidget::ChangePointSize(const float delta) {
370  if (delta == 0.0f) {
371  return;
372  }
373  point_size_ *= (1.0f + delta / 100.0f * kPointScaleSpeed);
374  point_size_ = std::max(kMinPointSize, std::min(kMaxPointSize, point_size_));
375  update();
376 }
377 
378 void ModelViewerWidget::RotateView(const float x, const float y,
379  const float prev_x, const float prev_y) {
380  if (x - prev_x == 0 && y - prev_y == 0) {
381  return;
382  }
383 
384  // Rotation according to the Arcball method "ARCBALL: A User Interface for
385  // Specifying Three-Dimensional Orientation Using a Mouse", Ken Shoemake,
386  // University of Pennsylvania, 1992.
387 
388  // Determine Arcball vector on unit sphere.
389  const Eigen::Vector3f u = PositionToArcballVector(x, y);
390  const Eigen::Vector3f v = PositionToArcballVector(prev_x, prev_y);
391 
392  // Angle between vectors.
393  const float angle = 2.0f * std::acos(std::min(1.0f, u.dot(v)));
394 
395  const float kMinAngle = 1e-3f;
396  if (angle > kMinAngle) {
397  const Eigen::Matrix4f vm_mat = QMatrixToEigen(model_view_matrix_).inverse();
398 
399  // Rotation axis.
400  Eigen::Vector3f axis = vm_mat.block<3, 3>(0, 0) * v.cross(u);
401  axis = axis.normalized();
402  // Center of rotation is current focus.
403  const Eigen::Vector4f rot_center =
404  vm_mat * Eigen::Vector4f(0, 0, -focus_distance_, 1);
405  // First shift to rotation center, then rotate and shift back.
406  model_view_matrix_.translate(rot_center(0), rot_center(1), rot_center(2));
407  model_view_matrix_.rotate(RadToDeg(angle), axis(0), axis(1), axis(2));
408  model_view_matrix_.translate(-rot_center(0), -rot_center(1),
409  -rot_center(2));
410  update();
411  }
412 }
413 
414 void ModelViewerWidget::TranslateView(const float x, const float y,
415  const float prev_x, const float prev_y) {
416  if (x - prev_x == 0 && y - prev_y == 0) {
417  return;
418  }
419 
420  Eigen::Vector3f tvec(x - prev_x, prev_y - y, 0.0f);
421 
422  if (options_->render->projection_type ==
423  RenderOptions::ProjectionType::PERSPECTIVE) {
424  tvec *= ZoomScale();
425  } else if (options_->render->projection_type ==
426  RenderOptions::ProjectionType::ORTHOGRAPHIC) {
427  tvec *= 2.0f * OrthographicWindowExtent() / height();
428  }
429 
430  const Eigen::Matrix4f vm_mat = QMatrixToEigen(model_view_matrix_).inverse();
431 
432  const Eigen::Vector3f tvec_rot = vm_mat.block<3, 3>(0, 0) * tvec;
433  model_view_matrix_.translate(tvec_rot(0), tvec_rot(1), tvec_rot(2));
434 
435  update();
436 }
437 
438 void ModelViewerWidget::ChangeCameraSize(const float delta) {
439  if (delta == 0.0f) {
440  return;
441  }
442  image_size_ *= (1.0f + delta / 100.0f * kImageScaleSpeed);
443  image_size_ = std::max(kMinImageSize, std::min(kMaxImageSize, image_size_));
444  UploadImageData();
445  UploadMovieGrabberData();
446  update();
447 }
448 
450  SetupView();
451  Upload();
452 }
453 
455  return model_view_matrix_;
456 }
457 
458 void ModelViewerWidget::SetModelViewMatrix(const QMatrix4x4& matrix) {
459  model_view_matrix_ = matrix;
460  update();
461 }
462 
463 void ModelViewerWidget::SelectObject(const int x, const int y) {
464  makeCurrent();
465 
466  // Ensure that anti-aliasing does not change the colors of objects.
467  glDisable(GL_MULTISAMPLE);
468 
469  glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
470  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
471 
472  // Upload data in selection mode (one color per object).
473  UploadImageData(true);
474  UploadPointData(true);
475 
476  // Render in selection mode, with larger points to improve selection accuracy.
477  const QMatrix4x4 pmv_matrix = projection_matrix_ * model_view_matrix_;
478  image_triangle_painter_.Render(pmv_matrix);
479  point_painter_.Render(pmv_matrix, 2 * point_size_);
480 
481  const int scaled_x = devicePixelRatio() * x;
482  const int scaled_y = devicePixelRatio() * (height() - y - 1);
483 
484  QOpenGLFramebufferObjectFormat fbo_format;
485  fbo_format.setSamples(0);
486  QOpenGLFramebufferObject fbo(1, 1, fbo_format);
487 
488  glBindFramebuffer(GL_READ_FRAMEBUFFER, defaultFramebufferObject());
489  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo.handle());
490  glBlitFramebuffer(scaled_x, scaled_y, scaled_x + 1, scaled_y + 1, 0, 0, 1, 1,
491  GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
492 
493  fbo.bind();
494  std::array<uint8_t, 3> color;
495  glReadPixels(0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, color.data());
496  fbo.release();
497 
498  const size_t index = RGBToIndex(color[0], color[1], color[2]);
499 
500  if (index < selection_buffer_.size()) {
501  const char buffer_type = selection_buffer_[index].second;
502  if (buffer_type == SELECTION_BUFFER_IMAGE_IDX) {
503  selected_image_id_ = static_cast<image_t>(selection_buffer_[index].first);
504  selected_point3D_id_ = kInvalidPoint3DId;
505  ShowImageInfo(selected_image_id_);
506  } else if (buffer_type == SELECTION_BUFFER_POINT_IDX) {
507  selected_image_id_ = kInvalidImageId;
508  selected_point3D_id_ = selection_buffer_[index].first;
509  ShowPointInfo(selection_buffer_[index].first);
510  } else {
511  selected_image_id_ = kInvalidImageId;
512  selected_point3D_id_ = kInvalidPoint3DId;
513  image_viewer_widget_->hide();
514  }
515  } else {
516  selected_image_id_ = kInvalidImageId;
517  selected_point3D_id_ = kInvalidPoint3DId;
518  image_viewer_widget_->hide();
519  }
520 
521  // Re-enable, since temporarily disabled above.
522  glEnable(GL_MULTISAMPLE);
523 
524  selection_buffer_.clear();
525 
526  UploadPointData();
527  UploadImageData();
528  UploadPointConnectionData();
529  UploadImageConnectionData();
530 
531  update();
532 }
533 
534 void ModelViewerWidget::SelectMoviewGrabberView(const size_t view_idx) {
535  selected_movie_grabber_view_ = view_idx;
536  UploadMovieGrabberData();
537  update();
538 }
539 
541  makeCurrent();
542 
544 
545  paintGL();
546 
547  const int scaled_width = static_cast<int>(devicePixelRatio() * width());
548  const int scaled_height = static_cast<int>(devicePixelRatio() * height());
549 
550  QOpenGLFramebufferObjectFormat fbo_format;
551  fbo_format.setSamples(0);
552  QOpenGLFramebufferObject fbo(scaled_width, scaled_height, fbo_format);
553 
554  glBindFramebuffer(GL_READ_FRAMEBUFFER, defaultFramebufferObject());
555  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo.handle());
556  glBlitFramebuffer(0, 0, scaled_width, scaled_height, 0, 0, scaled_width,
557  scaled_height, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
558  GL_NEAREST);
559 
560  fbo.bind();
561  QImage image(scaled_width, scaled_height, QImage::Format_RGB888);
562  glReadPixels(0, 0, scaled_width, scaled_height, GL_RGB, GL_UNSIGNED_BYTE,
563  image.bits());
564  fbo.release();
565 
567 
568  return image.mirrored();
569 }
570 
571 void ModelViewerWidget::GrabMovie() { movie_grabber_widget_->show(); }
572 
574  point_viewer_widget_->Show(point3D_id);
575 }
576 
578  image_viewer_widget_->ShowImageWithId(image_id);
579 }
580 
581 float ModelViewerWidget::PointSize() const { return point_size_; }
582 
583 float ModelViewerWidget::ImageSize() const { return image_size_; }
584 
585 void ModelViewerWidget::SetPointSize(const float point_size) {
586  point_size_ = point_size;
587 }
588 
589 void ModelViewerWidget::SetImageSize(const float image_size) {
590  image_size_ = image_size;
591  UploadImageData();
592 }
593 
594 void ModelViewerWidget::SetBackgroundColor(const float r, const float g,
595  const float b) {
596  background_color_[0] = r;
597  background_color_[1] = g;
598  background_color_[2] = b;
599  update();
600 }
601 
602 void ModelViewerWidget::mousePressEvent(QMouseEvent* event) {
603  if (mouse_press_timer_.isActive()) { // Select objects (2. click)
604  mouse_is_pressed_ = false;
605  mouse_press_timer_.stop();
606  selection_buffer_.clear();
607  SelectObject(event->pos().x(), event->pos().y());
608  } else { // Set timer to remember 1. click
609  mouse_press_timer_.setSingleShot(true);
610  mouse_press_timer_.start(kDoubleClickInterval);
611  mouse_is_pressed_ = true;
612  prev_mouse_pos_ = event->pos();
613  }
614  event->accept();
615 }
616 
617 void ModelViewerWidget::mouseReleaseEvent(QMouseEvent* event) {
618  mouse_is_pressed_ = false;
619  event->accept();
620 }
621 
622 void ModelViewerWidget::mouseMoveEvent(QMouseEvent* event) {
623  if (mouse_is_pressed_) {
624  if (event->buttons() & Qt::RightButton ||
625  (event->buttons() & Qt::LeftButton &&
626  event->modifiers() & Qt::ControlModifier)) {
627  TranslateView(event->pos().x(), event->pos().y(), prev_mouse_pos_.x(),
628  prev_mouse_pos_.y());
629  } else if (event->buttons() & Qt::LeftButton) {
630  RotateView(event->pos().x(), event->pos().y(), prev_mouse_pos_.x(),
631  prev_mouse_pos_.y());
632  }
633  }
634  prev_mouse_pos_ = event->pos();
635  event->accept();
636 }
637 
638 void ModelViewerWidget::wheelEvent(QWheelEvent* event) {
639  double delta = qtCompatWheelEventDelta(event);
640  if (event->modifiers() & Qt::ControlModifier) {
641  ChangePointSize(static_cast<float>(delta));
642  } else if (event->modifiers() & Qt::AltModifier) {
643  ChangeCameraSize(static_cast<float>(delta));
644  } else if (event->modifiers() & Qt::ShiftModifier) {
645  ChangeNearPlane(static_cast<float>(delta));
646  } else {
647  ChangeFocusDistance(static_cast<float>(delta));
648  }
649  event->accept();
650 }
651 
652 void ModelViewerWidget::SetupPainters() {
653  makeCurrent();
654 
655  coordinate_axes_painter_.Setup();
656  coordinate_grid_painter_.Setup();
657 
658  point_painter_.Setup();
659  point_connection_painter_.Setup();
660 
661  image_line_painter_.Setup();
662  image_triangle_painter_.Setup();
663  image_connection_painter_.Setup();
664 
665  movie_grabber_path_painter_.Setup();
666  movie_grabber_line_painter_.Setup();
667  movie_grabber_triangle_painter_.Setup();
668 }
669 
670 void ModelViewerWidget::SetupView() {
671  point_size_ = kInitPointSize;
672  image_size_ = kInitImageSize;
673  focus_distance_ = kInitFocusDistance;
674  model_view_matrix_.setToIdentity();
675  model_view_matrix_.translate(0, 0, -focus_distance_);
676  model_view_matrix_.rotate(225, 1, 0, 0);
677  model_view_matrix_.rotate(-45, 0, 1, 0);
678 }
679 
680 void ModelViewerWidget::Upload() {
681  point_colormap_->Prepare(cameras, images, points3D, reg_image_ids);
682  image_colormap_->Prepare(cameras, images, points3D, reg_image_ids);
683 
684  ComposeProjectionMatrix();
685 
686  UploadPointData();
687  UploadImageData();
688  UploadMovieGrabberData();
689  UploadPointConnectionData();
690  UploadImageConnectionData();
691 
692  update();
693 }
694 
695 void ModelViewerWidget::UploadCoordinateGridData() {
696  makeCurrent();
697 
698  const float scale = ZoomScale();
699 
700  // View center grid.
701  std::vector<LinePainter::Data> grid_data(3);
702 
703  grid_data[0].point1 =
704  PointPainter::Data(-20 * scale, 0, 0, kGridColor(0), kGridColor(1),
705  kGridColor(2), kGridColor(3));
706  grid_data[0].point2 =
707  PointPainter::Data(20 * scale, 0, 0, kGridColor(0), kGridColor(1),
708  kGridColor(2), kGridColor(3));
709 
710  grid_data[1].point1 =
711  PointPainter::Data(0, -20 * scale, 0, kGridColor(0), kGridColor(1),
712  kGridColor(2), kGridColor(3));
713  grid_data[1].point2 =
714  PointPainter::Data(0, 20 * scale, 0, kGridColor(0), kGridColor(1),
715  kGridColor(2), kGridColor(3));
716 
717  grid_data[2].point1 =
718  PointPainter::Data(0, 0, -20 * scale, kGridColor(0), kGridColor(1),
719  kGridColor(2), kGridColor(3));
720  grid_data[2].point2 =
721  PointPainter::Data(0, 0, 20 * scale, kGridColor(0), kGridColor(1),
722  kGridColor(2), kGridColor(3));
723 
724  coordinate_grid_painter_.Upload(grid_data);
725 
726  // Coordinate axes.
727  std::vector<LinePainter::Data> axes_data(3);
728 
729  axes_data[0].point1 = PointPainter::Data(
730  0, 0, 0, kXAxisColor(0), kXAxisColor(1), kXAxisColor(2), kXAxisColor(3));
731  axes_data[0].point2 =
732  PointPainter::Data(50 * scale, 0, 0, kXAxisColor(0), kXAxisColor(1),
733  kXAxisColor(2), kXAxisColor(3));
734 
735  axes_data[1].point1 = PointPainter::Data(
736  0, 0, 0, kYAxisColor(0), kYAxisColor(1), kYAxisColor(2), kYAxisColor(3));
737  axes_data[1].point2 =
738  PointPainter::Data(0, 50 * scale, 0, kYAxisColor(0), kYAxisColor(1),
739  kYAxisColor(2), kYAxisColor(3));
740 
741  axes_data[2].point1 = PointPainter::Data(
742  0, 0, 0, kZAxisColor(0), kZAxisColor(1), kZAxisColor(2), kZAxisColor(3));
743  axes_data[2].point2 =
744  PointPainter::Data(0, 0, 50 * scale, kZAxisColor(0), kZAxisColor(1),
745  kZAxisColor(2), kZAxisColor(3));
746 
747  coordinate_axes_painter_.Upload(axes_data);
748 }
749 
750 void ModelViewerWidget::UploadPointData(const bool selection_mode) {
751  makeCurrent();
752 
753  std::vector<PointPainter::Data> data;
754 
755  // Assume we want to display the majority of points
756  data.reserve(points3D.size());
757 
758  const size_t min_track_len =
759  static_cast<size_t>(options_->render->min_track_len);
760 
761  if (selected_image_id_ == kInvalidImageId &&
762  images.count(selected_image_id_) == 0) {
763  for (const auto& point3D : points3D) {
764  if (point3D.second.Error() <= options_->render->max_error &&
765  point3D.second.Track().Length() >= min_track_len) {
766  PointPainter::Data painter_point;
767 
768  painter_point.x = static_cast<float>(point3D.second.XYZ(0));
769  painter_point.y = static_cast<float>(point3D.second.XYZ(1));
770  painter_point.z = static_cast<float>(point3D.second.XYZ(2));
771 
772  Eigen::Vector4f color;
773  if (selection_mode) {
774  const size_t index = selection_buffer_.size();
775  selection_buffer_.push_back(
777  color = IndexToRGB(index);
778 
779  } else if (point3D.first == selected_point3D_id_) {
781  } else {
782  color = point_colormap_->ComputeColor(point3D.first, point3D.second);
783  }
784 
785  painter_point.r = color(0);
786  painter_point.g = color(1);
787  painter_point.b = color(2);
788  painter_point.a = color(3);
789 
790  data.push_back(painter_point);
791  }
792  }
793  } else { // Image selected
794  const auto& selected_image = images[selected_image_id_];
795  for (const auto& point3D : points3D) {
796  if (point3D.second.Error() <= options_->render->max_error &&
797  point3D.second.Track().Length() >= min_track_len) {
798  PointPainter::Data painter_point;
799 
800  painter_point.x = static_cast<float>(point3D.second.XYZ(0));
801  painter_point.y = static_cast<float>(point3D.second.XYZ(1));
802  painter_point.z = static_cast<float>(point3D.second.XYZ(2));
803 
804  Eigen::Vector4f color;
805  if (selection_mode) {
806  const size_t index = selection_buffer_.size();
807  selection_buffer_.push_back(
809  color = IndexToRGB(index);
810  } else if (selected_image.HasPoint3D(point3D.first)) {
812  } else if (point3D.first == selected_point3D_id_) {
814  } else {
815  color = point_colormap_->ComputeColor(point3D.first, point3D.second);
816  }
817 
818  painter_point.r = color(0);
819  painter_point.g = color(1);
820  painter_point.b = color(2);
821  painter_point.a = color(3);
822 
823  data.push_back(painter_point);
824  }
825  }
826  }
827 
828  point_painter_.Upload(data);
829 }
830 
831 void ModelViewerWidget::UploadPointConnectionData() {
832  makeCurrent();
833 
834  std::vector<LinePainter::Data> line_data;
835 
836  if (selected_point3D_id_ == kInvalidPoint3DId) {
837  // No point selected, so upload empty data
838  point_connection_painter_.Upload(line_data);
839  return;
840  }
841 
842  const auto& point3D = points3D[selected_point3D_id_];
843 
844  // 3D point position.
845  LinePainter::Data line;
846  line.point1 = PointPainter::Data(
847  static_cast<float>(point3D.XYZ(0)), static_cast<float>(point3D.XYZ(1)),
848  static_cast<float>(point3D.XYZ(2)), kSelectedPointColor(0),
850 
851  // All images in which 3D point is observed.
852  for (const auto& track_el : point3D.Track().Elements()) {
853  const Image& conn_image = images[track_el.image_id];
854  const Eigen::Vector3f conn_proj_center =
855  conn_image.ProjectionCenter().cast<float>();
856  line.point2 = PointPainter::Data(
857  conn_proj_center(0), conn_proj_center(1), conn_proj_center(2),
859  0.8f);
860  line_data.push_back(line);
861  }
862 
863  point_connection_painter_.Upload(line_data);
864 }
865 
866 void ModelViewerWidget::UploadImageData(const bool selection_mode) {
867  makeCurrent();
868 
869  std::vector<LinePainter::Data> line_data;
870  line_data.reserve(8 * reg_image_ids.size());
871 
872  std::vector<TrianglePainter::Data> triangle_data;
873  triangle_data.reserve(2 * reg_image_ids.size());
874 
875  for (const image_t image_id : reg_image_ids) {
876  const Image& image = images[image_id];
877  const Camera& camera = cameras[image.CameraId()];
878 
879  Eigen::Vector4f plane_color;
880  Eigen::Vector4f frame_color;
881  if (selection_mode) {
882  const size_t index = selection_buffer_.size();
883  selection_buffer_.push_back(
885  plane_color = frame_color = IndexToRGB(index);
886  } else {
887  if (image_id == selected_image_id_) {
888  plane_color = kSelectedImagePlaneColor;
889  frame_color = kSelectedImageFrameColor;
890  } else {
891  image_colormap_->ComputeColor(image, &plane_color, &frame_color);
892  }
893  }
894 
895  // Lines are not colored with the indexed color in selection mode, so do not
896  // show them, so they do not block the selection process
897  BuildImageModel(image, camera, image_size_, plane_color, frame_color,
898  &triangle_data, selection_mode ? nullptr : &line_data);
899  }
900 
901  image_line_painter_.Upload(line_data);
902  image_triangle_painter_.Upload(triangle_data);
903 }
904 
905 void ModelViewerWidget::UploadImageConnectionData() {
906  makeCurrent();
907 
908  std::vector<LinePainter::Data> line_data;
909  std::vector<image_t> image_ids;
910 
911  if (selected_image_id_ != kInvalidImageId) {
912  // Show connections to selected images
913  image_ids.push_back(selected_image_id_);
914  } else if (options_->render->image_connections) {
915  // Show all connections
916  image_ids = reg_image_ids;
917  } else { // Disabled, so upload empty data
918  image_connection_painter_.Upload(line_data);
919  return;
920  }
921 
922  for (const image_t image_id : image_ids) {
923  const Image& image = images.at(image_id);
924 
925  const Eigen::Vector3f proj_center = image.ProjectionCenter().cast<float>();
926 
927  // Collect all connected images
928  std::unordered_set<image_t> conn_image_ids;
929 
930  for (const Point2D& point2D : image.Points2D()) {
931  if (point2D.HasPoint3D()) {
932  const Point3D& point3D = points3D[point2D.Point3DId()];
933  for (const auto& track_elem : point3D.Track().Elements()) {
934  conn_image_ids.insert(track_elem.image_id);
935  }
936  }
937  }
938 
939  // Selected image in the center.
940  LinePainter::Data line;
941  line.point1 = PointPainter::Data(
942  proj_center(0), proj_center(1), proj_center(2),
944  kSelectedImageFrameColor(2), 0.8f);
945 
946  // All connected images to the selected image.
947  for (const image_t conn_image_id : conn_image_ids) {
948  const Image& conn_image = images[conn_image_id];
949  const Eigen::Vector3f conn_proj_center =
950  conn_image.ProjectionCenter().cast<float>();
951  line.point2 = PointPainter::Data(
952  conn_proj_center(0), conn_proj_center(1), conn_proj_center(2),
954  kSelectedImageFrameColor(2), 0.8f);
955  line_data.push_back(line);
956  }
957  }
958 
959  image_connection_painter_.Upload(line_data);
960 }
961 
962 void ModelViewerWidget::UploadMovieGrabberData() {
963  makeCurrent();
964 
965  std::vector<LinePainter::Data> path_data;
966  path_data.reserve(movie_grabber_widget_->views.size());
967 
968  std::vector<LinePainter::Data> line_data;
969  line_data.reserve(4 * movie_grabber_widget_->views.size());
970 
971  std::vector<TrianglePainter::Data> triangle_data;
972  triangle_data.reserve(2 * movie_grabber_widget_->views.size());
973 
974  if (movie_grabber_widget_->views.size() > 0) {
975  const Image& image0 = movie_grabber_widget_->views[0];
976  Eigen::Vector3f prev_proj_center = image0.ProjectionCenter().cast<float>();
977 
978  for (size_t i = 1; i < movie_grabber_widget_->views.size(); ++i) {
979  const Image& image = movie_grabber_widget_->views[i];
980  const Eigen::Vector3f curr_proj_center =
981  image.ProjectionCenter().cast<float>();
982  LinePainter::Data path;
983  path.point1 = PointPainter::Data(
984  prev_proj_center(0), prev_proj_center(1), prev_proj_center(2),
987  path.point2 = PointPainter::Data(
988  curr_proj_center(0), curr_proj_center(1), curr_proj_center(2),
991  path_data.push_back(path);
992  prev_proj_center = curr_proj_center;
993  }
994 
995  // Setup dummy camera with same settings as current OpenGL viewpoint.
996  const float kDefaultImageWdith = 2048.0f;
997  const float kDefaultImageHeight = 1536.0f;
998  const float focal_length =
999  -2.0f * std::tan(DegToRad(kFieldOfView) / 2.0f) * kDefaultImageWdith;
1000  Camera camera;
1001  camera.InitializeWithId(SimplePinholeCameraModel::model_id, focal_length,
1002  kDefaultImageWdith, kDefaultImageHeight);
1003 
1004  // Build all camera models
1005  for (size_t i = 0; i < movie_grabber_widget_->views.size(); ++i) {
1006  const Image& image = movie_grabber_widget_->views[i];
1007  Eigen::Vector4f plane_color;
1008  Eigen::Vector4f frame_color;
1009  if (i == selected_movie_grabber_view_) {
1010  plane_color = kSelectedImagePlaneColor;
1011  frame_color = kSelectedImageFrameColor;
1012  } else {
1013  plane_color = kMovieGrabberImagePlaneColor;
1014  frame_color = kMovieGrabberImageFrameColor;
1015  }
1016 
1017  BuildImageModel(image, camera, image_size_, plane_color, frame_color,
1018  &triangle_data, &line_data);
1019  }
1020  }
1021 
1022  movie_grabber_path_painter_.Upload(path_data);
1023  movie_grabber_line_painter_.Upload(line_data);
1024  movie_grabber_triangle_painter_.Upload(triangle_data);
1025 }
1026 
1027 void ModelViewerWidget::ComposeProjectionMatrix() {
1028  projection_matrix_.setToIdentity();
1029  if (options_->render->projection_type ==
1030  RenderOptions::ProjectionType::PERSPECTIVE) {
1031  projection_matrix_.perspective(kFieldOfView, AspectRatio(), near_plane_,
1032  kFarPlane);
1033  } else if (options_->render->projection_type ==
1034  RenderOptions::ProjectionType::ORTHOGRAPHIC) {
1035  const float extent = OrthographicWindowExtent();
1036  projection_matrix_.ortho(-AspectRatio() * extent, AspectRatio() * extent,
1037  -extent, extent, near_plane_, kFarPlane);
1038  }
1039 }
1040 
1041 float ModelViewerWidget::ZoomScale() const {
1042  // "Constant" scale factor w.r.t. zoom-level.
1043  return 2.0f * std::tan(static_cast<float>(DegToRad(kFieldOfView)) / 2.0f) *
1044  std::abs(focus_distance_) / height();
1045 }
1046 
1047 float ModelViewerWidget::AspectRatio() const {
1048  return static_cast<float>(width()) / static_cast<float>(height());
1049 }
1050 
1051 float ModelViewerWidget::OrthographicWindowExtent() const {
1052  return std::tan(DegToRad(kFieldOfView) / 2.0f) * focus_distance_;
1053 }
1054 
1055 Eigen::Vector3f ModelViewerWidget::PositionToArcballVector(
1056  const float x, const float y) const {
1057  Eigen::Vector3f vec(2.0f * x / width() - 1, 1 - 2.0f * y / height(), 0.0f);
1058  const float norm2 = vec.squaredNorm();
1059  if (norm2 <= 1.0f) {
1060  vec.z() = std::sqrt(1.0f - norm2);
1061  } else {
1062  vec = vec.normalized();
1063  }
1064  return vec;
1065 }
1066 
1067 } // namespace colmap
MouseEvent event
std::shared_ptr< core::Tensor > image
filament::Texture::InternalFormat format
int width
int height
math::float4 color
double qtCompatWheelEventDelta(const QWheelEvent *event) noexcept
Definition: QtCompat.h:751
void ShowImageWithId(const image_t image_id)
void Render(const QMatrix4x4 &pmv_matrix, const int width, const int height, const float line_width)
void Upload(const std::vector< LinePainter::Data > &data)
Definition: line_painter.cc:70
void ChangeFocusDistance(const float delta)
void ShowPointInfo(const point3D_t point3D_id)
void SetBackgroundColor(const float r, const float g, const float b)
std::unordered_map< point3D_t, Point3D > points3D
std::unordered_map< camera_t, Camera > cameras
ModelViewerWidget(QWidget *parent, OptionManager *options)
void SetPointColormap(PointColormapBase *colormap)
void TranslateView(const float x, const float y, const float prev_x, const float prev_y)
QMatrix4x4 ModelViewMatrix() const
void SetImageColormap(ImageColormapBase *colormap)
void SetImageSize(const float image_size)
void SetPointSize(const float point_size)
void ShowImageInfo(const image_t image_id)
void ChangeNearPlane(const float delta)
void SelectMoviewGrabberView(const size_t view_idx)
std::vector< image_t > reg_image_ids
void SelectObject(const int x, const int y)
void resizeGL(int width, int height) override
void RotateView(const float x, const float y, const float prev_x, const float prev_y)
void ChangePointSize(const float delta)
void SetModelViewMatrix(const QMatrix4x4 &matrix)
std::unordered_map< image_t, Image > images
void ChangeCameraSize(const float delta)
std::shared_ptr< RenderOptions > render
void Upload(const std::vector< PointPainter::Data > &data)
void Render(const QMatrix4x4 &pmv_matrix, const float point_size)
void Show(const point3D_t point3D_id)
const class Image & Image(const image_t image_id) const
const std::unordered_map< camera_t, class Camera > & Cameras() const
const std::unordered_map< point3D_t, class Point3D > & Points3D() const
const std::vector< image_t > & RegImageIds() const
void Upload(const std::vector< TrianglePainter::Data > &data)
void Render(const QMatrix4x4 &pmv_matrix)
GraphType data
Definition: graph_cut.cc:138
const Eigen::Vector4f kXAxisColor(0.9f, 0.0f, 0.0f, 0.5f)
const Eigen::Vector4f kMovieGrabberImagePlaneColor(0.0f, 1.0f, 1.0f, 0.6f)
#define SELECTION_BUFFER_IMAGE_IDX
const Eigen::Vector4f kMovieGrabberImageFrameColor(0.0f, 0.8f, 0.8f, 1.0f)
const Eigen::Vector4f kGridColor(0.2f, 0.2f, 0.2f, 0.6f)
const Eigen::Vector4f kZAxisColor(0.0f, 0.0f, 0.9f, 0.5f)
const Eigen::Vector4f kSelectedImageFrameColor(0.8f, 0.0f, 0.8f, 1.0f)
const Eigen::Vector4f kYAxisColor(0.0f, 0.9f, 0.0f, 0.5f)
const Eigen::Vector4f kSelectedImagePlaneColor(1.0f, 0.0f, 1.0f, 0.6f)
#define SELECTION_BUFFER_POINT_IDX
const Eigen::Vector4f kSelectedPointColor(0.0f, 1.0f, 0.0f, 1.0f)
normal_z y
normal_z x
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
Definition: SlabTraits.h:49
static const std::string path
Definition: PointCloud.cpp:59
float RadToDeg(const float rad)
Definition: math.h:180
uint64_t point3D_t
Definition: types.h:72
Eigen::Matrix4f QMatrixToEigen(const QMatrix4x4 &matrix)
Definition: qt_utils.cc:39
const point3D_t kInvalidPoint3DId
Definition: types.h:80
uint32_t image_t
Definition: types.h:61
const image_t kInvalidImageId
Definition: types.h:76
float DegToRad(const float deg)
Definition: math.h:171