ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
NurbsCurveFitting.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 "NurbsCurveFitting.h"
9 
10 #include <Utils/cc2sm.h>
11 #include <Utils/sm2cc.h>
12 
13 #include "PclUtils/PCLModules.h"
14 #include "Tools/Common/CurveFitting.h"
15 #include "Tools/Common/ecvTools.h" // must below above three
17 
18 // CV_DB_LIB
19 #include <ecvPlane.h>
20 #include <ecvPointCloud.h>
21 #include <ecvPolyline.h>
22 
23 // ECV_PLUGINS
24 #include <ecvMainAppInterface.h>
25 
26 // QT
27 #include <QMainWindow>
28 
29 // SYSTEM
30 #include <iostream>
31 
34  tr("BSpline Curve Fitting"),
35  tr("BSpline Curve Fitting"),
36  tr("BSpline Curve Fitting from clouds"),
37  ":/toolbar/PclAlgorithms/icons/bspline_curve.png")),
38  m_dialog(0),
39  m_order(3),
40  m_useVoxelGrid(false),
41  m_exportProjectedCloud(false),
42  m_curveFitting3D(true),
43  m_closed(false),
44  m_curveResolution(8),
45  m_minimizationType(0),
46  m_controlPoints(10),
47  m_curveRscale(1.0f),
48  m_curveSmoothness(0.000001f) {}
49 
51  // we must delete parent-less dialogs ourselves!
52  if (m_dialog && m_dialog->parent() == 0) delete m_dialog;
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
65  if (!m_dialog)
66  m_dialog =
68 
69  if (!m_dialog->exec()) return 0;
70 
71  return 1;
72 }
73 
75  if (!m_dialog) return;
76 
77  // get the parameters from the dialog
78  m_minimizationType = m_dialog->minimizationMethodsCombo->currentIndex();
79  m_order = m_dialog->orderSpinBox->value();
80  m_curveResolution = m_dialog->curveResolutionSpinBox->value();
81  m_controlPoints = m_dialog->controlPointsSpinBox->value();
82  m_useVoxelGrid = m_dialog->useVoxelGridCheckBox->isChecked();
84  m_dialog->exportProjectedCloudCheckBox->isChecked();
85  m_closed = m_dialog->closedCurveCheckBox->isChecked();
86  m_curveFitting3D = m_dialog->curve3DFittingCheckBox->isChecked();
89  m_curveRscale = static_cast<float>(m_dialog->curveRscaleSpinBox->value());
91  static_cast<float>(m_dialog->curveSmoothnessSpinBox->value());
92 }
93 
95 
98  if (!cloud) return -1;
99 
100  PointCloudRGB::Ptr outCurve(new PointCloudRGB);
101  PointCloudT::Ptr xyzCloud = cc2smReader(cloud).getXYZ2();
102  if (!xyzCloud) return -1;
103 
104  // step 1. voxel grid filter
105  if (m_useVoxelGrid) {
106  double leafSize =
107  3 * PCLModules::ComputeCloudResolution<PointT>(xyzCloud);
108  if (leafSize > 0) {
109  PointCloudT::Ptr tempCloud(new PointCloudT);
110  if (!PCLModules::VoxelGridFilter<PointT>(
111  xyzCloud, tempCloud, leafSize, leafSize, leafSize)) {
112  return -1;
113  }
114  xyzCloud = tempCloud;
115  }
116  }
117 
118 #if 0
119  PointCloudT::Ptr outCurve(new PointCloudT);
120  CurveFitting fit;
121  fit.setInputcloud(xyzCloud);
122  PointCloudT::Ptr point_mean(new PointCloudT);
123  std::vector<double> x_mean, y_mean, z_mean;
124  double a, b, c;
125  // 0.5 represent step length, -1
126  fit.grid_mean_xyz(0.5, -1, x_mean, y_mean, z_mean, point_mean);
127  fit.polynomial3D_fitting(x_mean, y_mean, z_mean, a, b, c);
128  fit.getPolynomial3D(outCurve, 0.01);
129 
130  if (outCurve->points.size() > 1)
131  {
132  PointT sp = outCurve->points[0];
133  CVLog::Print(tr("Start Point: X1= %1, Y1= %2, Z1= %3").arg(sp.x).arg(sp.y).arg(sp.z));
134  PointT ep = outCurve->points[outCurve->points.size() -1];
135  CVLog::Print(tr("End Point: X2= %1, Y2= %2, Z2= %3").arg(ep.x).arg(ep.y).arg(ep.z));
136  CVLog::Print(tr("space polynomial curve equation: z = %1 * (x^2+y^2) + %2 * sqrt(x^2+y^2) + %3").arg(a).arg(b).arg(c));
137  }
138  else
139  {
140  return -1;
141  }
142 #endif
143 
144  // step 2. get cloud plane matrix with x-y plane
145  ccGLMatrix makeZPosMatrix;
146  if (!m_closed || !m_curveFitting3D) {
147  double rms = 0.0;
148  CCVector3 C, N;
149  ccPlane* pPlane = ccPlane::Fit(cloud, &rms);
150  if (!pPlane) {
151  return -1;
152  }
153  CVLog::Print(tr("\t- plane fitting RMS: %1").arg(rms));
154  N = pPlane->getNormal();
155  delete pPlane;
156  pPlane = nullptr;
157  // C = *cloudViewer::Neighbourhood(cloud).getGravityCenter();
158  makeZPosMatrix = ccGLMatrix::FromToRotation(N, CCVector3(0, 0, PC_ONE));
159  // CCVector3 Gt = C;
160  // makeZPosMatrix.applyRotation(Gt);
161  // makeZPosMatrix.setTranslation(C - Gt);
162  Eigen::Matrix4f xoyTransformation(makeZPosMatrix.data());
163  pcl::transformPointCloud(*xyzCloud, *xyzCloud, xoyTransformation);
164  }
165 
166 #if defined(WITH_PCL_NURBS)
167  // step 3. transform cloud to x-y plane according makeZPosMatrix
168  PointCloudT::Ptr zoyCloud(new PointCloudT);
169  if (m_curveFitting3D) {
170  if (!PCLModules::BSplineCurveFitting3D<PointT>(
171  xyzCloud, outCurve, m_order, m_controlPoints,
173  m_closed)) {
174  return -1;
175  }
176  if (outCurve->points.size() == 0) {
177  return -53;
178  }
179  } else {
180  // step 4. init parameters and reconstruction on x-y plane
181  PCLModules::CurveFittingMethod fittingMethod =
182  (PCLModules::CurveFittingMethod)m_minimizationType;
183  if (!PCLModules::BSplineCurveFitting2D<PointT>(
184  xyzCloud, fittingMethod, outCurve, m_order, m_controlPoints,
186  m_closed)) {
187  return -1;
188  }
189  if (outCurve->points.size() == 0) {
190  return -53;
191  }
192  }
193 #else
194  CVLog::Print(
195  tr("[NurbsCurveFitting] PCL not supported with nurbs, please "
196  "rebuild pcl with -DBUILD_surface_on_nurbs=ON again"));
197  return -1;
198 #endif
199 
200  if (!m_closed || !m_curveFitting3D) {
201  // step 5. transform cloud from x-y plane to original plane
202  Eigen::Matrix4f inverseXoyTransformation(
203  makeZPosMatrix.inverse().data());
204  pcl::transformPointCloud(*outCurve, *outCurve,
205  inverseXoyTransformation);
206  }
207 
208  // convert output fitting curve to polyline
209  ccPolyline* curvePoly = nullptr;
210  {
211  PCLCloud::Ptr curve_sm(new PCLCloud);
212  TO_PCL_CLOUD(*outCurve, *curve_sm);
213  curvePoly = ecvTools::GetPolylines(curve_sm, "fitting-curve", m_closed);
214  if (!curvePoly) {
215  return -53;
216  }
217  CVLog::Print(tr("[NurbsCurveFitting] curve : %1 point(s)")
218  .arg(curvePoly->size()));
219  }
220 
221  ccHObject* outObject = nullptr;
223  outObject = new ccHObject(cloud ? cloud->getName()
224  : "" + QString(" [curve-fitting]-"));
225  if (outObject) {
226  outObject->setVisible(true);
227  PCLCloud curve_sm;
228  TO_PCL_CLOUD(*xyzCloud, curve_sm);
229  ccPointCloud* projectedCloud = pcl2cc::Convert(curve_sm);
230  if (projectedCloud) {
231  projectedCloud->setName("projected-xoy");
232  if (cloud) {
233  // copy global shift & scale
234  projectedCloud->setGlobalScale(cloud->getGlobalScale());
235  projectedCloud->setGlobalShift(cloud->getGlobalShift());
236  }
237  outObject->addChild(projectedCloud);
238  }
239 
240  outObject->addChild(curvePoly);
241  }
242 
243  if (outObject->getChildrenNumber() == 0) {
244  delete outObject;
245  outObject = nullptr;
246  return -1;
247  }
248  } else {
249  outObject = curvePoly;
250  }
251 
252  if (outObject) {
253  if (cloud->getParent()) cloud->getParent()->addChild(outObject);
254  emit newEntity(outObject);
255  } else {
256  return -1;
257  }
258 
259  return 1;
260 }
261 
262 QString NurbsCurveFitting::getErrorMessage(int errorCode) {
263  switch (errorCode) {
264  // THESE CASES CAN BE USED TO OVERRIDE OR ADD FILTER-SPECIFIC ERRORS
265  // CODES ALSO IN DERIVED CLASSES DEFULAT MUST BE ""
266 
267  case -51:
268  return tr(
269  "Selected entity does not have any suitable scalar field "
270  "or RGB.");
271  case -52:
272  return tr(
273  "Wrong Parameters. One or more parameters cannot be "
274  "accepted");
275  case -53:
276  return tr(
277  "Nurbs Curve Fitting does not returned any point. Try "
278  "relaxing your parameters");
279  }
280 
281  return BasePclModule::getErrorMessage(errorCode);
282 }
constexpr PointCoordinateType PC_ONE
'1' as a PointCoordinateType value
Definition: CVConst.h:67
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
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.
ccPointCloud * getSelectedEntityAsCCPointCloud() const
Returns the first selected entity as a ccPointCloud.
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
virtual int checkParameters()
virtual int compute()
Performs the actual filter job.
virtual void getParametersFromDialog()
Collects parameters from the filter dialog (if openDialog is successful)
virtual QString getErrorMessage(int errorCode)
Returns the error message corresponding to a given error code.
NurbsCurveFittingDlg * m_dialog
virtual int checkSelected()
Checks if current selection is compliant with the filter.
virtual int openInputDialog()
virtual void setVisible(bool state)
Sets entity visibility.
static ccGLMatrixTpl< float > FromToRotation(const Vector3Tpl< float > &from, const Vector3Tpl< float > &to)
Creates a transformation matrix that rotates a vector to another.
T * data()
Returns a pointer to internal data.
ccGLMatrixTpl< T > inverse() const
Returns inverse transformation.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
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.
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
Plane (primitive)
Definition: ecvPlane.h:18
static ccPlane * Fit(cloudViewer::GenericIndexedCloudPersist *cloud, double *rms=0)
Fits a plane primitive on a cloud.
CCVector3 getNormal() const override
Returns the entity normal.
Definition: ecvPlane.h:73
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
Colored polyline.
Definition: ecvPolyline.h:24
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.
unsigned size() const override
Returns the number of points.
virtual QWidget * getActiveWindow()=0
PCL filter description.
Definition: BasePclModule.h:26