ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
FastGlobalRegistrationFilter.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 
9 
10 #include "FastGlobalRegistration.h"
12 
13 // Local
14 #include <Utils/cc2sm.h>
15 #include <Utils/sm2cc.h>
16 
17 #include "PclUtils/PCLModules.h"
18 
19 // PCL
20 #include <pcl/features/fpfh_omp.h>
21 
22 // ECV_PLUGINS
23 #include <ecvMainAppInterface.h>
24 
25 // CV_DB_LIB
26 #include <ecvPointCloud.h>
27 
28 // Qt
29 #include <QMainWindow>
30 
31 // Boost
32 #include <boost/make_shared.hpp>
33 
34 // Error codes
35 static constexpr int NoNormals = -11;
36 
39  tr("Fast Global Registration"),
40  tr("Fast Global Registration, by Zhou et al."),
41  tr("Attempts to automatically register clouds (with normals) "
42  "without initial alignment"),
43  ":/toolbar/PclAlgorithms/icons/fastGlobalRegistration.png")),
44  m_alignedClouds(),
45  m_referenceCloud(nullptr),
46  m_featureRadius(0) {}
47 
49 
51  switch (errorCode) {
52  case NoNormals:
53  return tr("Clouds must have normals");
54  default:
55  // see below
56  break;
57  }
58 
59  return BasePclModule::getErrorMessage(errorCode);
60 }
61 
63  if (m_selected.size() < 2) {
64  return -11;
65  }
66 
67  for (ccHObject* entity : m_selected) {
68  // clouds only
69  if (!entity->isA(CV_TYPES::POINT_CLOUD)) return -11;
70  }
71 
72  return 1;
73 }
74 
75 static bool ComputeFeatures(ccPointCloud* cloud,
76  fgr::Features& features,
77  double radius) {
78  if (!cloud) {
79  assert(false);
80  return false;
81  }
82 
83  unsigned pointCount = cloud->size();
84  if (pointCount == 0) {
85  CVLog::Warning("Cloud is empty");
86  return false;
87  }
88 
89  pcl::PointCloud<pcl::PointNormal>::Ptr tmp_cloud =
90  cc2smReader(cloud).getAsPointNormal();
91  if (!tmp_cloud) {
92  CVLog::Warning("Failed to convert CC cloud to PCL cloud");
93  return false;
94  }
95 
96  pcl::PointCloud<pcl::FPFHSignature33> objectFeatures;
97  try {
98  pcl::FPFHEstimationOMP<pcl::PointNormal, pcl::PointNormal,
99  pcl::FPFHSignature33>
100  featEstimation;
101  featEstimation.setRadiusSearch(radius);
102  featEstimation.setInputCloud(tmp_cloud);
103  featEstimation.setInputNormals(tmp_cloud);
104  featEstimation.compute(objectFeatures);
105  } catch (...) {
106  CVLog::Warning("Failed to compute FPFH feature descriptors");
107  return false;
108  }
109 
110  try {
111  features.resize(pointCount, Eigen::VectorXf(33));
112  for (unsigned i = 0; i < pointCount; ++i) {
113  const pcl::FPFHSignature33& feature = objectFeatures.points[i];
114  memcpy(features[i].data(), feature.histogram, sizeof(float) * 33);
115  }
116  } catch (const std::bad_alloc&) {
117  CVLog::Warning("Not enough memory");
118  return false;
119  }
120 
121  return true;
122 }
123 
124 static bool ConverFromTo(const ccPointCloud& cloud, fgr::Points& points) {
125  unsigned pointCount = cloud.size();
126  if (pointCount == 0) {
127  CVLog::Warning("Cloud is empty");
128  return false;
129  }
130 
131  try {
132  points.resize(pointCount);
133  for (unsigned i = 0; i < pointCount; ++i) {
134  const CCVector3* P = cloud.getPoint(i);
135  points[i] = Eigen::Vector3f(P->x, P->y, P->z);
136  }
137  } catch (const std::bad_alloc&) {
138  CVLog::Warning("Not enough memory");
139  return false;
140  }
141 
142  return true;
143 }
144 
146  // just in case
147  m_alignedClouds.clear();
148  m_referenceCloud = nullptr;
149 
150  // get selected pointclouds
151  std::vector<ccPointCloud*> clouds;
152  clouds.reserve(m_selected.size());
153  for (ccHObject* entity : m_selected) {
154  // clouds only
155  if (entity->isA(CV_TYPES::POINT_CLOUD)) {
157  if (!cloud->hasNormals()) {
158  return;
159  }
160  clouds.push_back(cloud);
161  }
162  }
163  if (clouds.size() < 2) {
164  return;
165  }
166 
168  clouds, m_app ? m_app->getMainWindow() : nullptr);
169 
170  if (!dialog.exec()) {
171  return;
172  }
173 
174  // retrieve the reference clouds (= all the others)
176 
177  // retrieve the aligned clouds (= all the others)
178  m_alignedClouds.reserve(clouds.size() - 1);
179  for (ccPointCloud* cloud : clouds) {
180  if (cloud != m_referenceCloud) m_alignedClouds.push_back(cloud);
181  }
182 
183  // retrieve the feature radius
185 
186  dialog.saveParameters();
187 }
188 
190  if (m_alignedClouds.empty() || !m_referenceCloud || m_featureRadius <= 0) {
191  assert(false);
192  return -1;
193  }
194  if (!m_referenceCloud->hasNormals()) {
195  assert(false);
196  return -1;
197  }
198 
199  // compute the feature vector for the reference cloud
200  fgr::Features referenceFeatures;
201  if (!ComputeFeatures(m_referenceCloud, referenceFeatures,
202  m_featureRadius)) {
204  "Failed to compute the reference cloud feature descriptors");
205  return -53;
206  }
207 
208  // convert the reference cloud to vectors of Eigen::Vector3f
209  fgr::Points referencePoints;
210  if (!ConverFromTo(*m_referenceCloud, referencePoints)) {
211  return -1;
212  }
213 
214  // now for each aligned cloud
215  for (ccPointCloud* alignedCloud : m_alignedClouds) {
216  if (!alignedCloud->hasNormals()) {
217  assert(false);
218  return -1;
219  }
220 
221  // now compute the feature vector
222  fgr::Features alignedFeatures;
223  if (!ComputeFeatures(alignedCloud, alignedFeatures, m_featureRadius)) {
224  CVLog::Warning("Failed to compute the point feature descriptors");
225  return -1;
226  }
227 
228  // now convert the cloud to vectors of Eigen::Vector3f
229  fgr::Points alignedPoints;
230  if (!ConverFromTo(*alignedCloud, alignedPoints)) {
231  return -1;
232  }
233 
234  ccGLMatrix ccTrans;
235  try {
236  fgr::CApp fgrProcess;
237  fgrProcess.LoadFeature(referencePoints, referenceFeatures);
238  fgrProcess.LoadFeature(alignedPoints, alignedFeatures);
239  fgrProcess.NormalizePoints();
240  fgrProcess.AdvancedMatching();
241  if (!fgrProcess.OptimizePairwise(true)) {
243  "Failed to perform pair-wise optimization (not enough "
244  "points)");
245  return -1;
246  }
247 
248  Eigen::Matrix4f trans = fgrProcess.GetOutputTrans();
249  for (int i = 0; i < 16; ++i) {
250  // both ccGLMatrix and Eigen::Matrix4f should use column-major
251  // storage
252  ccTrans.data()[i] = trans.data()[i];
253  }
254  } catch (...) {
256  "Failed to determine the Global Registration matrix");
257  return -1;
258  }
259 
260  alignedCloud->setGLTransformation(ccTrans);
261 
262  CVLog::Print(
263  tr("[Fast Global Registration] Resulting matrix for cloud %1")
264  .arg(alignedCloud->getName()));
265  CVLog::Print(ccTrans.toString(12, ' ')); // full precision
266  CVLog::Print(
267  tr("Hint: copy it (CTRL+C) and apply it - or its inverse - on "
268  "any entity with the 'Edit > Apply transformation' tool"));
269  }
270 
271  return 1;
272 }
static constexpr int NoNormals
static bool ComputeFeatures(ccPointCloud *cloud, fgr::Features &features, double radius)
static bool ConverFromTo(const ccPointCloud &cloud, fgr::Points &points)
int points
Base abstract class for each implemented PCL filter.
Definition: BasePclModule.h:53
ecvMainAppInterface * m_app
Associated application interface.
virtual QString getErrorMessage(int errorCode)
Returns the error message corresponding to a given error code.
ccHObject::Container m_selected
Pointer to the main window.
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
Fast Global Registration dialog.
double getFeatureRadius() const
Returns the feature descritor comptation radius.
void saveParameters() const
Saves parameters for next call.
ccPointCloud * getReferenceCloud()
Returns the 'reference' cloud.
virtual int checkSelected() override
Checks if current selection is compliant with the filter.
std::vector< ccPointCloud * > m_alignedClouds
virtual QString getErrorMessage(int errorCode) override
Returns the error message corresponding to a given error code.
virtual int compute() override
Performs the actual filter job.
virtual void getParametersFromDialog() override
Collects parameters from the filter dialog (if openDialog is successful)
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
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
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool hasNormals() const override
Returns whether normals are enabled or not.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
unsigned size() const override
Definition: PointCloudTpl.h:38
const CCVector3 * getPoint(unsigned index) const override
virtual QMainWindow * getMainWindow()=0
Returns main window.
bool OptimizePairwise(bool decrease_mu=true)
void LoadFeature(const Points &pts, const Features &feat)
Eigen::Matrix4f GetOutputTrans() const
void AdvancedMatching(bool crossCheck=true, bool tupleConstraint=true)
@ POINT_CLOUD
Definition: CVTypes.h:104
std::vector< Eigen::Vector3f > Points
std::vector< Eigen::VectorXf > Features
PCL filter description.
Definition: BasePclModule.h:26