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();
129  SetPointColormap(new PointColormapPhotometric());
130  SetImageColormap(new ImageColormapUniform());
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 
171  cameras = reconstruction->Cameras();
172  points3D = reconstruction->Points3D();
173  reg_image_ids = reconstruction->RegImageIds();
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_) {
438  }
439  } else {
440  return;
441  }
442  }
443 
444  movie_grabber_widget_->show();
445 }
446 
447 void ModelViewerWidget::ShowPointInfo(const point3D_t point3D_id) {
448  point_viewer_widget_->Show(point3D_id);
449 }
450 
451 void ModelViewerWidget::ShowImageInfo(const image_t image_id) {
452  image_viewer_widget_->ShowImageWithId(image_id);
453 }
454 
456  if (app_) {
458  }
459 }
460 
462  if (app_) {
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;
855  camera.InitializeWithId(SimplePinholeCameraModel::model_id,
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
cmdLineReadable * params[]
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
ccHObjectContext removeObjectTemporarilyFromDBTree(ccHObject *obj) override
Removes object temporarily from DB tree.
ccDBRoot * db()
Returns real 'dbRoot' object.
Definition: MainWindow.h:193
void addToDB(const QStringList &filenames, QString fileFilter=QString(), bool displayDialog=true)
void doActionOrthogonalProjection()
void removeFromDB(ccHObject *obj, bool autoDelete=true) override
Removes an entity from main db tree.
void doActionPerspectiveProjection()
void putObjectBackIntoDBTree(ccHObject *obj, const ccHObjectContext &context) override
Adds back object to DB tree.
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)
void expandElement(ccHObject *object, bool state)
Expands tree at a given node.
Definition: ecvDBRoot.cpp:477
void unselectEntity(ccHObject *obj)
Unselects a given entity.
Definition: ecvDBRoot.cpp:1035
void selectEntity(ccHObject *obj, bool forceAdditiveSelection=false)
Selects a given entity.
Definition: ecvDBRoot.cpp:1054
ccHObject * find(int uniqueID) const
Finds an element in DB.
Definition: ecvDBRoot.cpp:1197
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.
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
void setRedrawFlagRecursive(bool redraw=false)
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.)
virtual bool IsEmpty() const override
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
ecvColor::Rgb & getPointColorPtr(size_t pointIndex)
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)
ModelViewerWidget(QWidget *parent, OptionManager *options, MainWindow *app)
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
RGB color structure.
Definition: ecvColorTypes.h:49
constexpr static RgbTpl FromEigen(const Eigen::Vector3d &t)
Definition: ecvColorTypes.h:86
static int GetDevicePixelRatio()
static void Draw(const CC_DRAW_CONTEXT &context, const ccHObject *obj)
static void Update()
static void SetDisplayParameters(const ecvGui::ParamStruct &params)
Sets current parameters for this display.
static void RemoveEntities(const ccHObject *obj)
static double GetCameraFovy(int viewport=0)
static int GlWidth()
Returns the OpenGL context width.
static double GetCameraFocalDistance(int viewport=0)
static ecvDisplayTools * TheInstance()
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 float GetFov()
Returns the current f.o.v. (field of view) in degrees.
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 void SetCameraFocalDistance(double focal_distance, int viewport=0)
static void UpdateConstellationCenterAndZoom(const ccBBox *aBox=nullptr, bool redraw=true)
Center and zoom on a given bounding box.
static void RedrawDisplay(bool only2D=false, bool forceRedraw=true)
static bool GetPerspectiveState()
Returns perspective mode.
static const ParamStruct & Parameters()
Returns the stored values of each parameter.
int min(int a, int b)
Definition: cutil_math.h:53
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
int max(int a, int b)
Definition: cutil_math.h:48
ImGuiContext * context
Definition: Window.cpp:76
@ 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
colmap::ImageColormapBase ImageColormapBase
colmap::OptionManager OptionManager
colmap::PointColormapBase PointColormapBase
Eigen::Matrix< Index, 2, 1 > Vector2i
Definition: knncpp.h:29
Intrinsic parameters of the camera sensor.
Display context.
GUI parameters.
Backup "context" for an object.