ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
PclTools.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 "PclTools.h"
9 
10 #include <Utils/cc2sm.h>
11 #include <Utils/sm2cc.h>
12 
13 #include "VtkUtils/vtkutils.h"
14 
15 // CV_CORE_LIB
16 #include <CVTools.h>
17 #include <FileSystem.h>
18 #include <ecvGLMatrix.h>
19 
20 // CV_DB_LIB
21 #include <LineSet.h>
24 #include <ecvBBox.h>
25 #include <ecvCameraSensor.h>
26 #include <ecvColorScale.h>
27 #include <ecvDisplayTools.h>
28 #include <ecvGBLSensor.h>
29 #include <ecvPlane.h>
30 #include <ecvScalarField.h>
31 #include <vtkActor.h>
32 #include <vtkAnnotatedCubeActor.h>
33 #include <vtkAppendPolyData.h>
34 #include <vtkAxesActor.h>
35 #include <vtkCaptionActor2D.h>
36 #include <vtkCellData.h>
37 #include <vtkDataSet.h>
38 #include <vtkDataSetMapper.h>
39 #include <vtkLODActor.h>
40 #include <vtkLine.h>
41 #include <vtkLineSource.h>
42 #include <vtkPlaneSource.h>
43 #include <vtkPointData.h>
44 #include <vtkPoints.h>
45 #include <vtkPolygon.h>
46 #include <vtkPropAssembly.h>
47 #include <vtkProperty.h>
48 #include <vtkProperty2D.h>
49 #include <vtkSmartPointer.h>
50 #include <vtkTextActor.h>
51 #include <vtkTextProperty.h>
52 #include <vtkTexture.h>
53 #include <vtkUnsignedCharArray.h>
54 #include <vtkUnstructuredGrid.h>
55 
56 #if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2
57 #include <pcl/visualization/vtk/vtkVertexBufferObjectMapper.h>
58 #endif
59 
65 
66 // PCL
67 #include <pcl/common/transforms.h>
68 #include <pcl/features/normal_3d.h>
69 #include <pcl/io/vtk_lib_io.h>
70 #include <pcl/visualization/pcl_visualizer.h>
71 
72 #if defined(_WIN32)
73 // Remove macros defined in Windows.h
74 #undef near
75 #undef far
76 #endif
77 
78 using namespace cloudViewer;
79 
84  bool use_scalars,
85  bool use_vbos) {
86  // If actor is not initialized, initialize it here
87  if (!actor) actor = vtkSmartPointer<vtkLODActor>::New();
88 
89 #if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2
90  if (use_vbos) {
93 
94  mapper->SetInput(data);
95 
96  if (use_scalars) {
98  data->GetPointData()->GetScalars();
99  double minmax[2];
100  if (scalars) {
101  scalars->GetRange(minmax);
102  mapper->SetScalarRange(minmax);
103 
104  mapper->SetScalarModeToUsePointData();
105  mapper->SetInterpolateScalarsBeforeMapping(
107  mapper->ScalarVisibilityOn();
108  }
109  }
110 
111  actor->SetNumberOfCloudPoints(
112  int(std::max<vtkIdType>(1, data->GetNumberOfPoints() / 10)));
113  actor->GetProperty()->SetInterpolationToFlat();
114 
119  // actor->GetProperty ()->BackfaceCullingOn ();
120 
121  actor->SetMapper(mapper);
122  } else
123 #endif
124  {
127 #if VTK_MAJOR_VERSION < 6
128  mapper->SetInput(data);
129 #else
130  mapper->SetInputData(data);
131 #endif
132 
133  if (use_scalars) {
135  data->GetPointData()->GetScalars();
136  double minmax[2];
137  if (scalars) {
138  scalars->GetRange(minmax);
139  mapper->SetScalarRange(minmax);
140 
141  mapper->SetScalarModeToUsePointData();
142  mapper->SetInterpolateScalarsBeforeMapping(
144  mapper->ScalarVisibilityOn();
145  }
146  }
147 #if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2
148  mapper->ImmediateModeRenderingOff();
149 #endif
150 
151  actor->SetNumberOfCloudPoints(
152  int(std::max<vtkIdType>(1, data->GetNumberOfPoints() / 10)));
153  actor->GetProperty()->SetInterpolationToFlat();
154 
159  // actor->GetProperty ()->BackfaceCullingOn ();
160 
161  actor->SetMapper(mapper);
162  }
163 }
164 
169  bool use_scalars,
170  bool use_vbos) {
171  // If actor is not initialized, initialize it here
172  if (!actor) actor = vtkSmartPointer<vtkActor>::New();
173 
174 #if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2
175  if (use_vbos) {
178 
179  mapper->SetInput(data);
180 
181  if (use_scalars) {
183  data->GetPointData()->GetScalars();
184  double minmax[2];
185  if (scalars) {
186  scalars->GetRange(minmax);
187  mapper->SetScalarRange(minmax);
188 
189  mapper->SetScalarModeToUsePointData();
190  mapper->SetInterpolateScalarsBeforeMapping(
192  mapper->ScalarVisibilityOn();
193  }
194  }
195 
196  // actor->SetNumberOfCloudPoints (int (std::max<vtkIdType> (1,
197  // data->GetNumberOfPoints () / 10)));
198  actor->GetProperty()->SetInterpolationToFlat();
199 
204  // actor->GetProperty ()->BackfaceCullingOn ();
205 
206  actor->SetMapper(mapper);
207  } else
208 #endif
209  {
212 #if VTK_MAJOR_VERSION < 6
213  mapper->SetInput(data);
214 #else
215  mapper->SetInputData(data);
216 #endif
217 
218  if (use_scalars) {
220  data->GetPointData()->GetScalars();
221  double minmax[2];
222  if (scalars) {
223  scalars->GetRange(minmax);
224  mapper->SetScalarRange(minmax);
225 
226  mapper->SetScalarModeToUsePointData();
227  mapper->SetInterpolateScalarsBeforeMapping(
229  mapper->ScalarVisibilityOn();
230  }
231  }
232 #if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2
233  mapper->ImmediateModeRenderingOff();
234 #endif
235 
236  // actor->SetNumberOfCloudPoints (int (std::max<vtkIdType> (1,
237  // data->GetNumberOfPoints () / 10)));
238  actor->GetProperty()->SetInterpolationToFlat();
239 
244  // actor->GetProperty ()->BackfaceCullingOn ();
245 
246  actor->SetMapper(mapper);
247  }
248 
249  // actor->SetNumberOfCloudPoints (std::max<vtkIdType> (1,
250  // data->GetNumberOfPoints () / 10));
251  actor->GetProperty()->SetInterpolationToFlat();
252 }
253 
258 }
259 
261  const cloudViewer::geometry::LineSet& lineset) {
263  linePoints->SetNumberOfPoints(
264  static_cast<vtkIdType>(2 * lineset.lines_.size()));
265  for (std::size_t i = 0; i < lineset.lines_.size(); ++i) {
266  std::pair<Eigen::Vector3d, Eigen::Vector3d> segment =
267  lineset.GetLineCoordinate(i);
268  linePoints->SetPoint(static_cast<vtkIdType>(2 * i),
269  segment.first.data());
270  linePoints->SetPoint(static_cast<vtkIdType>(2 * i + 1),
271  segment.second.data());
272  }
273  return linePoints;
274 }
275 
277  const cloudViewer::geometry::LineSet& lineset,
281  if (!points || !lines) {
282  return false;
283  }
284 
285  bool has_color = false;
286  if (lineset.hasColors()) {
287  has_color = true;
288  colors->SetNumberOfComponents(3);
289  colors->SetName("Colors");
290  colors->SetNumberOfTuples(
291  static_cast<vtkIdType>(lineset.points_.size()));
292  }
293 
294  points->SetNumberOfPoints(static_cast<vtkIdType>(lineset.points_.size()));
295  for (std::size_t i = 0; i < lineset.points_.size(); ++i) {
296  Eigen::Vector3d p = lineset.points_[i];
297  points->SetPoint(static_cast<vtkIdType>(i), p.data());
298  }
299 
300  for (std::size_t i = 0; i < lineset.lines_.size(); ++i) {
302  Eigen::Vector2i segIndex = lineset.lines_[i];
303  segment->GetPointIds()->SetId(0, segIndex(0));
304  segment->GetPointIds()->SetId(1, segIndex(1));
305  lines->InsertNextCell(segment);
306  if (has_color) {
308  colors->InsertTuple3(static_cast<vtkIdType>(i), color.r, color.g,
309  color.b);
310  }
311  }
312 
313  return true;
314 }
315 
318  vtkSmartPointer<vtkLineSource> lineSource =
320  lineSource->SetPoints(points);
321  lineSource->Update();
322  return lineSource->GetOutput();
323 }
324 
329  if (points->GetNumberOfPoints() == 0 && lines->GetNumberOfCells() == 0 &&
330  colors->GetNumberOfTuples() == 0) {
331  return nullptr;
332  }
333 
335  if (points->GetNumberOfPoints() > 0) {
336  polyData->SetPoints(points);
337  }
338  if (lines->GetNumberOfCells() > 0) {
339  polyData->SetLines(lines);
340  }
341  if (colors->GetNumberOfTuples() > 0) {
342  polyData->GetCellData()->SetScalars(colors);
343  }
344  return polyData;
345 }
346 
348  const ecvColor::Rgb& color,
349  bool is_cell) {
352  colors->SetNumberOfComponents(3);
353  colors->SetName("Colors");
354 
355  if (is_cell) {
356  colors->SetNumberOfTuples(polyData->GetPolys()->GetNumberOfCells());
357  for (vtkIdType i = 0; i < polyData->GetPolys()->GetNumberOfCells();
358  ++i) {
359  colors->InsertTuple3(i, color.r, color.g, color.b);
360  }
361  polyData->GetCellData()->SetScalars(colors);
362  } else {
363  colors->SetNumberOfTuples(polyData->GetNumberOfPoints());
364  for (vtkIdType i = 0; i < polyData->GetNumberOfPoints(); ++i) {
365  colors->InsertTuple3(i, color.r, color.g, color.b);
366  }
367  polyData->GetPointData()->SetScalars(colors);
368  }
369 }
370 
374  vtkIdType n_points = polyData->GetNumberOfPoints();
375  if (n_points == 8) {
376  vtkIdType cellId1[3] = {0, 1, 3};
377  cellArray->InsertNextCell(3, cellId1);
378  vtkIdType cellId2[3] = {7, 6, 4};
379  cellArray->InsertNextCell(3, cellId2);
380  polyData->SetPolys(cellArray);
381  } else if (n_points == 14) {
382  vtkIdType cellId1[3] = {0, 1, 3};
383  cellArray->InsertNextCell(3, cellId1);
384  vtkIdType cellId2[3] = {7, 6, 4};
385  cellArray->InsertNextCell(3, cellId2);
386 
387  vtkIdType cellId3[3] = {8, 9, 11};
388  cellArray->InsertNextCell(3, cellId3);
389  polyData->SetPolys(cellArray);
390  }
391 }
392 
394  const cloudViewer::geometry::LineSet& lineset) {
395  assert(lineset.lines_.size() == 3);
396 
397  // right vector
399  lineR->SetNumberOfPoints(2);
400  std::pair<Eigen::Vector3d, Eigen::Vector3d> segmentR =
401  lineset.GetLineCoordinate(0);
402  lineR->SetPoint(static_cast<vtkIdType>(0), segmentR.first.data());
403  lineR->SetPoint(static_cast<vtkIdType>(1), segmentR.second.data());
404  vtkSmartPointer<vtkPolyData> rLinesData = CreateLine(lineR);
405  SetPolyDataColor(rLinesData, ecvColor::red, false);
406 
407  // up vector
409  lineUp->SetNumberOfPoints(2);
410  std::pair<Eigen::Vector3d, Eigen::Vector3d> segmentUp =
411  lineset.GetLineCoordinate(1);
412  lineUp->SetPoint(static_cast<vtkIdType>(0), segmentUp.first.data());
413  lineUp->SetPoint(static_cast<vtkIdType>(1), segmentUp.second.data());
414  vtkSmartPointer<vtkPolyData> uLinesData = CreateLine(lineUp);
415  SetPolyDataColor(uLinesData, ecvColor::yellow, false);
416 
417  // view vector
419  lineV->SetNumberOfPoints(2);
420  std::pair<Eigen::Vector3d, Eigen::Vector3d> segmentV =
421  lineset.GetLineCoordinate(2);
422  lineV->SetPoint(static_cast<vtkIdType>(0), segmentV.first.data());
423  lineV->SetPoint(static_cast<vtkIdType>(1), segmentV.second.data());
424  vtkSmartPointer<vtkPolyData> vLinesData = CreateLine(lineV);
425  SetPolyDataColor(vLinesData, ecvColor::green, false);
426 
429  appendFilter->AddInputData(rLinesData);
430  appendFilter->AddInputData(uLinesData);
431  appendFilter->AddInputData(vLinesData);
432  appendFilter->Update();
433  return appendFilter->GetOutput();
434 }
435 
437  const ccGBLSensor* gBLSensor) {
438  assert(gBLSensor);
439  auto linePoints =
441  gBLSensor->getSensorHead());
442 
443  // sensor head lines
444  vtkSmartPointer<vtkPolyData> headLinesData =
445  CreatePolyDataFromLineSet(*linePoints, false);
446 
447  // sensor leg lines
448  vtkSmartPointer<vtkPolyData> legLinesData =
449  CreatePolyDataFromLineSet(gBLSensor->getSensorLegLines(), false);
450 
451  // sensor axis lines
452  vtkSmartPointer<vtkPolyData> axisLinesData =
453  CreatePolyDataFromLineSet(gBLSensor->getSensorAxis(), false);
454 
457  appendFilter->AddInputData(headLinesData);
458  appendFilter->AddInputData(legLinesData);
459  appendFilter->AddInputData(axisLinesData);
460  appendFilter->Update();
461  return appendFilter->GetOutput();
462 }
463 
465  const ccCameraSensor* cameraSensor,
466  const ecvColor::Rgb& lineColor,
467  const ecvColor::Rgb& planeColor) {
468  assert(cameraSensor);
469 
470  // near plane
471  vtkSmartPointer<vtkPolyData> nearPlaneLinesData =
472  CreatePolyDataFromLineSet(cameraSensor->getNearPlane());
473  AddPolyDataCell(nearPlaneLinesData);
474  SetPolyDataColor(nearPlaneLinesData, planeColor, false);
475 
476  // side lines
477  vtkSmartPointer<vtkPolyData> sideLinesData =
478  CreatePolyDataFromLineSet(cameraSensor->getSideLines());
479  SetPolyDataColor(sideLinesData, lineColor, false);
480 
481  // arrow lines
482  vtkSmartPointer<vtkPolyData> arrowLinesData =
483  CreatePolyDataFromLineSet(cameraSensor->getArrow());
484  AddPolyDataCell(arrowLinesData);
485  SetPolyDataColor(arrowLinesData, lineColor, false);
486 
487  // axis lines
488  vtkSmartPointer<vtkPolyData> axisLinesData =
489  CreateCoordinateFromLineSet(cameraSensor->getAxis());
490 
493  appendFilter->AddInputData(nearPlaneLinesData);
494  appendFilter->AddInputData(sideLinesData);
495  appendFilter->AddInputData(arrowLinesData);
496  appendFilter->AddInputData(axisLinesData);
497  appendFilter->Update();
498  return appendFilter->GetOutput();
499 }
500 
502  const pcl::ModelCoefficients& coefficients) {
505  plane->SetNormal(coefficients.values[0], coefficients.values[1],
506  coefficients.values[2]);
507 
508  double norm_sqr = coefficients.values[0] * coefficients.values[0] +
509  coefficients.values[1] * coefficients.values[1] +
510  coefficients.values[2] * coefficients.values[2];
511 
512  plane->Push(-coefficients.values[3] / sqrt(norm_sqr));
513  plane->Update();
514  return (plane->GetOutput());
515 }
516 
518  const pcl::ModelCoefficients& coefficients,
519  double x,
520  double y,
521  double z,
522  double scale) {
525 
526  double norm_sqr = 1.0 / (coefficients.values[0] * coefficients.values[0] +
527  coefficients.values[1] * coefficients.values[1] +
528  coefficients.values[2] * coefficients.values[2]);
529 
530  plane->SetNormal(coefficients.values[0], coefficients.values[1],
531  coefficients.values[2]);
532  double t = x * coefficients.values[0] + y * coefficients.values[1] +
533  z * coefficients.values[2] + coefficients.values[3];
534  x -= coefficients.values[0] * t * norm_sqr;
535  y -= coefficients.values[1] * t * norm_sqr;
536  z -= coefficients.values[2] * t * norm_sqr;
537 
538  plane->SetCenter(x, y, z);
539  plane->Update();
540 
541  Eigen::Vector3d p1, p2;
542  Eigen::Vector3d n;
543  n.x() = x + coefficients.values[0];
544  n.y() = y + coefficients.values[1];
545  n.z() = z + coefficients.values[2];
546 
547  p1.x() = x + 1.0; //
548  p1.y() = y;
549  p1.z() = (coefficients.values[3] - coefficients.values[0] * p1.x() -
550  coefficients.values[1] * p1.y()) /
551  coefficients.values[2];
552 
553  p2 = n.cross(p1);
554 
555  p1.normalize();
556  p2.normalize();
557 
558  p1 = p1 * scale;
559  p2 = p2 * scale;
560 
561  double point1[3];
562  double point2[3];
563 
564  point1[0] = p1.x();
565  point1[1] = p1.y();
566  point1[2] = p1.z();
567 
568  point2[0] = p2.x();
569  point2[1] = p2.y();
570  point2[2] = p2.z();
571 
572  plane->SetOrigin(x, y, z);
573  plane->SetPoint1(point1);
574  plane->SetPoint2(point2);
575 
576  plane->Update();
577 
578  return plane->GetOutput();
579 }
580 
582  double axesLength,
583  const std::string& xLabel,
584  const std::string& yLabel,
585  const std::string& zLabel,
586  const std::string& xPlus,
587  const std::string& xMinus,
588  const std::string& yPlus,
589  const std::string& yMinus,
590  const std::string& zPlus,
591  const std::string& zMinus) {
593  axes->SetShaftTypeToCylinder();
594  axes->SetXAxisLabelText(xLabel.c_str());
595  axes->SetYAxisLabelText(yLabel.c_str());
596  axes->SetZAxisLabelText(zLabel.c_str());
597  axes->GetXAxisTipProperty()->SetColor(1.0, 0.0, 0.0);
598  axes->GetXAxisShaftProperty()->SetColor(1.0, 0.0, 0.0);
599  axes->GetYAxisTipProperty()->SetColor(1.0, 1.0, 0.0);
600  axes->GetYAxisShaftProperty()->SetColor(1.0, 1.0, 0.0);
601  axes->GetZAxisTipProperty()->SetColor(0.0, 1.0, 0.0);
602  axes->GetZAxisShaftProperty()->SetColor(0.0, 1.0, 0.0);
603  axes->SetTotalLength(axesLength, axesLength, axesLength);
604 
607  cube->SetXPlusFaceText(xPlus.c_str());
608  cube->SetXMinusFaceText(xMinus.c_str());
609  cube->SetYPlusFaceText(yPlus.c_str());
610  cube->SetYMinusFaceText(yMinus.c_str());
611  cube->SetZPlusFaceText(zPlus.c_str());
612  cube->SetZMinusFaceText(zMinus.c_str());
613  cube->SetXFaceTextRotation(180);
614  cube->SetYFaceTextRotation(180);
615  cube->SetZFaceTextRotation(-90);
616  cube->SetFaceTextScale(0.65);
617  cube->GetCubeProperty()->SetColor(0.5, 1, 1);
618  cube->GetTextEdgesProperty()->SetLineWidth(1);
619  cube->GetTextEdgesProperty()->SetDiffuse(0);
620  cube->GetTextEdgesProperty()->SetAmbient(1);
621  cube->GetTextEdgesProperty()->SetColor(0.1800, 0.2800, 0.2300);
622  // this static function improves the appearance of the text edges
623  // since they are overlaid on a surface rendering of the cube's faces
624  vtkMapper::SetResolveCoincidentTopologyToPolygonOffset();
625 
626  cube->GetXPlusFaceProperty()->SetColor(1.0, 0.0, 0.0);
627  cube->GetXPlusFaceProperty()->SetInterpolationToFlat();
628  cube->GetXMinusFaceProperty()->SetColor(1.0, 0.0, 0.0);
629  cube->GetXMinusFaceProperty()->SetInterpolationToFlat();
630  cube->GetYPlusFaceProperty()->SetColor(1.0, 1.0, 0.0);
631  cube->GetYPlusFaceProperty()->SetInterpolationToFlat();
632  cube->GetYMinusFaceProperty()->SetColor(1.0, 1.0, 0.0);
633  cube->GetYMinusFaceProperty()->SetInterpolationToFlat();
634  cube->GetZPlusFaceProperty()->SetColor(0.0, 1.0, 0.0);
635  cube->GetZPlusFaceProperty()->SetInterpolationToFlat();
636  cube->GetZMinusFaceProperty()->SetColor(0.0, 1.0, 0.0);
637  cube->GetZMinusFaceProperty()->SetInterpolationToFlat();
638 
641  tprop4->ShadowOn();
642  tprop4->SetFontFamilyToArial();
643 
644  // tprop.SetFontFamilyToTimes();
645  axes->GetXAxisCaptionActor2D()->SetCaptionTextProperty(tprop4);
646  //
649  tprop2->ShallowCopy(tprop4);
650  axes->GetYAxisCaptionActor2D()->SetCaptionTextProperty(tprop2);
651  //
654  tprop3->ShallowCopy(tprop4);
655  axes->GetZAxisCaptionActor2D()->SetCaptionTextProperty(tprop3);
656 
659  assembly->AddPart(cube);
660  assembly->AddPart(axes);
661  return assembly;
662 }
663 
664 // For log scale inversion
665 const double c_log10 = log(10.0);
666 
667 // structure for recursive display of labels
668 struct vlabel {
669  int yPos;
670  int yMin;
671  int yMax;
672  double val;
674  // default constructor
675  vlabel(int y, int y1, int y2, double v)
676  : yPos(y), yMin(y1), yMax(y2), val(v) {
677  assert(y2 >= y1);
678  }
679 };
680 
682 using vlabelSet = std::list<vlabel>;
683 
684 // Convert standard range to log scale
685 void ConvertToLogScale(ScalarType& dispMin, ScalarType& dispMax) {
686  ScalarType absDispMin = (dispMax < 0 ? std::min(-dispMax, -dispMin)
687  : std::max<ScalarType>(dispMin, 0));
688  ScalarType absDispMax = std::max(std::abs(dispMin), std::abs(dispMax));
689  dispMin = std::log10(
690  std::max(absDispMin, std::numeric_limits<ScalarType>::epsilon()));
691  dispMax = std::log10(
692  std::max(absDispMax, std::numeric_limits<ScalarType>::epsilon()));
693 }
694 
695 // helper: returns the neighbouring labels at a given position
696 //(first: above label, second: below label)
697 // Warning: set must be already sorted!
698 using vlabelPair = std::pair<vlabelSet::iterator, vlabelSet::iterator>;
699 
701  if (set.empty()) {
702  return vlabelPair(set.end(), set.end());
703  } else {
704  vlabelSet::iterator it1 = set.begin();
705  if (y < it1->yPos) {
706  return vlabelPair(set.end(), it1);
707  }
708  vlabelSet::iterator it2 = it1;
709  ++it2;
710  for (; it2 != set.end(); ++it2, ++it1) {
711  if (y <= it2->yPos) // '<=' to make sure the last label stays at
712  // the top!
713  return vlabelPair(it1, it2);
714  }
715  return vlabelPair(it1, set.end());
716  }
717 }
718 
719 bool PclTools::UpdateScalarBar(vtkAbstractWidget* widget,
720  const CC_DRAW_CONTEXT& CONTEXT) {
721  if (!widget) return false;
722  std::string viewID = CVTools::FromQString(CONTEXT.viewID);
723  const ccScalarField* sf = CONTEXT.sfColorScaleToDisplay;
724  if (!sf || !sf->getColorScale()) {
725  return false;
726  }
727 
728  bool logScale = sf->logScale();
729  bool symmetricalScale = sf->symmetricalScale();
730  bool alwaysShowZero = sf->isZeroAlwaysShown();
731 
732  // set of particular values
733  // DGM: we work with doubles for maximum accuracy
734  ccColorScale::LabelSet keyValues;
735  bool customLabels = false;
736  try {
737  ccColorScale::Shared colorScale = sf->getColorScale();
738  if (colorScale && colorScale->customLabels().size() >= 2) {
739  keyValues = colorScale->customLabels();
740 
741  if (alwaysShowZero) {
742  keyValues.insert(0.0);
743  }
744 
745  customLabels = true;
746  } else if (!logScale) {
747  keyValues.insert(sf->displayRange().min());
748  keyValues.insert(sf->displayRange().start());
749  keyValues.insert(sf->displayRange().stop());
750  keyValues.insert(sf->displayRange().max());
751  keyValues.insert(sf->saturationRange().min());
752  keyValues.insert(sf->saturationRange().start());
753  keyValues.insert(sf->saturationRange().stop());
754  keyValues.insert(sf->saturationRange().max());
755 
756  if (symmetricalScale)
757  keyValues.insert(-sf->saturationRange().max());
758 
759  if (alwaysShowZero) keyValues.insert(0.0);
760  } else {
761  ScalarType minDisp = sf->displayRange().min();
762  ScalarType maxDisp = sf->displayRange().max();
763  ConvertToLogScale(minDisp, maxDisp);
764  keyValues.insert(minDisp);
765  keyValues.insert(maxDisp);
766 
767  ScalarType startDisp = sf->displayRange().start();
768  ScalarType stopDisp = sf->displayRange().stop();
769  ConvertToLogScale(startDisp, stopDisp);
770  keyValues.insert(startDisp);
771  keyValues.insert(stopDisp);
772 
773  keyValues.insert(sf->saturationRange().min());
774  keyValues.insert(sf->saturationRange().start());
775  keyValues.insert(sf->saturationRange().stop());
776  keyValues.insert(sf->saturationRange().max());
777  }
778  } catch (const std::bad_alloc&) {
779  // not enough memory
780  return false;
781  }
782 
783  // magic fix (for infinite values!)
784  {
785  for (ccColorScale::LabelSet::iterator it = keyValues.begin();
786  it != keyValues.end(); ++it) {
787 #if defined(CV_WINDOWS) && defined(_MSC_VER)
788  if (!_finite(it->value))
789 #else
790  if (!std::isfinite(it->value))
791 #endif
792  {
793  bool minusInf = (it->value < 0);
794  keyValues.erase(it);
795  if (minusInf)
796  keyValues.insert({std::numeric_limits<ScalarType>::lowest(),
797  "-Inf"});
798  else
799  keyValues.insert(
800  {std::numeric_limits<ScalarType>::max(), "+Inf"});
801  it = keyValues.begin(); // restart the process (easier than
802  // trying to be intelligent here ;)
803  }
804  }
805  }
806 
807  // Internally, the elements in a set are already sorted
808  // std::sort(keyValues.begin(), keyValues.end());
809 
810  if (!customLabels && !sf->areNaNValuesShownInGrey()) {
811  // remove 'hidden' values
812  if (!logScale) {
813  for (ccColorScale::LabelSet::iterator it = keyValues.begin();
814  it != keyValues.end();) {
815  if (!sf->displayRange().isInRange(
816  static_cast<ScalarType>(it->value)) &&
817  (!alwaysShowZero ||
818  it->value != 0)) // we keep zero if the user has
819  // explicitely asked for it!
820  {
821  ccColorScale::LabelSet::iterator toDelete = it;
822  ++it;
823  keyValues.erase(toDelete);
824  } else {
825  ++it;
826  }
827  }
828  } else {
829  // convert actual display range to log scale
830  //(we can't do the opposite, otherwise we get accuracy/round-off
831  // issues!)
832  ScalarType dispMin = sf->displayRange().start();
833  ScalarType dispMax = sf->displayRange().stop();
834  ConvertToLogScale(dispMin, dispMax);
835 
836  for (ccColorScale::LabelSet::iterator it = keyValues.begin();
837  it != keyValues.end();) {
838  if (it->value >= dispMin && it->value <= dispMax) {
839  ++it;
840  } else {
841  ccColorScale::LabelSet::iterator toDelete = it;
842  ++it;
843  keyValues.erase(toDelete);
844  }
845  }
846  }
847  }
848 
849  std::vector<ccColorScale::Label> sortedKeyValues(keyValues.begin(),
850  keyValues.end());
851  double maxRange =
852  sortedKeyValues.back().value - sortedKeyValues.front().value;
853 
854  const ecvGui::ParamStruct& displayParams =
856  // default color: text color
857  const ecvColor::Rgbub& textColor = displayParams.textDefaultCol;
858  // histogram?
859  const ccScalarField::Histogram histogram = sf->getHistogram();
860  bool showHistogram = (displayParams.colorScaleShowHistogram && !logScale &&
861  histogram.maxValue != 0 && histogram.size() > 1);
862 
863  // display area
864  float renderZoom = CONTEXT.renderZoom;
865  QFont font = ecvDisplayTools::GetTextDisplayFont(); // takes rendering zoom
866  // into account!
867  const int strHeight =
868  static_cast<int>(displayParams.defaultFontSize *
869  renderZoom); // QFontMetrics(font).height() -->
870  // always returns the same value?!
871  const int scaleWidth =
872  static_cast<int>(displayParams.colorScaleRampWidth * renderZoom);
873  const int scaleMaxHeight =
874  (keyValues.size() > 1
875  ? std::max(
876  CONTEXT.glH - static_cast<int>(140 * renderZoom),
877  2 * strHeight)
878  : scaleWidth); // if 1 value --> we draw a cube
879 
880  // list of labels to draw
881  vlabelSet drawnLabels;
882  {
883  // add first label
884  drawnLabels.emplace_back(0, 0, strHeight,
885  sortedKeyValues.front().value);
886 
887  if (keyValues.size() > 1) {
888  // add last label
889  drawnLabels.emplace_back(scaleMaxHeight, scaleMaxHeight - strHeight,
890  scaleMaxHeight,
891  sortedKeyValues.back().value);
892  }
893 
894  // we try to display the other keyPoints (if any)
895  if (keyValues.size() > 2) {
896  assert(maxRange > 0.0);
897  const int minGap = strHeight;
898  for (size_t i = 1; i < keyValues.size() - 1; ++i) {
899  int yScale = static_cast<int>(
900  (sortedKeyValues[i].value - sortedKeyValues[0].value) *
901  scaleMaxHeight / maxRange);
902  vlabelPair nLabels = GetVLabelsAround(yScale, drawnLabels);
903 
904  assert(nLabels.first != drawnLabels.end() &&
905  nLabels.second != drawnLabels.end());
906  if ((nLabels.first == drawnLabels.end() ||
907  nLabels.first->yMax <= yScale - minGap) &&
908  (nLabels.second == drawnLabels.end() ||
909  nLabels.second->yMin >= yScale + minGap)) {
910  // insert it at the right place (so as to keep a sorted
911  // list!)
912  drawnLabels.insert(nLabels.second,
913  vlabel(yScale, yScale - strHeight / 2,
914  yScale + strHeight / 2,
915  sortedKeyValues[i].value));
916  }
917  }
918  }
919 
920  // now we recursively display labels for which we have some room left
921  if (!customLabels && drawnLabels.size() > 1) {
922  const int minGap = strHeight * 2;
923 
924  size_t drawnLabelsBefore = 0; // just to init the loop
925  size_t drawnLabelsAfter = drawnLabels.size();
926 
927  // proceed until no more label can be inserted
928  while (drawnLabelsAfter > drawnLabelsBefore) {
929  drawnLabelsBefore = drawnLabelsAfter;
930 
931  vlabelSet::iterator it1 = drawnLabels.begin();
932  vlabelSet::iterator it2 = it1;
933  ++it2;
934  for (; it2 != drawnLabels.end(); ++it2) {
935  if (it1->yMax + 2 * minGap < it2->yMin) {
936  // insert label
937  double val = (it1->val + it2->val) / 2.0;
938  int yScale = static_cast<int>(
939  (val - sortedKeyValues[0].value) *
940  scaleMaxHeight / maxRange);
941 
942  // insert it at the right place (so as to keep a sorted
943  // list!)
944  drawnLabels.insert(
945  it2, vlabel(yScale, yScale - strHeight / 2,
946  yScale + strHeight / 2, val));
947  }
948  it1 = it2;
949  }
950 
951  drawnLabelsAfter = drawnLabels.size();
952  }
953  }
954  }
955 
956  // start draw scalar bar!
957  vtkScalarBarWidgetCustom* scalarBarWidget =
958  vtkScalarBarWidgetCustom::SafeDownCast(widget);
959  if (!scalarBarWidget) {
960  return false;
961  }
962  vtkContext2DScalarBarActor* lutActor =
963  vtkContext2DScalarBarActor::SafeDownCast(
964  scalarBarWidget->GetScalarBarActor());
965  if (!lutActor) {
966  scalarBarWidget->Off();
967  return false;
968  }
969 
971  vtkScalarBarRepresentationCustom::SafeDownCast(
972  scalarBarWidget->GetRepresentation());
973  if (rep) {
974  rep->SetWindowLocation(
976  }
977 
979  {
980  // lut->SetNumberOfColors(scaleMaxHeight);
981  // lut->SetNodeValue();
982  // lut->SetNumberOfTableValues(static_cast<vtkIdType>(scaleMaxHeight));
983  // lut->SetTableRange(sortedKeyValues.front(), sortedKeyValues.back());
984  // lut->SetNumberOfIndexedColors(scaleMaxHeight);
985  // lut->SetUseAboveRangeColor(1);
986  // lut->SetUseBelowRangeColor(1);
987  // lut->SetDiscretize(1);
988  lut->Build();
989  // if (logScale)
990  //{
991  // lut->SetScaleToLog10();
992  // }
993  // else
994  //{
995  // lut->SetScaleToLinear();
996  // }
997 
998  for (int j = 0; j < scaleMaxHeight; ++j) {
999  double baseValue = sortedKeyValues.front().value +
1000  (j * maxRange) / scaleMaxHeight;
1001  double value = baseValue;
1002  if (logScale) {
1003  value = std::exp(value * c_log10);
1004  }
1005  const ecvColor::Rgb* col =
1006  sf->getColor(static_cast<ScalarType>(value));
1007  if (!col) {
1008  // special case: if we have user-defined labels, we want all the
1009  // labels to be displayed with their associated color
1010  if (customLabels) {
1011  assert(sf->getColorScale() &&
1012  !sf->getColorScale()->isRelative());
1013  col = sf->getColorScale()->getColorByValue(
1014  value, &ecvColor::lightGrey);
1015  } else {
1016  col = &ecvColor::lightGrey;
1017  }
1018  }
1019  assert(col);
1020  Eigen::Vector3d rgb = ecvColor::Rgb::ToEigen(*col);
1021  // lut->SetTableValue(j, rgb(0), rgb(1), rgb(2), 1);
1022  lut->AddRGBPoint(value, rgb(0), rgb(1), rgb(2));
1023  }
1024  }
1025 
1026  // Scalar field name
1027  const char* sfName = sf->getName();
1028  QString sfTitle(sfName);
1029  if (sfName) {
1030  if (sf->getGlobalShift() != 0) sfTitle += QString("[Shifted]");
1031  if (logScale) sfTitle += QString("[Log scale]");
1032  }
1033  lutActor->SetTitle(CVTools::FromQString(sfTitle).c_str());
1034  lutActor->SetLookupTable(lut);
1035  lutActor->SetOutlineScalarBar(showHistogram);
1036  lutActor->SetScalarBarLength(0.8);
1037  lutActor->SetScalarBarThickness(scaleWidth);
1038  lutActor->SetTitleJustification(VTK_TEXT_CENTERED);
1039  lutActor->SetForceHorizontalTitle(true);
1040  lutActor->SetDrawColorBar(1);
1041  lutActor->SetDrawTickLabels(1);
1042  // lutActor->SetNumberOfLabels(20);
1043  lutActor->SetNumberOfTicks(static_cast<int>(drawnLabels.size()));
1044  // lutActor->SetAutomaticLabelFormat(1);
1045  // lutActor->SetAutomaticAnnotations(1)
1046  // lutActor->SetAddRangeAnnotations(1);
1047  lutActor->SetTextPositionToPrecedeScalarBar();
1048  // lutActor->SetTextPositionToSucceedScalarBar();
1049  lutActor->GetLabelTextProperty()->SetFontSize(
1050  static_cast<int>(displayParams.defaultFontSize * renderZoom));
1051  lutActor->SetDrawFrame(1);
1052  Eigen::Vector3d col = ecvColor::Rgb::ToEigen(textColor);
1053  lutActor->GetFrameProperty()->SetColor(col(0), col(1), col(2));
1054  lutActor->GetAnnotationTextProperty()->SetColor(col(0), col(1), col(2));
1055  lutActor->GetTitleTextProperty()->SetColor(col(0), col(1), col(2));
1056  lutActor->GetLabelTextProperty()->SetColor(col(0), col(1), col(2));
1057  lutActor->GetFrameProperty()->SetLineWidth(2.0f * renderZoom);
1058  // lutActor->SetDrawBackground(1);
1059  // lutActor->GetBackgroundProperty()->SetColor(1., 1., 1.);
1060  // precision (same as color scale)
1061  const unsigned precision = displayParams.displayedNumPrecision;
1062  // format
1063  const char format = (sf->logScale() ? 'E' : 'f');
1064  QString formatInfo =
1065  QString("%1.%2%3").arg(precision).arg(precision).arg(format);
1066  const std::string labelFormat = "%" + formatInfo.toStdString();
1067  lutActor->SetRangeLabelFormat(labelFormat.c_str());
1068  scalarBarWidget->SetScalarBarActor(lutActor);
1069  scalarBarWidget->On();
1070  scalarBarWidget->Modified();
1071  return true;
1072 }
1073 
1075  const ccGLMatrixd& trans) {
1076  if (!polyData) {
1077  return false;
1078  }
1079  vtkSmartPointer<vtkPoints> points = polyData->GetPoints();
1080  if (!points || points->GetNumberOfPoints() == 0) {
1081  return false;
1082  }
1083 
1084  return TransformVtkPoints(points, trans);
1085 }
1086 
1088  const ccGLMatrixd& trans) {
1089  if (!points || points->GetNumberOfPoints() == 0) {
1090  return false;
1091  }
1092 
1093  for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i) {
1094  double* P = points->GetPoint(i);
1095  trans.apply(P);
1096  }
1097 
1098  return true;
1099 }
1100 
1102  double height,
1103  double depth,
1104  const ccGLMatrixd& trans) {
1106  if (!TransformPolyData(data, trans)) {
1107  CVLog::Error("[PclTools::CreateCube] Creating cube failed!");
1108  return nullptr;
1109  }
1110  return data;
1111 }
1112 
1114  double height,
1115  double depth) {
1117  cube->SetXLength(width);
1118  cube->SetYLength(height);
1119  cube->SetZLength(depth);
1120  cube->Update();
1121  return cube->GetOutput();
1122 }
1123 
1125  const cloudViewer::geometry::LineSet& lineset,
1126  bool useLineSource /* = true*/) {
1127  if (useLineSource) {
1128  return CreateLine(GetVtkPointsFromLineSet(lineset));
1129  } else {
1135  if (GetVtkPointsAndLinesFromLineSet(lineset, points, lines, colors)) {
1136  return CreateLine(points, lines, colors);
1137  } else {
1138  return nullptr;
1139  }
1140  }
1141 }
1142 
1143 namespace {
1144 using namespace pcl;
1145 
1148 void showCameras(pcl::texture_mapping::CameraVector cams,
1149  pcl::PointCloud<PointT>::Ptr& cloud) {
1150  // visualization object
1151  pcl::visualization::PCLVisualizer visu("cameras");
1152 
1153  // add a visual for each camera at the correct pose
1154  for (std::size_t i = 0; i < cams.size(); ++i) {
1155  // read current camera
1156  pcl::TextureMapping<PointT>::Camera cam = cams[i];
1157  double focal = cam.focal_length;
1158  double height = cam.height;
1159  double width = cam.width;
1160 
1161  // create a 5-point visual for each camera
1162  PointT p1, p2, p3, p4, p5;
1163  p1.x = 0;
1164  p1.y = 0;
1165  p1.z = 0;
1166  double dist = 0.75;
1167  double minX, minY, maxX, maxY;
1168  maxX = dist * tan(std::atan(width / (2.0 * focal)));
1169  minX = -maxX;
1170  maxY = dist * tan(std::atan(height / (2.0 * focal)));
1171  minY = -maxY;
1172  p2.x = minX;
1173  p2.y = minY;
1174  p2.z = dist;
1175  p3.x = maxX;
1176  p3.y = minY;
1177  p3.z = dist;
1178  p4.x = maxX;
1179  p4.y = maxY;
1180  p4.z = dist;
1181  p5.x = minX;
1182  p5.y = maxY;
1183  p5.z = dist;
1184  p1 = pcl::transformPoint(p1, cam.pose);
1185  p2 = pcl::transformPoint(p2, cam.pose);
1186  p3 = pcl::transformPoint(p3, cam.pose);
1187  p4 = pcl::transformPoint(p4, cam.pose);
1188  p5 = pcl::transformPoint(p5, cam.pose);
1189  std::stringstream ss;
1190  ss << "Cam #" << i + 1;
1191  visu.addText3D(ss.str(), p1, 0.1, 1.0, 1.0, 1.0, ss.str());
1192 
1193  ss.str("");
1194  ss << "camera_" << i << "line1";
1195  visu.addLine(p1, p2, ss.str());
1196  ss.str("");
1197  ss << "camera_" << i << "line2";
1198  visu.addLine(p1, p3, ss.str());
1199  ss.str("");
1200  ss << "camera_" << i << "line3";
1201  visu.addLine(p1, p4, ss.str());
1202  ss.str("");
1203  ss << "camera_" << i << "line4";
1204  visu.addLine(p1, p5, ss.str());
1205  ss.str("");
1206  ss << "camera_" << i << "line5";
1207  visu.addLine(p2, p5, ss.str());
1208  ss.str("");
1209  ss << "camera_" << i << "line6";
1210  visu.addLine(p5, p4, ss.str());
1211  ss.str("");
1212  ss << "camera_" << i << "line7";
1213  visu.addLine(p4, p3, ss.str());
1214  ss.str("");
1215  ss << "camera_" << i << "line8";
1216  visu.addLine(p3, p2, ss.str());
1217  }
1218 
1219  // add a coordinate system
1220  visu.addCoordinateSystem(1.0, "global");
1221 
1222  // add the mesh's cloud (colored on Z axis)
1223  pcl::visualization::PointCloudColorHandlerGenericField<PointT>
1224  color_handler(cloud, "z");
1225  visu.addPointCloud(cloud, color_handler, "cloud");
1226 
1227  // reset camera
1228  visu.resetCamera();
1229 
1230  // wait for user input
1231  visu.spin();
1232 }
1233 
1236 void showTextureMesh(const PCLTextureMesh::ConstPtr textureMesh) {
1237  // visualization object
1238  pcl::visualization::PCLVisualizer visu("TextureMesh");
1239 
1240  // add a coordinate system
1241  visu.addCoordinateSystem(1.0, "global");
1242 
1243  // add the mesh's cloud (colored on Z axis)
1244  visu.addTextureMesh(*textureMesh, "texture");
1245 
1246  // reset camera
1247  visu.resetCamera();
1248 
1249  // wait for user input
1250  visu.spin();
1251 }
1252 
1254 std::ifstream& GotoLine(std::ifstream& file, unsigned int num) {
1255  file.seekg(std::ios::beg);
1256  for (unsigned int i = 0; i < num - 1; ++i) {
1257  file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
1258  }
1259  return (file);
1260 }
1261 
1263 bool readCamPoseFile(std::string filename,
1264  pcl::TextureMapping<PointT>::Camera& cam) {
1265  std::ifstream myReadFile;
1266  myReadFile.open(filename.c_str(), std::ios::in);
1267  if (!myReadFile.is_open()) {
1268  CVLog::Error("Error opening file %s", filename.c_str());
1269  return false;
1270  }
1271  myReadFile.seekg(ios::beg);
1272 
1273  double val;
1274 
1275  // go to line 2 to read translations
1276  GotoLine(myReadFile, 2);
1277  myReadFile >> val;
1278  cam.pose(0, 3) = val; // TX
1279  myReadFile >> val;
1280  cam.pose(1, 3) = val; // TY
1281  myReadFile >> val;
1282  cam.pose(2, 3) = val; // TZ
1283 
1284  // go to line 7 to read rotations
1285  GotoLine(myReadFile, 7);
1286 
1287  myReadFile >> val;
1288  cam.pose(0, 0) = val;
1289  myReadFile >> val;
1290  cam.pose(0, 1) = val;
1291  myReadFile >> val;
1292  cam.pose(0, 2) = val;
1293 
1294  myReadFile >> val;
1295  cam.pose(1, 0) = val;
1296  myReadFile >> val;
1297  cam.pose(1, 1) = val;
1298  myReadFile >> val;
1299  cam.pose(1, 2) = val;
1300 
1301  myReadFile >> val;
1302  cam.pose(2, 0) = val;
1303  myReadFile >> val;
1304  cam.pose(2, 1) = val;
1305  myReadFile >> val;
1306  cam.pose(2, 2) = val;
1307 
1308  cam.pose(3, 0) = 0.0;
1309  cam.pose(3, 1) = 0.0;
1310  cam.pose(3, 2) = 0.0;
1311  cam.pose(3, 3) = 1.0; // Scale
1312 
1313  // go to line 12 to read camera focal length and size
1314  GotoLine(myReadFile, 12);
1315  myReadFile >> val;
1316  cam.focal_length = val;
1317  myReadFile >> val;
1318  cam.height = val;
1319  myReadFile >> val;
1320  cam.width = val;
1321 
1322  // close file
1323  myReadFile.close();
1324 
1325  return true;
1326 }
1327 
1328 bool ConvertToPCLCamPose(const camera::PinholeCameraParameters& pinholeCamera,
1329  pcl::TextureMapping<PointT>::Camera& cam) {
1330  // read camera extrinsic parameters
1331  Eigen::Matrix4f camera_pose = pinholeCamera.extrinsic_.cast<float>();
1332  cam.pose.matrix() = camera_pose;
1333 
1334  // read camera focal length and size
1335  auto focal_length = pinholeCamera.intrinsic_.GetFocalLength();
1336  auto center = pinholeCamera.intrinsic_.GetPrincipalPoint();
1337  cam.focal_length = focal_length.second;
1338  cam.focal_length_w = focal_length.first;
1339  cam.focal_length_h = focal_length.second;
1340  cam.height = pinholeCamera.intrinsic_.height_;
1341  cam.width = pinholeCamera.intrinsic_.width_;
1342  cam.center_w = center.first;
1343  cam.center_h = center.second;
1344 
1345  return true;
1346 }
1347 
1348 } // namespace
1349 
1350 PCLTextureMesh::Ptr PclTools::CreateTexturingMesh(
1351  const std::string& filePath,
1352  const camera::PinholeCameraTrajectory& cameraTrajectory,
1353  bool show_cameras,
1354  bool verbose) {
1355  if (verbose) {
1356  CVLog::Print("Loading mesh from file %s...", filePath.c_str());
1357  }
1358  PCLMesh::Ptr triangles(new PCLMesh);
1359  pcl::io::loadPolygonFilePLY(filePath.c_str(), *triangles);
1360 
1361  // Load textures and cameras poses and intrinsics
1362  if (verbose) {
1363  CVLog::Print("Loading textures and camera poses...");
1364  }
1365  pcl::texture_mapping::CameraVector my_cams;
1366  for (std::size_t i = 0; i < cameraTrajectory.parameters_.size(); i++) {
1367  pcl::TextureMapping<PointT>::Camera cam;
1368  ConvertToPCLCamPose(cameraTrajectory.parameters_[i], cam);
1369  cam.texture_file = cameraTrajectory.parameters_[i].texture_file_;
1370  my_cams.push_back(cam);
1371  }
1372 
1373  if (verbose) {
1374  CVLog::Print("Loaded %i textures.", my_cams.size());
1375  CVLog::Print("...Done.");
1376  }
1377 
1378  return CreateTexturingMesh(triangles, my_cams, show_cameras, verbose);
1379 }
1380 
1381 PCLTextureMesh::Ptr PclTools::CreateTexturingMesh(const std::string& filePath,
1382  bool show_cameras,
1383  bool verbose) {
1384  if (verbose) {
1385  CVLog::Print("Loading mesh from file %s...", filePath.c_str());
1386  }
1387 
1388  PCLMesh::Ptr triangles(new PCLMesh);
1389  pcl::io::loadPolygonFilePLY(filePath.c_str(), *triangles);
1390 
1391  // Load textures and cameras poses and intrinsics
1392  if (verbose) {
1393  CVLog::Print("Loading textures and camera poses...");
1394  }
1395  pcl::texture_mapping::CameraVector my_cams;
1396 
1397  std::string extension("txt");
1398  std::vector<std::string> file_names;
1400  file_names);
1401  for (std::size_t i = 0; i < file_names.size(); i++) {
1402  pcl::TextureMapping<PointT>::Camera cam;
1403  readCamPoseFile(file_names[i], cam);
1404  cam.texture_file =
1405  utility::filesystem::GetFileBaseName(file_names[i]) + ".png";
1406  my_cams.push_back(cam);
1407  }
1408 
1409  if (verbose) {
1410  CVLog::Print("Loaded %i textures.", my_cams.size());
1411  CVLog::Print("...Done.");
1412  }
1413 
1414  return CreateTexturingMesh(triangles, my_cams, show_cameras, verbose);
1415 }
1416 
1417 PCLTextureMesh::Ptr PclTools::CreateTexturingMesh(
1418  const PCLMesh::ConstPtr& triangles,
1419  const pcl::texture_mapping::CameraVector& cameras,
1420  bool show_cameras,
1421  bool verbose) {
1422  PointCloudT::Ptr cloud(new PointCloudT);
1423  FROM_PCL_CLOUD(triangles->cloud, *cloud);
1424 
1425  // Create the texturemesh object that will contain our UV-mapped mesh
1426  PCLTextureMesh::Ptr mesh(new PCLTextureMesh);
1427  mesh->cloud = triangles->cloud;
1428  std::vector<pcl::Vertices> polygons;
1429 
1430  // push faces into the texturemesh object
1431  polygons.resize(triangles->polygons.size());
1432  for (std::size_t i = 0; i < triangles->polygons.size(); ++i) {
1433  polygons[i] = triangles->polygons[i];
1434  }
1435  mesh->tex_polygons.push_back(polygons);
1436  if (verbose) {
1437  CVLog::Print(
1438  "[PclTools::CreateTexturingMesh] Input mesh contains %i faces "
1439  "and %i vertices",
1440  mesh->tex_polygons[0].size(),
1441  static_cast<std::size_t>(cloud->size()));
1442  CVLog::Print("...Done.");
1443  }
1444 
1445  // Display cameras to user
1446  if (show_cameras) {
1447  CVLog::Print(
1448  "Displaying cameras. Press \'q\' to continue texture mapping");
1449  showCameras(cameras, cloud);
1450  }
1451 
1452  // Create materials for each texture (and one extra for occluded faces)
1453  mesh->tex_materials.resize(cameras.size() + 1);
1454  for (std::size_t i = 0; i <= cameras.size(); ++i) {
1455  pcl::TexMaterial mesh_material;
1456  mesh_material.tex_Ka.r = 0.2f;
1457  mesh_material.tex_Ka.g = 0.2f;
1458  mesh_material.tex_Ka.b = 0.2f;
1459 
1460  mesh_material.tex_Kd.r = 0.8f;
1461  mesh_material.tex_Kd.g = 0.8f;
1462  mesh_material.tex_Kd.b = 0.8f;
1463 
1464  mesh_material.tex_Ks.r = 1.0f;
1465  mesh_material.tex_Ks.g = 1.0f;
1466  mesh_material.tex_Ks.b = 1.0f;
1467 
1468  mesh_material.tex_d = 1.0f;
1469  mesh_material.tex_Ns = 75.0f;
1470  mesh_material.tex_illum = 2;
1471 
1472  std::stringstream tex_name;
1473  tex_name << "material_" << i;
1474  tex_name >> mesh_material.tex_name;
1475 
1476  if (i < cameras.size()) {
1477  mesh_material.tex_file = cameras[i].texture_file;
1478  } else {
1479  mesh_material.tex_file = cameras[0].texture_file;
1480  }
1481 
1482  mesh->tex_materials[i] = mesh_material;
1483  }
1484 
1485  // Sort faces
1486  if (verbose) {
1487  CVLog::Print("Sorting faces by cameras...");
1488  }
1489  pcl::TextureMapping<PointT>
1490  tm; // TextureMapping object that will perform the sort
1491  tm.textureMeshwithMultipleCameras(*mesh, cameras);
1492 
1493  if (verbose) {
1494  CVLog::Print("Sorting faces by cameras done.");
1495  for (std::size_t i = 0; i <= cameras.size(); ++i) {
1496  CVLog::Print("Sub mesh %i contains %i faces and %i UV coordinates.",
1497  i, mesh->tex_polygons[i].size(),
1498  mesh->tex_coordinates[i].size());
1499  }
1500  }
1501 
1502  // compute normals for the mesh
1503  if (verbose) {
1504  CVLog::Print("Estimating normals...");
1505  }
1506  pcl::NormalEstimation<PointT, NormalT> n;
1507  CloudNormal::Ptr normals(new CloudNormal);
1508  pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>);
1509  tree->setInputCloud(cloud);
1510  n.setInputCloud(cloud);
1511  n.setSearchMethod(tree);
1512  n.setKSearch(20);
1513  n.compute(*normals);
1514 
1515  // Concatenate XYZ and normal fields
1516  PointCloudNormal::Ptr cloud_with_normals(new PointCloudNormal);
1517  pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
1518 
1519  TO_PCL_CLOUD(*cloud_with_normals, mesh->cloud);
1520 
1521  return mesh;
1522 }
std::string filename
filament::Texture::InternalFormat format
int width
int height
int points
math::float4 color
pcl::PointCloud< PointNT > PointCloudNormal
Definition: PCLCloud.h:25
pcl::PointCloud< PointT > PointCloudT
Definition: PCLCloud.h:17
pcl::PointXYZ PointT
Definition: PCLCloud.h:16
pcl::TextureMesh PCLTextureMesh
Definition: PCLCloud.h:33
pcl::PointCloud< NormalT > CloudNormal
Definition: PCLCloud.h:27
pcl::PolygonMesh PCLMesh
Definition: PCLCloud.h:31
#define TO_PCL_CLOUD
Definition: PCLConv.h:12
#define FROM_PCL_CLOUD
Definition: PCLConv.h:11
std::pair< vlabelSet::iterator, vlabelSet::iterator > vlabelPair
Definition: PclTools.cpp:698
void ConvertToLogScale(ScalarType &dispMin, ScalarType &dispMax)
Definition: PclTools.cpp:685
static vlabelPair GetVLabelsAround(int y, vlabelSet &set)
Definition: PclTools.cpp:700
const double c_log10
Definition: PclTools.cpp:665
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
static std::string FromQString(const QString &qs)
Definition: CVTools.cpp:100
Camera (projective) sensor.
const cloudViewer::geometry::LineSet & getNearPlane() const
const cloudViewer::geometry::LineSet & getSideLines() const
const cloudViewer::geometry::LineSet & getArrow() const
const cloudViewer::geometry::LineSet & getAxis() const
QSharedPointer< ccColorScale > Shared
Shared pointer type.
Definition: ecvColorScale.h:74
std::set< Label > LabelSet
Type of a list of custom labels.
virtual bool hasColors() const
Returns whether colors are enabled or not.
Ground-based Laser sensor.
Definition: ecvGBLSensor.h:26
const ecvOrientedBBox & getSensorHead() const
Definition: ecvGBLSensor.h:236
const cloudViewer::geometry::LineSet & getSensorLegLines() const
Definition: ecvGBLSensor.h:230
const cloudViewer::geometry::LineSet & getSensorAxis() const
Definition: ecvGBLSensor.h:233
void apply(float vec[3]) const
Applies transformation to a 3D vector (in place) - float version.
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
ScalarType stop() const
ScalarType min() const
bool isInRange(ScalarType val) const
Returns whether a value is inside range or not.
ScalarType max() const
ScalarType start() const
A scalar field associated to display-related parameters.
const ccColorScale::Shared & getColorScale() const
Returns associated color scale.
bool logScale() const
Returns whether scalar field is logarithmic or not.
double getGlobalShift() const
Returns the global shift (if any)
const Range & saturationRange() const
Access to the range of saturation values.
const Histogram & getHistogram() const
Returns associated histogram values (for display)
const Range & displayRange() const
Access to the range of displayed values.
const ecvColor::Rgb * getColor(ScalarType value) const
bool symmetricalScale() const
Returns whether the color scale s symmetrical or not.
bool areNaNValuesShownInGrey() const
Returns whether NaN values are displayed in gray or hidden.
bool isZeroAlwaysShown() const
Returns whether 0 should always appear in associated color ramp or not.
const char * getName() const
Returns scalar field name.
Definition: ScalarField.h:43
std::pair< double, double > GetFocalLength() const
Returns the focal length in a tuple of X-axis and Y-axis focal lengths.
std::pair< double, double > GetPrincipalPoint() const
Contains both intrinsic and extrinsic pinhole camera parameters.
PinholeCameraIntrinsic intrinsic_
PinholeCameraIntrinsic object.
Eigen::Matrix4d_u extrinsic_
Camera extrinsic parameters.
std::vector< PinholeCameraParameters > parameters_
List of PinholeCameraParameters objects.
LineSet define a sets of lines in 3D. A typical application is to display the point cloud corresponde...
Definition: LineSet.h:29
std::pair< Eigen::Vector3d, Eigen::Vector3d > GetLineCoordinate(size_t line_index) const
Returns the coordinates of the line at the given index.
Definition: LineSet.h:92
static std::shared_ptr< LineSet > CreateFromOrientedBoundingBox(const ecvOrientedBBox &box)
Factory function to create a LineSet from an OrientedBoundingBox.
std::vector< Eigen::Vector3d > points_
Points coordinates.
Definition: LineSet.h:156
std::vector< Eigen::Vector3d > colors_
RGB colors of lines.
Definition: LineSet.h:160
std::vector< Eigen::Vector2i > lines_
Lines denoted by the index of points forming the line.
Definition: LineSet.h:158
RGB color structure.
Definition: ecvColorTypes.h:49
constexpr static RgbTpl FromEigen(const Eigen::Vector3d &t)
Definition: ecvColorTypes.h:86
static Eigen::Vector3d ToEigen(const Type col[3])
Definition: ecvColorTypes.h:72
static QFont GetTextDisplayFont()
static const ecvGui::ParamStruct & GetDisplayParameters()
Returns current parameters for this display (const version)
virtual void SetScalarBarActor(vtkScalarBarActor *actor) override
virtual vtkScalarBarActor * GetScalarBarActor() override
double colors[3]
double normals[3]
std::pair< vlabelSet::iterator, vlabelSet::iterator > vlabelPair
std::list< vlabel > vlabelSet
A set of 'vlabel' structures.
GraphType data
Definition: graph_cut.cc:138
normal_z y
normal_z rgb
normal_z x
normal_z z
vtkSmartPointer< vtkPolyData > CreateCoordinateFromLineSet(const cloudViewer::geometry::LineSet &lineset)
Definition: PclTools.cpp:393
QPCL_ENGINE_LIB_API PCLTextureMesh::Ptr CreateTexturingMesh(const std::string &filePath, bool show_cameras=false, bool verbose=false)
Definition: PclTools.cpp:1381
vtkSmartPointer< vtkPolyData > CreatePolyDataFromLineSet(const cloudViewer::geometry::LineSet &lineset, bool useLineSource=true)
Definition: PclTools.cpp:1124
int GetDefaultScalarInterpolationForDataSet(vtkDataSet *data)
Definition: PclTools.h:73
vtkSmartPointer< vtkPoints > GetVtkPointsFromLineSet(const cloudViewer::geometry::LineSet &lineset)
Definition: PclTools.cpp:260
void AddPolyDataCell(vtkSmartPointer< vtkPolyData > polyData)
Definition: PclTools.cpp:371
vtkSmartPointer< vtkPolyData > CreateLine(vtkSmartPointer< vtkPoints > points)
Definition: PclTools.cpp:316
vtkSmartPointer< vtkPropAssembly > CreateCoordinate(double axesLength=1.5, const std::string &xLabel="x", const std::string &yLabel="y", const std::string &zLabel="z", const std::string &xPlus="R", const std::string &xMinus="L", const std::string &yPlus="A", const std::string &yMinus="P", const std::string &zPlus="I", const std::string &zMinus="S")
Definition: PclTools.cpp:581
bool TransformVtkPoints(vtkSmartPointer< vtkPoints > points, const ccGLMatrixd &trans)
Definition: PclTools.cpp:1087
bool GetVtkPointsAndLinesFromLineSet(const cloudViewer::geometry::LineSet &lineset, vtkSmartPointer< vtkPoints > points, vtkSmartPointer< vtkCellArray > lines, vtkSmartPointer< vtkUnsignedCharArray > colors)
Definition: PclTools.cpp:276
vtkSmartPointer< vtkPolyData > CreateCameraSensor(const ccCameraSensor *cameraSensor, const ecvColor::Rgb &lineColor, const ecvColor::Rgb &planeColor)
Definition: PclTools.cpp:464
void SetPolyDataColor(vtkSmartPointer< vtkPolyData > polyData, const ecvColor::Rgb &color, bool is_cell=false)
Definition: PclTools.cpp:347
bool UpdateScalarBar(vtkAbstractWidget *widget, const CC_DRAW_CONTEXT &CONTEXT)
Definition: PclTools.cpp:719
void AllocVtkUnstructuredGrid(vtkSmartPointer< vtkUnstructuredGrid > &polydata)
Allocate a new unstructured grid smartpointer. For internal use only.
Definition: PclTools.cpp:255
vtkSmartPointer< vtkPolyData > CreatePlane(const pcl::ModelCoefficients &coefficients, double x, double y, double z, double scale=1)
Definition: PclTools.cpp:517
vtkSmartPointer< vtkPolyData > CreateGBLSensor(const ccGBLSensor *gBLSensor)
Definition: PclTools.cpp:436
vtkSmartPointer< vtkPolyData > CreateCube(double width, double height, double depth, const ccGLMatrixd &trans)
Definition: PclTools.cpp:1101
void CreateActorFromVTKDataSet(const vtkSmartPointer< vtkDataSet > &data, vtkSmartPointer< vtkActor > &actor, bool use_scalars=true, bool use_vbos=false)
Internal method. Creates a vtk actor from a vtk polydata object.
Definition: PclTools.cpp:166
bool TransformPolyData(vtkSmartPointer< vtkPolyData > polyData, const ccGLMatrixd &trans)
Definition: PclTools.cpp:1074
bool ListFilesInDirectoryWithExtension(const std::string &directory, const std::string &extname, std::vector< std::string > &filenames)
Definition: FileSystem.cpp:567
std::string GetFileBaseName(const std::string &filename)
Definition: FileSystem.cpp:310
Generic file read and write utility for python interface.
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb lightGrey(static_cast< ColorCompType >(MAX *0.8), static_cast< ColorCompType >(MAX *0.8), static_cast< ColorCompType >(MAX *0.8))
constexpr Rgb green(0, MAX, 0)
constexpr Rgb yellow(MAX, MAX, 0)
Eigen::Matrix< Index, 2, 1 > Vector2i
Definition: knncpp.h:29
Display context.
float renderZoom
Current zoom (screen to file rendering mode)
int glH
GL screen height.
ccScalarField * sfColorScaleToDisplay
Currently displayed color scale (the corresponding scalar field in fact)
Simple histogram structure.
unsigned maxValue
Max histogram value.
GUI parameters.
unsigned displayedNumPrecision
Displayed numbers precision.
ecvColor::Rgbub textDefaultCol
Default text color.
unsigned defaultFontSize
Default displayed font size.
unsigned colorScaleRampWidth
Color scale ramp width (for display)
bool colorScaleShowHistogram
Color scale option: show histogram next to color ramp.
vlabel(int y, int y1, int y2, double v)
Definition: PclTools.cpp:675
#define VTK_CREATE(type, name)