GteApprCylinder3.h
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.3.1 (2016/11/20)
7 
8 #pragma once
9 
14 #include <algorithm>
15 #include <vector>
16 #include <thread>
17 
18 // The algorithm for least-squares fitting of a point set by a cylinder is
19 // described in
20 // http://www.geometrictools.com/Documentation/CylinderFitting.pdf
21 // This document shows how to compute the cylinder radius r and the cylinder
22 // axis as a line C+t*W with origin C, unit-length direction W, and any
23 // real-valued t. The implementation here adds one addition step. It
24 // projects the point set onto the cylinder axis, computes the bounding
25 // t-interval [tmin,tmax] for the projections, and sets the cylinder center
26 // to C + 0.5*(tmin+tmax)*W and the cylinder height to tmax-tmin.
27 
28 namespace gte
29 {
30 
31 template <typename Real>
33 {
34 public:
35  // Construction.
36  //
37  // To run in the main process, choose numThreads = 0. To run
38  // multithreaded, choose numThreads > 0.
39  //
40  // To search the hemisphere for a minimum, choose numThetaSamples and
41  // numPhiSamples to be positive (and preferably large). These are used
42  // to generate a hemispherical grid of samples to be evaluated to find
43  // the cylinder axis-direction W. If the grid samples is quite large
44  // and the number of points to be fitted is large, you most likely will
45  // want to run multithreaded.
46  //
47  // Choose numThetaSamples and numPhiSamples to be zero if you want the
48  // fitter to use the eigenvector corresponding to the largest eigenvalue
49  // of the covariance matrix as the cylinder axis direction. In this
50  // case, multithreading is not used because there is much less work to do.
51  ApprCylinder3(unsigned int numThreads, unsigned int numThetaSamples,
52  unsigned int numPhiSamples);
53 
54  // The algorithm must estimate 6 parameters, so the number of points should
55  // be at least 6 but preferably larger. If the number of points is smaller
56  // than 6, the returned 'Real' value is std::numeric_limits<Real>::max();
57  // otherwise, the returned 'Real' value is the root-mean-square of the
58  // least-squares error: sqrt((sum_{i=0}^{N-1} error[i]^2)/N).
59  Real operator()(unsigned int numPoints, Vector3<Real> const* points,
60  Cylinder3<Real>& cylinder);
61 
62 private:
63  Real ComputeUsingCovariance(Vector3<Real>& minPC, Vector3<Real>& minW, Real& minRSqr);
64  Real ComputeSingleThreaded(Vector3<Real>& minPC, Vector3<Real>& minW, Real& minRSqr);
65  Real ComputeMultiThreaded(Vector3<Real>& minPC, Vector3<Real>& minW, Real& minRSqr);
66  Real Error(Vector3<Real> const& W, Vector3<Real>& PC, Real& rsqr) const;
67 
68  unsigned int mNumThreads;
69  unsigned int mNumThetaSamples;
70  unsigned int mNumPhiSamples;
71 
72  // A copy of the input points but translated by their average for
73  // numerical robustness.
74  std::vector<Vector3<Real>> mX;
76 };
77 
78 template <typename Real>
79 ApprCylinder3<Real>::ApprCylinder3(unsigned int numThreads, unsigned int numThetaSamples,
80  unsigned int numPhiSamples)
81  :
82  mNumThreads(numThreads),
83  mNumThetaSamples(numThetaSamples),
84  mNumPhiSamples(numPhiSamples),
85  mInvNumPoints((Real)0)
86 {
87 }
88 
89 template <typename Real>
90 Real ApprCylinder3<Real>::operator()(unsigned int numPoints, Vector3<Real> const* points,
91  Cylinder3<Real>& cylinder)
92 {
93  if (numPoints < 6 || !points)
94  {
95  mX.clear();
96  mInvNumPoints = 0;
97 
98  cylinder.axis.origin = Vector3<Real>::Zero();
99  cylinder.axis.direction = Vector3<Real>::Zero();
100  cylinder.radius = (Real)0;
101  cylinder.height = (Real)0;
102  return std::numeric_limits<Real>::max();
103  }
104 
105  mX.resize(numPoints);
106  mInvNumPoints = (Real)1 / (Real)numPoints;
107 
108  // Copy the points and translate by the average for numerical robustness.
109  Vector3<Real> average{ (Real)0, (Real)0, (Real)0 };
110  for (unsigned int i = 0; i < numPoints; ++i)
111  {
112  average += points[i];
113  }
114  average *= mInvNumPoints;
115  for (unsigned int i = 0; i < numPoints; ++i)
116  {
117  mX[i] = points[i] - average;
118  }
119 
120  // Estimate the center, direction, and squared radius.
121  Vector3<Real> minPC, minW;
122  Real minRSqr, minError;
123 
124  if (mNumThetaSamples == 0 || mNumPhiSamples == 0)
125  {
126  // Use the eigenvector corresponding to the maximum eigenvalue of
127  // the covariance matrix as the cylinder axis direction.
128  minError = ComputeUsingCovariance(minPC, minW, minRSqr);
129  }
130  else
131  {
132  // Search the hemisphere for the vector that leads to minimum error
133  // and use it for the cylinder axis.
134  if (mNumThreads == 0)
135  {
136  // Execute the algorithm in the main process.
137  minError = ComputeSingleThreaded(minPC, minW, minRSqr);
138  }
139  else
140  {
141  // Execute the algorithm in multiple threads.
142  minError = ComputeMultiThreaded(minPC, minW, minRSqr);
143  }
144  }
145 
146  // Translate back to the original space by the average of the points.
147  cylinder.axis.origin = minPC + average;
148  cylinder.axis.direction = minW;
149 
150  // Compute the cylinder radius.
151  cylinder.radius = sqrt(minRSqr);
152 
153  // Project the points onto the cylinder axis and choose the cylinder
154  // center and cylinder height as described in the comments at the top of
155  // this header file.
156  Real tmin = (Real)0, tmax = (Real)0;
157  for (unsigned int i = 0; i < numPoints; ++i)
158  {
159  Real t = Dot(cylinder.axis.direction, points[i] - cylinder.axis.origin);
160  tmin = std::min(t, tmin);
161  tmax = std::max(t, tmax);
162  }
163 
164  cylinder.axis.origin += ((tmin + tmax) * (Real)0.5) * cylinder.axis.direction;
165  cylinder.height = tmax - tmin;
166  return minError;
167 }
168 
169 template <typename Real>
171 {
173  for (auto const& X : mX)
174  {
175  covar += OuterProduct(X, X);
176  }
177  covar *= mInvNumPoints;
178  std::array<Real, 3> eval;
179  std::array<std::array<Real, 3>, 3> evec;
181  covar(0, 0), covar(0, 1), covar(0, 2), covar(1, 1), covar(1, 2), covar(2, 2),
182  true, +1, eval, evec);
183  minW = evec[2];
184  return Error(minW, minPC, minRSqr);
185 }
186 
187 template <typename Real>
189 {
190  Real const iMultiplier = (Real)GTE_C_TWO_PI / (Real)mNumThetaSamples;
191  Real const jMultiplier = (Real)GTE_C_HALF_PI / (Real)mNumPhiSamples;
192 
193  // Handle the north pole (0,0,1) separately.
194  minW = { (Real)0, (Real)0, (Real)1 };
195  Real minError = Error(minW, minPC, minRSqr);
196 
197  for (unsigned int j = 1; j <= mNumPhiSamples; ++j)
198  {
199  Real phi = jMultiplier * static_cast<Real>(j); // in [0,pi/2]
200  Real csphi = cos(phi);
201  Real snphi = sin(phi);
202  for (unsigned int i = 0; i < mNumThetaSamples; ++i)
203  {
204  Real theta = iMultiplier * static_cast<Real>(i); // in [0,2*pi)
205  Real cstheta = cos(theta);
206  Real sntheta = sin(theta);
207  Vector3<Real> W{ cstheta * snphi, sntheta * snphi, csphi };
208  Vector3<Real> PC;
209  Real rsqr;
210  Real error = Error(W, PC, rsqr);
211  if (error < minError)
212  {
213  minError = error;
214  minRSqr = rsqr;
215  minW = W;
216  minPC = PC;
217  }
218  }
219  }
220 
221  return minError;
222 }
223 
224 template <typename Real>
226 {
227  Real const iMultiplier = (Real)GTE_C_TWO_PI / (Real)mNumThetaSamples;
228  Real const jMultiplier = (Real)GTE_C_HALF_PI / (Real)mNumPhiSamples;
229 
230  // Handle the north pole (0,0,1) separately.
231  minW = { (Real)0, (Real)0, (Real)1 };
232  Real minError = Error(minW, minPC, minRSqr);
233 
234  struct Local
235  {
236  Real error;
237  Real rsqr;
238  Vector3<Real> W;
239  Vector3<Real> PC;
240  unsigned int imin;
241  unsigned int imax;
242  unsigned int jmin;
243  unsigned int jmax;
244  };
245 
246  std::vector<Local> local(mNumThreads);
247  unsigned int numThetaSamplesPerThread = mNumThetaSamples / mNumThreads;
248  unsigned int numPhiSamplesPerThread = mNumPhiSamples / mNumThreads;
249  for (unsigned int t = 0; t < mNumThreads; ++t)
250  {
251  local[t].error = std::numeric_limits<Real>::max();
252  local[t].rsqr = (Real)0;
253  local[t].W = Vector3<Real>::Zero();
254  local[t].PC = Vector3<Real>::Zero();
255  local[t].imin = numThetaSamplesPerThread * t;
256  local[t].imax = numThetaSamplesPerThread * (t + 1);
257  local[t].jmin = numPhiSamplesPerThread * t;
258  local[t].jmax = numPhiSamplesPerThread * (t + 1);
259  }
260  local[mNumThreads - 1].imax = mNumThetaSamples;
261  local[mNumThreads - 1].jmax = mNumPhiSamples + 1;
262 
263  std::vector<std::thread> process(mNumThreads);
264  for (unsigned int t = 0; t < mNumThreads; ++t)
265  {
266  process[t] = std::thread
267  (
268  [this, t, iMultiplier, jMultiplier, &local]()
269  {
270  for (unsigned int j = local[t].jmin; j < local[t].jmax; ++j)
271  {
272  Real phi = jMultiplier * static_cast<Real>(j); // in [0,pi/2]
273  Real csphi = cos(phi);
274  Real snphi = sin(phi);
275  for (unsigned int i = local[t].imin; i < local[t].imax; ++i)
276  {
277  Real theta = iMultiplier * static_cast<Real>(i); // in [0,2*pi)
278  Real cstheta = cos(theta);
279  Real sntheta = sin(theta);
280  Vector3<Real> W{ cstheta * snphi, sntheta * snphi, csphi };
281  Vector3<Real> PC;
282  Real rsqr;
283  Real error = Error(W, PC, rsqr);
284  if (error < local[t].error)
285  {
286  local[t].error = error;
287  local[t].rsqr = rsqr;
288  local[t].W = W;
289  local[t].PC = PC;
290  }
291  }
292  }
293  }
294  );
295  }
296 
297  for (unsigned int t = 0; t < mNumThreads; ++t)
298  {
299  process[t].join();
300 
301  if (local[t].error < minError)
302  {
303  minError = local[t].error;
304  minRSqr = local[t].rsqr;
305  minW = local[t].W;
306  minPC = local[t].PC;
307  }
308  }
309 
310  return minError;
311 }
312 
313 template <typename Real>
314 Real ApprCylinder3<Real>::Error(Vector3<Real> const& W, Vector3<Real>& PC, Real& rsqr) const
315 {
318  {
319  (Real)0, -W[2], W[1],
320  W[2], (Real)0, -W[0],
321  -W[1], W[0], (Real)0
322  };
323 
324  std::vector<Vector3<Real>> Y(mX.size());
325  std::vector<Real> sqrLength(mX.size());
326 
329  Real qform = (Real)0;
330  for (size_t i = 0; i < mX.size(); ++i)
331  {
332  Vector3<Real> projection = P * mX[i];
333  Y[i] = projection;
334  sqrLength[i] = Dot(projection, projection);
335  A += OuterProduct(projection, projection);
336  B += sqrLength[i] * projection;
337  qform += sqrLength[i];
338  }
339  A *= mInvNumPoints;
340  B *= mInvNumPoints;
341  qform *= mInvNumPoints;
342 
343  Matrix3x3<Real> Ahat = -S * A * S;
344  PC = (Ahat * B) / Trace<Real>(Ahat * A);
345 
346  Real error = (Real)0;
347  rsqr = (Real)0;
348  for (size_t i = 0; i < mX.size(); ++i)
349  {
350  Real term = sqrLength[i] - Dot(Y[i], PC) * (Real)2 - qform;
351  error += term * term;
352  Vector3<Real> diff = PC - Y[i];
353  rsqr += Dot(diff, diff);
354  }
355  error *= mInvNumPoints;
356  rsqr *= mInvNumPoints;
357  return error;
358 }
359 
360 }
unsigned int mNumPhiSamples
Real ComputeMultiThreaded(Vector3< Real > &minPC, Vector3< Real > &minW, Real &minRSqr)
std::vector< Vector3< Real > > mX
Real operator()(unsigned int numPoints, Vector3< Real > const *points, Cylinder3< Real > &cylinder)
Real ComputeUsingCovariance(Vector3< Real > &minPC, Vector3< Real > &minW, Real &minRSqr)
GLfixed GLfixed GLint GLint GLfixed points
Definition: glext.h:4927
unsigned int mNumThetaSamples
static Vector Zero()
Definition: GteVector.h:295
ApprCylinder3(unsigned int numThreads, unsigned int numThetaSamples, unsigned int numPhiSamples)
DualQuaternion< Real > Dot(DualQuaternion< Real > const &d0, DualQuaternion< Real > const &d1)
GLdouble GLdouble t
Definition: glext.h:239
Real ComputeSingleThreaded(Vector3< Real > &minPC, Vector3< Real > &minW, Real &minRSqr)
Real Error(Vector3< Real > const &W, Vector3< Real > &PC, Real &rsqr) const
#define GTE_C_HALF_PI
Definition: GteConstants.h:18
static Matrix Identity()
Definition: GteMatrix.h:490
unsigned int mNumThreads
#define GTE_C_TWO_PI
Definition: GteConstants.h:20
Line3< Real > axis
Definition: GteCylinder3.h:31
GMatrix< Real > OuterProduct(GVector< Real > const &U, GVector< Real > const &V)
Definition: GteGMatrix.h:815
static Matrix Zero()
Definition: GteMatrix.h:473


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 03:59:58