ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
TemplateAlignment.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 "TemplateAlignment.h"
9 
10 #include <Utils/cc2sm.h>
11 #include <Utils/sm2cc.h>
12 
13 #include "PclUtils/PCLModules.h"
15 
16 // CV_DB_LIB
17 #include <ecvMesh.h>
18 #include <ecvPointCloud.h>
19 
20 // ECV_PLUGINS
21 #include <ecvMainAppInterface.h>
22 
23 // QT
24 #include <QMainWindow>
25 
26 // SYSTEM
27 #include <iostream>
28 
31  tr("Template Alignment"),
32  tr("Template Alignment"),
33  tr("Template Alignment from clouds"),
34  ":/toolbar/PclAlgorithms/icons/templateAlignment.png")),
35  m_dialog(nullptr),
36  m_templateMatch(nullptr),
37  m_targetCloud(nullptr),
38  m_useVoxelGrid(false),
39  m_leafSize(0.005f),
40  m_normalRadius(0.02f),
41  m_featureRadius(0.02f),
42  m_minSampleDistance(0.05f),
43  m_maxCorrespondenceDistance(0.01f * 0.01f),
44  m_maxIterations(500),
45  m_maxThreadCount(1) {}
46 
48  // we must delete parent-less dialogs ourselves!
49  if (m_dialog && m_dialog->parent() == nullptr) delete m_dialog;
50  if (m_templateMatch) {
51  delete m_templateMatch;
52  }
53 }
54 
56  // do we have a selected cloud?
57  int have_cloud = isFirstSelectedCcPointCloud();
58  if (have_cloud != 1) return -11;
59 
60  return 1;
61 }
62 
64  // initialize the dialog object
66 
68  if (!m_dialog->exec()) return 0;
69 
71 
72  return 1;
73 }
74 
76  if (!m_dialog) return;
77 
78  // get PCLModules::FeatureCloud parameters
82  // get PCLModules::TemplateMatching parameters
86 
87  if (!m_templateMatch) {
88  m_templateMatch = new PCLModules::TemplateMatching();
89  }
90  m_templateMatch->clear();
91 
92  // PCLModules::TemplateMatching parameters
93  m_templateMatch->setmaxIterations(m_maxIterations);
94  m_templateMatch->setminSampleDis(m_minSampleDistance);
95  m_templateMatch->setmaxCorrespondenceDis(m_maxCorrespondenceDistance);
96 
97  // get the parameters from the dialog
99  m_useVoxelGrid = m_leafSize > 0 ? true : false;
100 
101  // get scales
102  m_scales.clear();
103  {
104  if (!m_dialog->getScales(m_scales)) {
105  m_app->dispToConsole(tr("Invalid scale parameters!"),
107  return;
108  }
109  // make sure values are in descending order!
110  std::sort(m_scales.begin(), m_scales.end(), std::greater<float>());
111  }
112 
115  m_templateClouds.clear();
116  {
117  if (!cloud1 && !cloud2) {
118  if (m_app)
119  m_app->dispToConsole(tr("At least one cloud (class #1 or #2) "
120  "was not defined!"),
122  return;
123  }
124  assert(cloud1 != cloud2);
125  if (cloud1) m_templateClouds.push_back(cloud1);
126  if (cloud2) m_templateClouds.push_back(cloud2);
127  }
128 
130 }
131 
133  if (!m_targetCloud || m_templateClouds.size() == 0) {
134  return -52;
135  }
136 
138  return -52;
139  }
140  return 1;
141 }
142 
144  // 1. Load the object templates
145  for (size_t i = 0; i < m_templateClouds.size(); ++i) {
146  PointCloudT::Ptr cloudFiltered =
147  cc2smReader(m_templateClouds[i]).getXYZ2();
148  if (cloudFiltered->width * cloudFiltered->height != 0) {
149  // Assign to the template FeatureCloud
150  PCLModules::FeatureCloud template_cloud;
151 
152  template_cloud.setmaxThreadCount(m_maxThreadCount);
153  template_cloud.setNormalRadius(m_normalRadius);
154  template_cloud.setFeatureRadius(m_featureRadius);
155  template_cloud.setInputCloud(cloudFiltered);
156  m_templateMatch->addTemplateCloud(template_cloud);
157  }
158  }
159 
160  // 2. Load the target cloud
161  {
162  PointCloudT::Ptr cloudFiltered = cc2smReader(m_targetCloud).getXYZ2();
163  // down sampling the template point cloud 1
164  if (m_useVoxelGrid) {
165  PointCloudT::Ptr tempCloud(new PointCloudT);
166  if (!PCLModules::VoxelGridFilter<PointT>(cloudFiltered, tempCloud,
168  m_leafSize)) {
169  return -1;
170  }
171  cloudFiltered = tempCloud;
172  }
173 
174  if (!cloudFiltered) {
175  return -1;
176  }
177 
178  // Assign to the target FeatureCloud
179  PCLModules::FeatureCloud target_cloud;
180  target_cloud.setmaxThreadCount(m_maxThreadCount);
181  target_cloud.setNormalRadius(m_normalRadius);
182  target_cloud.setFeatureRadius(m_featureRadius);
183 
184  target_cloud.setInputCloud(cloudFiltered);
185  m_templateMatch->setTargetCloud(target_cloud);
186  }
187 
188  // 3. Find the best template alignment
189  PCLModules::TemplateMatching::Result best_alignment;
190  int best_index = m_templateMatch->findBestAlignment(best_alignment);
191  const PCLModules::FeatureCloud* best_template =
192  m_templateMatch->getTemplateCloud(best_index);
193  if (!best_template) {
194  return -1;
195  }
196 
197  // Print the alignment fitness score (values less than 0.00002 are good)
198  CVLog::Print(
199  tr("(values less than 0.00002 are good) Best fitness score: %1")
200  .arg(best_alignment.fitness_score));
201 
202  ccGLMatrixd transMat(best_alignment.final_transformation.data());
203 
204  // Save the aligned template for visualization
205  PCLCloud out_cloud_sm;
206  TO_PCL_CLOUD(*best_template->getPointCloud(), out_cloud_sm);
207  if (out_cloud_sm.height * out_cloud_sm.width == 0) {
208  // cloud is empty
209  return -53;
210  }
211 
212  ccPointCloud* out_cloud_cc = pcl2cc::Convert(out_cloud_sm);
213  if (!out_cloud_cc) {
214  // conversion failed (not enough memory?)
215  return -1;
216  }
217 
218  // apply transformation and colors
219  applyTransformation(out_cloud_cc, transMat);
220  {
221  out_cloud_cc->setRGBColor(ecvColor::darkBlue);
222  out_cloud_cc->showColors(true);
223  out_cloud_cc->showSF(false);
224  }
225 
226  // copy global shift & scale and set name
227  ccPointCloud* cloud = nullptr;
228  {
229  if (best_index == 0) {
230  cloud = m_dialog->getTemplate1Cloud();
231  } else if (best_index == 1) {
232  cloud = m_dialog->getTemplate2Cloud();
233  }
234 
235  if (cloud) {
236  QString outName = cloud->getName() + "-alignment";
237  out_cloud_cc->setName(outName);
238  // copy global shift & scale
239  out_cloud_cc->setGlobalScale(cloud->getGlobalScale());
240  out_cloud_cc->setGlobalShift(cloud->getGlobalShift());
241 
242  if (cloud->getParent()) cloud->getParent()->addChild(out_cloud_cc);
243  }
244  }
245 
246  emit newEntity(out_cloud_cc);
247 
248  return 1;
249 }
250 
252  const ccGLMatrixd& mat) {
253  entity->setGLTransformation(ccGLMatrix(mat.data()));
254  // DGM FIXME: we only test the entity own bounding box (and we update its
255  // shift & scale info) but we apply the transformation to all its children?!
257  CVLog::Print(tr("[ApplyTransformation] Applied transformation matrix:"));
258  CVLog::Print(mat.toString(12, ' ')); // full precision
259  CVLog::Print(
260  tr("Hint: copy it (CTRL+C) and apply it - or its inverse - on any "
261  "entity with the 'Edit > Apply transformation' tool"));
262 }
263 
264 QString TemplateAlignment::getErrorMessage(int errorCode) {
265  switch (errorCode) {
266  // THESE CASES CAN BE USED TO OVERRIDE OR ADD FILTER-SPECIFIC ERRORS
267  // CODES ALSO IN DERIVED CLASSES DEFULAT MUST BE ""
268 
269  case -51:
270  return tr(
271  "Selected entity does not have any suitable scalar field "
272  "or RGB.");
273  case -52:
274  return tr(
275  "Wrong Parameters. One or more parameters cannot be "
276  "accepted");
277  case -53:
278  return tr(
279  "Template Alignment does not returned any point. Try "
280  "relaxing your parameters");
281  }
282 
283  return BasePclModule::getErrorMessage(errorCode);
284 }
Base abstract class for each implemented PCL filter.
Definition: BasePclModule.h:53
int isFirstSelectedCcPointCloud()
Returns 1 if the first selected object is a ccPointCloud.
void newEntity(ccHObject *)
Signal emitted when a new entity is created by the filter.
ecvMainAppInterface * m_app
Associated application interface.
virtual QString getErrorMessage(int errorCode)
Returns the error message corresponding to a given error code.
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
CANUPO plugin's training dialog.
float getMaxCorrespondenceDistance() const
Returns the Maximum Correspondence Distance.
float getFeatureRadius() const
Returns the Feature Radius.
float getMinSampleDistance() const
Returns the Minimum Sample Distance.
bool getScales(std::vector< float > &scales) const
Returns input scales.
int getMaxIterations() const
Returns the Maximum Iterations.
void saveParamsToPersistentSettings()
Saves parameters to persistent settings.
ccPointCloud * getTemplate1Cloud()
Get template #1 point cloud.
float getNormalRadius() const
Returns the Normal Radius.
ccPointCloud * getTemplate2Cloud()
Get template #2 point cloud.
ccPointCloud * getEvaluationCloud()
Get evaluation point cloud.
void applyTransformation(ccHObject *entity, const ccGLMatrixd &mat)
virtual int checkSelected()
Checks if current selection is compliant with the filter.
virtual int openInputDialog()
virtual void getParametersFromDialog()
Collects parameters from the filter dialog (if openDialog is successful)
std::vector< float > m_scales
virtual int checkParameters()
virtual QString getErrorMessage(int errorCode)
Returns the error message corresponding to a given error code.
virtual int compute()
Performs the actual filter job.
TemplateAlignmentDialog * m_dialog
std::vector< ccPointCloud * > m_templateClouds
ccPointCloud * m_targetCloud
PCLModules::TemplateMatching * m_templateMatch
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
virtual void setGLTransformation(const ccGLMatrix &trans)
Associates entity with a GL transformation (rotation + translation)
QString toString(int precision=12, QChar separator=' ') const
Returns matrix as a string.
T * data()
Returns a pointer to internal data.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool setRGBColor(ColorCompType r, ColorCompType g, ColorCompType b)
Set a unique color for the whole cloud (shortcut)
virtual void setGlobalScale(double scale)
virtual void setGlobalShift(double x, double y, double z)
Sets shift applied to original coordinates (information storage only)
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
constexpr Rgb darkBlue(0, 0, MAX/2)
PCL filter description.
Definition: BasePclModule.h:26