ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ModelViewerWidget.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 "ModelViewerWidget.h"
9 
10 #include "ReconstructionWidget.h"
11 #include "ui/qt_utils.h"
12 #include "ui/render_options.h"
13 
14 // CV_DB_LIB
15 #include <ecvCameraSensor.h>
16 #include <ecvDisplayTools.h>
17 #include <ecvHObjectCaster.h>
18 #include <ecvPointCloud.h>
19 
20 // LOCAL
21 #include "MainWindow.h"
22 #include "ecvConsole.h"
23 #include "ecvDBRoot.h"
24 
25 #define SELECTION_BUFFER_IMAGE_IDX 0
26 #define SELECTION_BUFFER_POINT_IDX 1
27 
28 const Eigen::Vector4d kSelectedPointColor(0.0, 1.0, 0.0, 1.0);
29 
30 const Eigen::Vector4d kSelectedImagePlaneColor(1.0, 0.0, 1.0, 0.6);
31 const Eigen::Vector4d kSelectedImageFrameColor(0.8, 0.0, 0.8, 1.0);
32 
33 const Eigen::Vector4d kMovieGrabberImagePlaneColor(0.0, 1.0, 1.0, 0.6);
34 const Eigen::Vector4d kMovieGrabberImageFrameColor(0.0, 0.8, 0.8, 1.0);
35 
36 namespace cloudViewer {
37 namespace {
38 
39 // Generate unique index from RGB color in the range [0, 256^3].
40 inline size_t RGBToIndex(const uint8_t r, const uint8_t g, const uint8_t b) {
41  return static_cast<size_t>(r) + static_cast<size_t>(g) * 256 +
42  static_cast<size_t>(b) * 65536;
43 }
44 
45 // Derive color from unique index, generated by `RGBToIndex`.
46 inline Eigen::Vector4f IndexToRGB(const size_t index) {
47  Eigen::Vector4f color;
48  color(0) = ((index & 0x000000FF) >> 0) / 255.0f;
49  color(1) = ((index & 0x0000FF00) >> 8) / 255.0f;
50  color(2) = ((index & 0x00FF0000) >> 16) / 255.0f;
51  color(3) = 1.0f;
52  return color;
53 }
54 
55 void BuildImageModel(ccCameraSensor* sensor,
56  const colmap::Image& image,
57  const colmap::Camera& camera,
58  const float image_size,
59  const Eigen::Vector4d& plane_color,
60  const Eigen::Vector4d& frame_color) {
61  // temp information
62  const float kBaseCameraWidth = 1024.0f;
63  const float image_width = image_size * camera.Width() / kBaseCameraWidth;
64  const float image_height = image_width *
65  static_cast<float>(camera.Height()) /
66  static_cast<float>(camera.Width());
67  const float image_extent = std::max(image_width, image_height);
68  const float camera_extent = std::max(camera.Width(), camera.Height());
69  const float camera_extent_world =
70  static_cast<float>(camera.ImageToWorldThreshold(camera_extent));
71  const float focal_length = 2.0f * image_extent / camera_extent_world;
72 
73  // set color
75  Eigen::Vector3d(plane_color(0), plane_color(1), plane_color(2)));
76  sensor->setPlaneColor(planeColor);
78  Eigen::Vector3d(frame_color(0), frame_color(1), frame_color(2)));
79  sensor->setFrameColor(frameColor);
80  int retinaScale = ecvDisplayTools::GetDevicePixelRatio();
81  sensor->setGraphicScale(-PC_ONE / retinaScale);
82 
83  // init camera intrinsic parameters
85  iParams.zFar_mm = 1e5f;
86  iParams.zNear_mm = 1e-3f;
87  float pixelSize_mm = image_size; // pixel size (real distance in meter)
88  iParams.pixelSize_mm[0] = pixelSize_mm;
89  iParams.pixelSize_mm[1] = pixelSize_mm;
90  iParams.vertFocal_pix =
91  ccCameraSensor::ConvertFocalMMToPix(focal_length, pixelSize_mm);
92  iParams.vFOV_rad = cloudViewer::DegreesToRadians(45.0f);
93  iParams.arrayWidth = static_cast<int>(camera.Width());
94  iParams.arrayHeight = static_cast<int>(camera.Height());
95  iParams.principal_point[0] = static_cast<float>(camera.PrincipalPointX());
96  iParams.principal_point[1] = static_cast<float>(camera.PrincipalPointY());
97  sensor->setIntrinsicParameters(iParams);
98 
99  // init camera rigid transformation parameters
100  const Eigen::Matrix<float, 3, 4> proj_matrix =
101  image.InverseProjectionMatrix().cast<float>();
102  Eigen::Matrix3f rotation = proj_matrix.leftCols<3>();
103  Eigen::Vector3f translation = proj_matrix.rightCols<1>();
105  rot.setTranslation(translation.data());
106  sensor->setRigidTransformation(rot);
107 }
108 
109 } // namespace
110 
111 using namespace colmap;
113  OptionManager* options,
114  MainWindow* app)
115  : QWidget(parent),
116  statusbar_status_label(nullptr),
117  options_(options),
118  point_viewer_widget_(new PointViewerWidget(parent, this, options)),
119  image_viewer_widget_(
120  new DatabaseImageViewerWidget(parent, this, options)),
121  movie_grabber_widget_(new MovieGrabberWidget(parent, this)),
122  focus_distance_(kInitFocusDistance),
123  selected_image_id_(kInvalidImageId),
124  selected_point3D_id_(kInvalidPoint3DId),
125  app_(app),
126  cloud_sparse_(nullptr),
127  main_sensors_(nullptr) {
128  SetupView();
131 
134 
135  image_size_ = static_cast<float>(ecvDisplayTools::GetDevicePixelRatio() *
136  image_size_);
137  point_size_ = static_cast<float>(ecvDisplayTools::GetDevicePixelRatio() *
138  point_size_);
139 
140  point_line_data_.clear();
141  image_line_data_.clear();
142  sensors_.clear();
143  movie_grabber_sensors_.clear();
144 }
145 
147  if (movie_grabber_widget_) {
148  movie_grabber_widget_->views.clear();
149  }
150 
152 
153  if (cloud_sparse_) {
154  delete cloud_sparse_;
155  cloud_sparse_ = nullptr;
156  }
157 
158  if (main_sensors_) {
159  delete main_sensors_;
160  main_sensors_ = nullptr;
161  }
162 }
163 
164 QWidget* ModelViewerWidget::getMainWindow() { return app_; }
165 
167  if (reconstruction == nullptr) {
168  return;
169  }
170 
174 
175  images.clear();
176  for (const image_t image_id : reg_image_ids) {
177  images[image_id] = reconstruction->Image(image_id);
178  }
179 
181  statusbar_status_label->setText(QString().asprintf(
182  "%d Images - %d Points", static_cast<int>(reg_image_ids.size()),
183  static_cast<int>(points3D.size())));
184  }
185 
186  Upload();
187 }
188 
190  cameras.clear();
191  images.clear();
192  points3D.clear();
193  reg_image_ids.clear();
194  reconstruction = nullptr;
195  selected_image_id_ = kInvalidImageId;
196  selected_point3D_id_ = kInvalidPoint3DId;
197  Upload();
198 }
199 
202  options_->render->projection_type =
203  colmap::RenderOptions::ProjectionType::PERSPECTIVE;
204  } else {
205  options_->render->projection_type =
206  colmap::RenderOptions::ProjectionType::ORTHOGRAPHIC;
207  }
208  return options_->render->projection_type;
209 }
210 
212  point_colormap_.reset(colormap);
213 }
214 
216  image_colormap_.reset(colormap);
217 }
218 
220  StartRender();
221  UploadMovieGrabberData();
222  EndRender(false);
223  update();
224 }
225 
227  focus_distance_ =
228  static_cast<float>(ecvDisplayTools::GetCameraFocalDistance());
229  // "Constant" scale factor w.r.t. zoom-level.
230  return 2.0f *
231  std::tan(static_cast<float>(DegToRad(ecvDisplayTools::GetFov())) /
232  2.0f) *
233  std::abs(focus_distance_) / ecvDisplayTools::GlHeight();
234 }
235 
237  return static_cast<float>(ecvDisplayTools::GlWidth()) /
238  static_cast<float>(ecvDisplayTools::GlHeight());
239 }
240 
241 void ModelViewerWidget::ChangeFocusDistance(const float delta) {
242  if (delta == 0.0f) {
243  return;
244  }
245 
246  focus_distance_ =
247  static_cast<float>(ecvDisplayTools::GetCameraFocalDistance());
248  const float prev_focus_distance = focus_distance_;
249  float diff = delta * ZoomScale() * kFocusSpeed;
250  focus_distance_ -= diff;
251  if (focus_distance_ < kMinFocusDistance) {
252  focus_distance_ = kMinFocusDistance;
253  diff = prev_focus_distance - focus_distance_;
254  } else if (focus_distance_ > kMaxFocusDistance) {
255  focus_distance_ = kMaxFocusDistance;
256  diff = prev_focus_distance - focus_distance_;
257  }
259  static_cast<double>(focus_distance_));
261 }
262 
263 void ModelViewerWidget::ChangePointSize(const float delta) {
264  if (delta == 0.0f) {
265  return;
266  }
267  point_size_ *= (1.0f + delta / 100.0f * kPointScaleSpeed);
268  point_size_ = std::max(kMinPointSize, std::min(kMaxPointSize, point_size_));
269 
270  StartRender();
271  if (cloud_sparse_) {
272  cloud_sparse_->setPointSize(static_cast<unsigned>(point_size_));
273  }
274  EndRender(false);
275  update();
276 }
277 
278 void ModelViewerWidget::ChangeCameraSize(const float delta) {
279  if (delta == 0.0f) {
280  return;
281  }
282  image_size_ *= (1.0f + delta / 100.0f * kImageScaleSpeed);
283  image_size_ = std::max(kMinImageSize, std::min(kMaxImageSize, image_size_));
284  StartRender();
285  UploadImageData();
286  UploadMovieGrabberData();
287  EndRender(false);
288  update();
289 }
290 
292  SetupView();
293  Upload();
294 }
295 
297  ccGLMatrixd mat;
299  return mat;
300 }
301 
303  unsigned subEntityID,
304  int x,
305  int y,
306  const CCVector3& P) {
307  Q_UNUSED(x);
308  Q_UNUSED(y);
309 
310  if (!entity || reg_image_ids.empty() || points3D.empty()) {
311  return;
312  }
313 
314  ccHObject* obj = entity;
315  if (!main_sensors_ || !cloud_sparse_) {
316  return;
317  } else {
318  if (obj->isKindOf(CV_TYPES::CAMERA_SENSOR)) {
319  if (!main_sensors_->find(subEntityID)) {
320  return;
321  }
322  } else if (obj->isKindOf(CV_TYPES::POINT_CLOUD)) {
323  if (cloud_sparse_->getUniqueID() != obj->getUniqueID()) {
324  return;
325  }
326  }
327  }
328 
329  // Upload data in selection mode (one color per object).
330  UploadImageData(true);
331  UploadPointData(true);
332 
333  std::size_t selected_index = 0;
334  if (obj->isKindOf(CV_TYPES::CAMERA_SENSOR)) {
336  ecvColor::Rgb plane_color = camera->getPlaneColor();
337  selected_index =
338  RGBToIndex(plane_color.r, plane_color.g, plane_color.b);
339  } else if (obj->isKindOf(CV_TYPES::POINT_CLOUD) &&
340  obj->getUniqueID() == cloud_sparse_->getUniqueID()) {
341  selected_index = static_cast<std::size_t>(subEntityID);
342  if (selected_index >= cloud_sparse_->size()) {
344  "[ModelViewerWidget::SelectObject] Invalid picking point "
345  "index!");
346  return;
347  }
348 
349  const CCVector3* refP = cloud_sparse_->getPointPtr(selected_index);
350  if (GreaterThanEpsilon((P - *refP).norm())) {
351  return;
352  }
353 
354  ecvColor::Rgb& pointColor =
355  cloud_sparse_->getPointColorPtr(selected_index);
356  selected_index = RGBToIndex(pointColor.r, pointColor.g, pointColor.b);
357 
358  } else {
359  return;
360  }
361 
362  colmap::image_t last_selected_image_id = selected_image_id_;
363 
364  if (selected_index < selection_buffer_.size()) {
365  const char buffer_type = selection_buffer_[selected_index].second;
366  if (buffer_type == SELECTION_BUFFER_IMAGE_IDX) {
367  selected_image_id_ = static_cast<image_t>(
368  selection_buffer_[selected_index].first);
369  selected_point3D_id_ = kInvalidPoint3DId;
370  ShowImageInfo(selected_image_id_);
371  } else if (buffer_type == SELECTION_BUFFER_POINT_IDX) {
372  selected_image_id_ = kInvalidImageId;
373  selected_point3D_id_ = selection_buffer_[selected_index].first;
374  ShowPointInfo(selection_buffer_[selected_index].first);
375  } else {
376  selected_image_id_ = kInvalidImageId;
377  selected_point3D_id_ = kInvalidPoint3DId;
378  image_viewer_widget_->hide();
379  }
380  } else {
381  selected_image_id_ = kInvalidImageId;
382  selected_point3D_id_ = kInvalidPoint3DId;
383  image_viewer_widget_->hide();
384  }
385 
386  selection_buffer_.clear();
387 
388  StartRender();
389  UploadPointData();
390  UploadImageData();
391  UploadPointConnectionData();
392  UploadImageConnectionData();
393  EndRender(false);
394 
395  if (app_) {
396  if (selected_image_id_ == kInvalidImageId) {
397  app_->db()->expandElement(main_sensors_, false);
398  ccHObject* obj = getSelectedCamera(last_selected_image_id);
399  if (obj) {
400  app_->db()->unselectEntity(obj);
401  }
402  } else {
403  app_->db()->expandElement(main_sensors_, true);
404  ccHObject* obj = getSelectedCamera(selected_image_id_);
405  if (obj) {
406  app_->db()->selectEntity(obj);
407  }
408  }
409  }
410 
411  update();
412 }
413 
414 void ModelViewerWidget::SelectMoviewGrabberView(const size_t view_idx) {
415  selected_movie_grabber_view_ = view_idx;
416  StartRender();
417  UploadMovieGrabberData();
418  EndRender(false);
419  update();
420 }
421 
423  // render to image
424  return ecvDisplayTools::RenderToImage(1, false, true, 0);
425 }
426 
428  if (GetProjectionType() !=
429  colmap::RenderOptions::ProjectionType::PERSPECTIVE) {
430  const int reply = QMessageBox::question(
431  this, tr("Warning"),
432  tr("You must use perspective projection, "
433  "do you want to switch to perspective mode now?"),
434  QMessageBox::Yes | QMessageBox::No);
435  if (reply == QMessageBox::Yes) {
436  if (app_) {
437  app_->doActionPerspectiveProjection();
438  }
439  } else {
440  return;
441  }
442  }
443 
444  movie_grabber_widget_->show();
445 }
446 
448  point_viewer_widget_->Show(point3D_id);
449 }
450 
452  image_viewer_widget_->ShowImageWithId(image_id);
453 }
454 
456  if (app_) {
457  app_->doActionPerspectiveProjection();
458  }
459 }
460 
462  if (app_) {
463  app_->doActionOrthogonalProjection();
464  }
465 }
466 
467 float ModelViewerWidget::PointSize() const { return point_size_; }
468 
469 float ModelViewerWidget::ImageSize() const { return image_size_; }
470 
471 void ModelViewerWidget::SetPointSize(const float point_size,
472  bool autoUpdate /* = true*/) {
473  point_size_ = point_size;
474 
475  if (autoUpdate) {
476  StartRender();
477  }
478 
479  if (cloud_sparse_) {
480  cloud_sparse_->setPointSize(static_cast<unsigned>(point_size_));
481  }
482 
483  if (autoUpdate) {
484  EndRender(false);
485  update();
486  }
487 }
488 
489 void ModelViewerWidget::SetImageSize(const float image_size,
490  bool autoUpdate /* = true*/) {
491  image_size_ = image_size;
492 
493  if (autoUpdate) {
494  StartRender();
495  }
496 
497  UploadImageData();
498 
499  if (autoUpdate) {
500  EndRender(false);
501  update();
502  }
503 }
504 
506  const float g,
507  const float b) {
509  params.backgroundCol =
510  ecvColor::Rgb::FromEigen(Eigen::Vector3f(r, g, b).cast<double>());
512  ecvDisplayTools::RedrawDisplay(true, false);
513 }
514 
515 void ModelViewerWidget::SetupView() {
516  point_size_ = kInitPointSize;
517  image_size_ = kInitImageSize;
518  focus_distance_ = kInitFocusDistance;
519 }
520 
521 void ModelViewerWidget::Upload() {
522  point_colormap_->Prepare(cameras, images, points3D, reg_image_ids);
523  image_colormap_->Prepare(cameras, images, points3D, reg_image_ids);
524 
525  StartRender();
526  UploadPointData();
527  UploadImageData();
528  UploadMovieGrabberData();
529  UploadPointConnectionData();
530  UploadImageConnectionData();
531  EndRender();
532 
533  update();
534 }
535 
537  // render before
539  bbox_.clear();
540 }
541 
542 void ModelViewerWidget::EndRender(bool autoZoom /* = true*/) {
543  if (autoZoom && bbox_.isValid()) {
545  } else {
547  }
548 }
549 
550 void ModelViewerWidget::UploadPointData(const bool selection_mode) {
551  if (!cloud_sparse_) {
552  cloud_sparse_ = new ccPointCloud("sparseCloud");
553  if (!cloud_sparse_) {
555  "[ModelViewerWidget::UploadPointData] Not enough memory!");
556  return;
557  }
558  }
559 
560  cloud_sparse_->clear();
561 
562  if (points3D.size() == 0) {
563  resetPointCloud(cloud_sparse_);
564  return;
565  }
566 
567  MainWindow::ccHObjectContext objContext;
568  if (app_ &&
569  app_->db()->find(static_cast<int>(cloud_sparse_->getUniqueID()))) {
570  objContext = app_->removeObjectTemporarilyFromDBTree(cloud_sparse_);
571  }
572 
573  // Assume we want to display the majority of points
574  if (!cloud_sparse_->reserve(static_cast<unsigned>(points3D.size()))) {
575  return;
576  } else {
577  cloud_sparse_->reserveTheRGBTable();
578  cloud_sparse_->showColors(true);
579  }
580 
581  const size_t min_track_len =
582  static_cast<size_t>(options_->render->min_track_len);
583 
584  if (selected_image_id_ == kInvalidImageId &&
585  images.count(selected_image_id_) == 0) {
586  for (const auto& point3D : points3D) {
587  if (point3D.second.Error() <= options_->render->max_error &&
588  point3D.second.Track().Length() >= min_track_len) {
589  CCVector3 painter_point;
590  painter_point.x =
591  static_cast<PointCoordinateType>(point3D.second.XYZ(0));
592  painter_point.y =
593  static_cast<PointCoordinateType>(point3D.second.XYZ(1));
594  painter_point.z =
595  static_cast<PointCoordinateType>(point3D.second.XYZ(2));
596  cloud_sparse_->addPoint(painter_point);
597 
598  Eigen::Vector4d color;
599  if (selection_mode) {
600  const size_t index = selection_buffer_.size();
601  selection_buffer_.push_back(std::make_pair(
602  point3D.first, SELECTION_BUFFER_POINT_IDX));
603  color = IndexToRGB(index).cast<double>();
604 
605  } else if (point3D.first == selected_point3D_id_) {
607  } else {
608  color = point_colormap_
609  ->ComputeColor(point3D.first,
610  point3D.second)
611  .cast<double>();
612  }
613 
615  Eigen::Vector3d(color(0), color(1), color(2)));
616  cloud_sparse_->addRGBColor(pointColor);
617  }
618  }
619  } else { // Image selected
620  const auto& selected_image = images[selected_image_id_];
621  for (const auto& point3D : points3D) {
622  if (point3D.second.Error() <= options_->render->max_error &&
623  point3D.second.Track().Length() >= min_track_len) {
624  CCVector3 painter_point;
625  painter_point.x =
626  static_cast<PointCoordinateType>(point3D.second.XYZ(0));
627  painter_point.y =
628  static_cast<PointCoordinateType>(point3D.second.XYZ(1));
629  painter_point.z =
630  static_cast<PointCoordinateType>(point3D.second.XYZ(2));
631  cloud_sparse_->addPoint(painter_point);
632 
633  Eigen::Vector4d color;
634  if (selection_mode) {
635  const size_t index = selection_buffer_.size();
636  selection_buffer_.push_back(std::make_pair(
637  point3D.first, SELECTION_BUFFER_POINT_IDX));
638  color = IndexToRGB(index).cast<double>();
639  } else if (selected_image.HasPoint3D(point3D.first)) {
641  } else if (point3D.first == selected_point3D_id_) {
643  } else {
644  color = point_colormap_
645  ->ComputeColor(point3D.first,
646  point3D.second)
647  .cast<double>();
648  }
649 
651  Eigen::Vector3d(color(0), color(1), color(2)));
652  cloud_sparse_->addRGBColor(pointColor);
653  }
654  }
655  }
656 
657  if (app_ && objContext.parent) {
658  app_->putObjectBackIntoDBTree(cloud_sparse_, objContext);
659  }
660 
661  drawPointCloud(cloud_sparse_);
662 }
663 
664 void ModelViewerWidget::UploadPointConnectionData() {
665  point_line_data_.clear();
666  if (selected_point3D_id_ == kInvalidPoint3DId) {
667  // No point selected, so upload empty data
668  resetLines(point_line_data_);
669  return;
670  }
671 
672  const auto& point3D = points3D[selected_point3D_id_];
673  if (point3D.Track().Elements().empty()) {
674  resetLines(point_line_data_);
675  return;
676  }
677 
678  // 3D point position.
679  point_line_data_.points_.push_back(point3D.XYZ());
680 
681  // All images in which 3D point is observed.
682  int index = 0;
683  for (const auto& track_el : point3D.Track().Elements()) {
684  const Image& conn_image = images[track_el.image_id];
685  const Eigen::Vector3d conn_proj_center = conn_image.ProjectionCenter();
686  point_line_data_.points_.push_back(conn_proj_center);
687  point_line_data_.lines_.push_back(Eigen::Vector2i(0, ++index));
688  }
689 
690  point_line_data_.PaintUniformColor(Eigen::Vector3d(kSelectedPointColor(0),
692  kSelectedPointColor(2)));
693  drawLines(point_line_data_);
694 }
695 
696 void ModelViewerWidget::UploadImageData(const bool selection_mode) {
697  std::size_t lastSensorNum = sensors_.size();
698  std::size_t curSensorNum = reg_image_ids.size();
699 
700  if (curSensorNum == 0) {
701  resetCameraSensors(sensors_);
702  return;
703  }
704 
705  if (lastSensorNum < curSensorNum) {
706  sensors_.reserve(curSensorNum);
707  } else if (lastSensorNum > curSensorNum) {
708  updateSensors(sensors_, reg_image_ids);
709  lastSensorNum = sensors_.size();
710  }
711 
712  std::size_t cameraIndex = 0;
713  for (const image_t image_id : reg_image_ids) {
714  const Image& image = images[image_id];
715  const Camera& camera = cameras[image.CameraId()];
716 
717  Eigen::Vector4f plane_color;
718  Eigen::Vector4f frame_color;
719  if (selection_mode) {
720  const size_t index = selection_buffer_.size();
721  selection_buffer_.push_back(
723  plane_color = frame_color = IndexToRGB(index);
724  } else {
725  if (image_id == selected_image_id_) {
726  plane_color = kSelectedImagePlaneColor.cast<float>();
727  frame_color = kSelectedImageFrameColor.cast<float>();
728  } else {
729  image_colormap_->ComputeColor(image, &plane_color,
730  &frame_color);
731  }
732  }
733 
734  // Lines are not colored with the indexed color in selection mode, so do
735  // not show them, so they do not block the selection process
736  ccCameraSensor* sensor = nullptr;
737  if (cameraIndex < lastSensorNum) {
738  sensor = sensors_[cameraIndex];
739  } else {
740  sensor = new ccCameraSensor();
741  sensors_.push_back(sensor);
742  }
743 
744  if (!sensor) {
745  return;
746  }
747 
748  sensor->setName(QString::number(image_id));
749  BuildImageModel(sensor, image, camera, image_size_,
750  plane_color.cast<double>(), frame_color.cast<double>());
751  cameraIndex++;
752  }
753 
754  drawCameraSensors(sensors_);
755 }
756 
757 void ModelViewerWidget::UploadImageConnectionData() {
758  std::vector<image_t> image_ids;
759 
760  image_line_data_.clear();
761 
762  if (selected_image_id_ != kInvalidImageId) {
763  // Show connections to selected images
764  image_ids.push_back(selected_image_id_);
765  } else if (options_->render->image_connections) {
766  // Show all connections
767  image_ids = reg_image_ids;
768  } else { // Disabled, so upload empty data
769  resetLines(image_line_data_);
770  return;
771  }
772 
773  for (const image_t image_id : image_ids) {
774  const Image& image = images.at(image_id);
775 
776  const Eigen::Vector3d proj_center = image.ProjectionCenter();
777 
778  // Collect all connected images
779  std::unordered_set<image_t> conn_image_ids;
780 
781  for (const Point2D& point2D : image.Points2D()) {
782  if (point2D.HasPoint3D()) {
783  const Point3D& point3D = points3D[point2D.Point3DId()];
784  for (const auto& track_elem : point3D.Track().Elements()) {
785  conn_image_ids.insert(track_elem.image_id);
786  }
787  }
788  }
789 
790  // Selected image in the center.
791  int centerIndex = static_cast<int>(image_line_data_.points_.size());
792  image_line_data_.points_.push_back(proj_center);
793 
794  // All connected images to the selected image.
795  int index = centerIndex;
796  for (const image_t conn_image_id : conn_image_ids) {
797  const Image& conn_image = images[conn_image_id];
798  const Eigen::Vector3d conn_proj_center =
799  conn_image.ProjectionCenter();
800  image_line_data_.points_.push_back(conn_proj_center);
801  image_line_data_.lines_.push_back(
802  Eigen::Vector2i(centerIndex, ++index));
803  }
804  }
805 
806  image_line_data_.PaintUniformColor(Eigen::Vector3d(
809  drawLines(image_line_data_);
810 }
811 
812 void ModelViewerWidget::UploadMovieGrabberData() {
813  std::size_t lastSensorNum = movie_grabber_sensors_.size();
814  std::size_t curSensorNum = movie_grabber_widget_->views.size();
815 
816  if (lastSensorNum < curSensorNum) {
817  movie_grabber_sensors_.reserve(curSensorNum);
818  } else if (lastSensorNum > curSensorNum) {
819  std::vector<colmap::image_t> image_ids;
820  for (const Image& image : movie_grabber_widget_->views) {
821  image_ids.push_back(image.ImageId());
822  }
823  updateSensors(movie_grabber_sensors_, image_ids);
824  lastSensorNum = movie_grabber_sensors_.size();
825  }
826 
827  movie_grabber_path_.clear();
828 
829  if (curSensorNum > 0) {
830  const Image& image0 = movie_grabber_widget_->views[0];
831  Eigen::Vector3d prev_proj_center = image0.ProjectionCenter();
832 
833  for (size_t i = 1; i < movie_grabber_widget_->views.size(); ++i) {
834  const Image& image = movie_grabber_widget_->views[i];
835  const Eigen::Vector3d curr_proj_center = image.ProjectionCenter();
836  movie_grabber_path_.points_.push_back(prev_proj_center);
837  movie_grabber_path_.points_.push_back(curr_proj_center);
838  std::size_t start = movie_grabber_path_.points_.size() - 2;
839  std::size_t end = movie_grabber_path_.points_.size() - 1;
840  movie_grabber_path_.lines_.push_back(Eigen::Vector2i(start, end));
841  prev_proj_center = curr_proj_center;
842  }
843  movie_grabber_path_.PaintUniformColor(Eigen::Vector3d(
846 
847  // Setup dummy camera with same settings as current OpenGL viewpoint.
848  const unsigned long kDefaultImageWdith = 2048;
849  const unsigned long kDefaultImageHeight = 1536;
850  const double focal_length =
851  -2.0 *
852  std::tan(DegToRad(ecvDisplayTools::GetCameraFovy()) / 2.0) *
853  kDefaultImageWdith;
854  Camera camera;
856  focal_length, kDefaultImageWdith,
857  kDefaultImageHeight);
858 
859  // Build all camera models
860  std::size_t index = 0;
861  for (size_t i = 0; i < curSensorNum; ++i) {
862  const Image& image = movie_grabber_widget_->views[i];
863  Eigen::Vector4d plane_color;
864  Eigen::Vector4d frame_color;
865  if (i == selected_movie_grabber_view_) {
866  plane_color = kSelectedImagePlaneColor;
867  frame_color = kSelectedImageFrameColor;
868  } else {
869  plane_color = kMovieGrabberImagePlaneColor;
870  frame_color = kMovieGrabberImageFrameColor;
871  }
872 
873  ccCameraSensor* sensor = nullptr;
874  if (index < lastSensorNum) {
875  sensor = movie_grabber_sensors_[index];
876  } else {
877  sensor = new ccCameraSensor();
878  movie_grabber_sensors_.push_back(sensor);
879  }
880 
881  if (!sensor) {
882  return;
883  }
884 
885  sensor->setName(QString::number(image.ImageId()));
886  BuildImageModel(sensor, image, camera, image_size_, plane_color,
887  frame_color);
888  index++;
889  }
890 
891  drawLines(movie_grabber_path_);
892  drawCameraSensors(movie_grabber_sensors_);
893  } else {
894  resetLines(movie_grabber_path_);
895  resetCameraSensors(movie_grabber_sensors_);
896  return;
897  }
898 }
899 
901 
902 void ModelViewerWidget::drawPointCloud(ccPointCloud* cloud) {
903  if (cloud->IsEmpty()) {
904  cloud->setVisible(false);
905  cloud->setEnabled(false);
906  cloud->setRedrawFlagRecursive(false);
907  return;
908  } else {
909  if (app_ && !app_->db()->find(static_cast<int>(cloud->getUniqueID()))) {
910  app_->addToDB(cloud);
911  cloud->setLocked(true);
912  }
913 
914  cloud->setPointSize(static_cast<unsigned>(point_size_));
915  cloud->setVisible(true);
916  cloud->setEnabled(true);
917  cloud->setRedrawFlagRecursive(true);
918  ccBBox box = cloud->getOwnBB();
919  if (box.isValid()) {
920  bbox_ += box;
921  }
922  }
923 }
924 
925 void ModelViewerWidget::resetPointCloud(ccPointCloud* cloud) {
926  if (cloud) {
927  cloud->clear();
928  if (app_ && app_->db()->find(cloud->getUniqueID())) {
929  cloud->setLocked(false);
930  app_->removeFromDB(cloud, false);
931  }
932  }
933 }
934 
935 void ModelViewerWidget::drawLines(geometry::LineSet& lineset) {
936  if (!lineset.IsEmpty()) {
938  if (lineset.isColorOverridden()) {
939  context.defaultPolylineColor = lineset.getTempColor();
940  } else if (lineset.colorsShown() && lineset.hasColors()) {
941  context.defaultPolylineColor =
942  ecvColor::Rgb::FromEigen(lineset.colors_[0]);
943  }
944 
945  context.currentLineWidth = 1;
946  context.viewID = QString::number(lineset.getUniqueID(), 10);
947  lineset.setRedrawFlagRecursive(true);
948  ccBBox box = lineset.getOwnBB();
949  if (box.isValid()) {
950  bbox_ += box;
951  }
952 
953  ecvDisplayTools::Draw(context, &lineset);
954  }
955 }
956 
957 void ModelViewerWidget::resetLines(geometry::LineSet& lineset) {
959 }
960 
961 void ModelViewerWidget::drawCameraSensors(
962  std::vector<ccCameraSensor*>& sensors) {
963  if (!main_sensors_) {
964  main_sensors_ = new ccHObject("SensorsGroup");
965  main_sensors_->setLocked(true);
966  }
967 
968  MainWindow::ccHObjectContext objContext;
969  if (main_sensors_ && app_ &&
970  app_->db()->find(static_cast<int>(main_sensors_->getUniqueID()))) {
971  objContext = app_->removeObjectTemporarilyFromDBTree(main_sensors_);
972  }
973 
974  for (std::size_t i = 0; i < sensors.size(); ++i) {
975  if (!sensors[i]) continue;
976  if (main_sensors_ && !main_sensors_->find(sensors[i]->getUniqueID())) {
977  sensors[i]->setLocked(true);
978  main_sensors_->addChild(sensors[i]);
979  }
980  sensors[i]->setVisible(true);
981  sensors[i]->setEnabled(true);
982  sensors[i]->setRedrawFlagRecursive(true);
983  ccBBox box = sensors[i]->getOwnBB(true);
984  if (box.isValid()) {
985  bbox_ += box;
986  }
987  }
988 
989  if (main_sensors_ && app_ && objContext.parent) {
990  app_->putObjectBackIntoDBTree(main_sensors_, objContext);
991  }
992 
993  if (app_ &&
994  !app_->db()->find(static_cast<int>(main_sensors_->getUniqueID()))) {
995  if (main_sensors_->getChildrenNumber() > 0) {
996  app_->addToDB(main_sensors_);
997  // just avoid duplicated rendering
998  main_sensors_->setRedrawFlagRecursive(false);
999  }
1000  }
1001 }
1002 
1003 void ModelViewerWidget::resetCameraSensors(
1004  std::vector<ccCameraSensor*>& sensors) {
1005  clearSensors(sensors);
1006 }
1007 
1008 void ModelViewerWidget::clearSensors(std::vector<ccCameraSensor*>& sensors) {
1009  for (std::size_t i = 0; i < sensors.size(); ++i) {
1010  if (!sensors[i]) continue;
1011  if (app_ &&
1012  app_->db()->find(static_cast<int>(sensors[i]->getUniqueID()))) {
1013  sensors[i]->setLocked(false);
1014  if (sensors[i]->isSelected()) {
1015  // must unselect this sensor to be deleted,
1016  // otherwise will remain in rendering screen?
1017  app_->db()->unselectEntity(sensors[i]);
1018  }
1019  app_->removeFromDB(sensors[i], true);
1020  }
1021  sensors[i] = nullptr;
1022  }
1023 
1024  if (main_sensors_ && main_sensors_->getChildrenNumber() == 0) {
1025  if (app_ &&
1026  app_->db()->find(static_cast<int>(main_sensors_->getUniqueID()))) {
1027  main_sensors_->setLocked(false);
1028  app_->removeFromDB(main_sensors_, true);
1029  main_sensors_ = nullptr;
1030  }
1031  }
1032 
1033  sensors.clear();
1034 }
1035 
1036 void ModelViewerWidget::updateSensors(
1037  std::vector<ccCameraSensor*>& sensors,
1038  const std::vector<colmap::image_t>& image_ids) {
1039  if (sensors.size() > image_ids.size()) {
1040  std::vector<ccCameraSensor*> toBeDeleted;
1041  std::vector<ccCameraSensor*> preserved;
1042  for (ccCameraSensor* sensor : sensors) {
1043  if (!sensor) {
1044  continue;
1045  }
1046  colmap::image_t sensorId = sensor->getName().toUInt();
1047  if (std::find(image_ids.begin(), image_ids.end(), sensorId) ==
1048  image_ids.end()) {
1049  toBeDeleted.push_back(sensor);
1050  } else {
1051  preserved.push_back(sensor);
1052  }
1053  }
1054 
1055  if (!toBeDeleted.empty()) {
1056  sensors.clear();
1057  for (ccCameraSensor* sensor : preserved) {
1058  sensors.push_back(sensor);
1059  }
1060 
1061  clearSensors(toBeDeleted);
1062  }
1063  }
1064 }
1065 
1066 ccHObject* ModelViewerWidget::getSelectedCamera(colmap::image_t selected_id) {
1067  if (main_sensors_ && main_sensors_->getChildrenNumber() > 0) {
1068  for (unsigned ci = 0; ci != main_sensors_->getChildrenNumber(); ++ci) {
1069  ccHObject* obj = main_sensors_->getChild(ci);
1070  if (obj->getName().toUInt() == selected_id) {
1071  return obj;
1072  }
1073  }
1074  }
1075 
1076  return nullptr;
1077 }
1078 
1079 } // namespace cloudViewer
constexpr PointCoordinateType PC_ONE
'1' as a PointCoordinateType value
Definition: CVConst.h:67
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
std::shared_ptr< core::Tensor > image
math::float4 color
const Eigen::Vector4d kMovieGrabberImageFrameColor(0.0, 0.8, 0.8, 1.0)
const Eigen::Vector4d kSelectedImagePlaneColor(1.0, 0.0, 1.0, 0.6)
#define SELECTION_BUFFER_IMAGE_IDX
const Eigen::Vector4d kSelectedPointColor(0.0, 1.0, 0.0, 1.0)
const Eigen::Vector4d kSelectedImageFrameColor(0.8, 0.0, 0.8, 1.0)
const Eigen::Vector4d kMovieGrabberImagePlaneColor(0.0, 1.0, 1.0, 0.6)
#define SELECTION_BUFFER_POINT_IDX
Eigen::Matrix3d rotation
Definition: VoxelGridIO.cpp:27
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
Bounding box structure.
Definition: ecvBBox.h:25
virtual ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
Definition: ecvBBox.h:64
Camera (projective) sensor.
const ecvColor::Rgb & getPlaneColor() const
static float ConvertFocalMMToPix(float focal_mm, float ccdPixelSize_mm)
Helper: converts camera focal from mm to pixels.
void setIntrinsicParameters(const IntrinsicParameters &params)
Sets intrinsic parameters.
void setPlaneColor(ecvColor::Rgb color)
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showColors(bool state)
Sets colors visibility.
static ccGLMatrixTpl< float > FromEigenMatrix3(const Eigen::Matrix< float, 3, 3 > &mat)
T * data()
Returns a pointer to internal data.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
void setPointSize(unsigned size=0)
Sets point size.
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
static ccCameraSensor * ToCameraSensor(ccHObject *obj)
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
ccHObject * find(unsigned uniqueID)
Finds an entity in this object hierarchy.
Definition: ecvHObject.cpp:594
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
void setRedrawFlagRecursive(bool redraw=false)
Definition: ecvHObject.cpp:772
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
Definition: ecvHObject.cpp:534
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
virtual void setLocked(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:117
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual unsigned getUniqueID() const
Returns object unique ID.
Definition: ecvObject.h:86
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
ecvColor::Rgb & getPointColorPtr(size_t pointIndex)
virtual bool IsEmpty() const override
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
void clear() override
Clears the entity from all its points and features.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
void setFrameColor(ecvColor::Rgb color)
Definition: ecvSensor.h:130
virtual void setRigidTransformation(const ccGLMatrix &mat)
Definition: ecvSensor.h:99
void setGraphicScale(PointCoordinateType scale)
Sets the sensor graphic representation scale.
Definition: ecvSensor.h:125
void clear()
Resets the bounding box.
Definition: BoundingBox.h:125
bool isValid() const
Returns whether bounding box is valid or not.
Definition: BoundingBox.h:203
void ShowImageWithId(const colmap::image_t image_id)
void SelectObject(ccHObject *entity, unsigned subEntityID, int x, int y, const CCVector3 &P)
std::vector< colmap::image_t > reg_image_ids
std::unordered_map< colmap::point3D_t, colmap::Point3D > points3D
void ShowImageInfo(const colmap::image_t image_id)
void SetImageSize(const float image_size, bool autoUpdate=true)
void SetPointColormap(PointColormapBase *colormap)
void SetPointSize(const float point_size, bool autoUpdate=true)
void ChangeCameraSize(const float delta)
std::unordered_map< colmap::camera_t, colmap::Camera > cameras
void SetImageColormap(ImageColormapBase *colormap)
void SetBackgroundColor(const float r, const float g, const float b)
void ShowPointInfo(const colmap::point3D_t point3D_id)
void ChangePointSize(const float delta)
colmap::Reconstruction * reconstruction
void EndRender(bool autoZoom=true)
void ChangeFocusDistance(const float delta)
std::unordered_map< colmap::image_t, colmap::Image > images
void SelectMoviewGrabberView(const size_t view_idx)
std::vector< colmap::Image > views
CCVector3 * getPointPtr(size_t index)
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
unsigned size() const override
Definition: PointCloudTpl.h:38
void Show(const colmap::point3D_t point3D_id)
std::vector< Eigen::Vector3d > points_
Points coordinates.
Definition: LineSet.h:156
std::vector< Eigen::Vector2i > lines_
Lines denoted by the index of points forming the line.
Definition: LineSet.h:158
LineSet & PaintUniformColor(const Eigen::Vector3d &color)
Assigns each line in the LineSet the same color.
Definition: LineSet.h:101
void InitializeWithId(const int model_id, const double focal_length, const size_t width, const size_t height)
Definition: camera.cc:203
double ImageToWorldThreshold(const double threshold) const
Definition: camera.cc:226
double PrincipalPointX() const
Definition: camera.cc:146
size_t Width() const
Definition: camera.h:160
double PrincipalPointY() const
Definition: camera.cc:152
size_t Height() const
Definition: camera.h:162
Eigen::Vector3d ProjectionCenter() const
Definition: image.cc:152
ModelViewerWidget(QWidget *parent, OptionManager *options)
std::shared_ptr< RenderOptions > render
const class Track & Track() const
Definition: point3d.h:106
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
const std::vector< TrackElement > & Elements() const
Definition: track.h:84
RGB color structure.
Definition: ecvColorTypes.h:49
constexpr static RgbTpl FromEigen(const Eigen::Vector3d &t)
Definition: ecvColorTypes.h:86
static ecvDisplayTools * TheInstance()
static void RemoveEntities(const ccHObject *obj)
static int GetDevicePixelRatio()
static void SetDisplayParameters(const ecvGui::ParamStruct &params)
Sets current parameters for this display.
static void Draw(const CC_DRAW_CONTEXT &context, const ccHObject *obj)
static void Update()
static double GetCameraFovy(int viewport=0)
static void UpdateConstellationCenterAndZoom(const ccBBox *aBox=nullptr, bool redraw=true)
Center and zoom on a given bounding box.
static int GlWidth()
Returns the OpenGL context width.
static double GetCameraFocalDistance(int viewport=0)
void itemPicked(ccHObject *entity, unsigned subEntityID, int x, int y, const CCVector3 &P)
Signal emitted when a point (or a triangle) is picked.
static int GlHeight()
Returns the OpenGL context height.
static void SetRedrawRecursive(bool redraw=false)
static void RedrawDisplay(bool only2D=false, bool forceRedraw=true)
static void UpdateScreen()
static QImage RenderToImage(int zoomFactor=1, bool renderOverlayItems=false, bool silent=false, int viewport=0)
static void GetViewMatrix(double *viewArray, int viewport=0)
static float GetFov()
Returns the current f.o.v. (field of view) in degrees.
static void SetCameraFocalDistance(double focal_distance, int viewport=0)
static bool GetPerspectiveState()
Returns perspective mode.
static const ParamStruct & Parameters()
Returns the stored values of each parameter.
ImGuiContext * context
Definition: Window.cpp:76
normal_z y
normal_z x
@ POINT_CLOUD
Definition: CVTypes.h:104
@ CAMERA_SENSOR
Definition: CVTypes.h:118
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
Definition: SlabTraits.h:49
Generic file read and write utility for python interface.
bool GreaterThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:37
float DegreesToRadians(int degrees)
Convert degrees to radians.
Definition: CVMath.h:98
uint64_t point3D_t
Definition: types.h:72
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
Eigen::Matrix< Index, 2, 1 > Vector2i
Definition: knncpp.h:29
Intrinsic parameters of the camera sensor.
Display context.
GUI parameters.
ecvColor::Rgbub backgroundCol
Background color.
Backup "context" for an object.