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) {
89  }
91 
92  // PCLModules::TemplateMatching parameters
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 =
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
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 }
pcl::PointCloud< PointT > PointCloudT
Definition: PCLCloud.h:17
pcl::PCLPointCloud2 PCLCloud
Definition: PCLCloud.h:34
#define TO_PCL_CLOUD
Definition: PCLConv.h:12
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
void setmaxThreadCount(int maxThreadCount)
Definition: PCLModules.h:1465
void setNormalRadius(float normalRadius)
Definition: PCLModules.h:1461
void setInputCloud(PointCloudT::Ptr xyz)
Definition: PCLModules.h:1470
void setFeatureRadius(float featureRadius)
Definition: PCLModules.h:1462
PointCloudT::Ptr getPointCloud() const
Definition: PCLModules.h:1479
void setTargetCloud(FeatureCloud &target_cloud)
Definition: PCLModules.cpp:399
void addTemplateCloud(FeatureCloud &template_cloud)
Definition: PCLModules.h:1538
void setminSampleDis(float minSampleDistance)
Definition: PCLModules.h:1524
void setmaxCorrespondenceDis(float maxCorrespondenceDistance)
Definition: PCLModules.h:1527
int findBestAlignment(TemplateMatching::Result &result)
Definition: PCLModules.cpp:432
void setmaxIterations(int maxIterations)
Definition: PCLModules.h:1530
FeatureCloud * getTemplateCloud(int index)
Definition: PCLModules.h:1543
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
CC to PCL cloud converter.
Definition: cc2sm.h:43
pcl::PointCloud< pcl::PointXYZ >::Ptr getXYZ2() const
Definition: cc2sm.cpp:218
virtual void setGLTransformation(const ccGLMatrix &trans)
Associates entity with a GL transformation (rotation + translation)
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
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)
Definition: ecvHObject.cpp:948
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
Definition: ecvHObject.cpp:534
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 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 void setGlobalScale(double scale)
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
static ccMesh * Convert(PCLTextureMesh::ConstPtr textureMesh)
Converts a PCL point cloud to a ccPointCloud.
template int VoxelGridFilter< PointT >(const PointCloudT::ConstPtr inCloud, PointCloudT::Ptr outcloud, const float &leafSizeX, const float &leafSizeY, const float &leafSizeZ)
constexpr Rgb darkBlue(0, 0, MAX/2)
PCL filter description.
Definition: BasePclModule.h:26