Point Cloud Library (PCL)  1.6.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sac_model_cylinder.hpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2009-2010, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage, Inc. nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * $Id: sac_model_cylinder.hpp 6144 2012-07-04 22:06:28Z rusu $
35  *
36  */
37 
38 #ifndef PCL_SAMPLE_CONSENSUS_IMPL_SAC_MODEL_CYLINDER_H_
39 #define PCL_SAMPLE_CONSENSUS_IMPL_SAC_MODEL_CYLINDER_H_
40 
42 #include <pcl/common/concatenate.h>
43 #include <unsupported/Eigen/NonLinearOptimization>
44 
46 template <typename PointT, typename PointNT> bool
48 {
49  return (true);
50 }
51 
53 template <typename PointT, typename PointNT> bool
55  const std::vector<int> &samples, Eigen::VectorXf &model_coefficients)
56 {
57  // Need 2 samples
58  if (samples.size () != 2)
59  {
60  PCL_ERROR ("[pcl::SampleConsensusModelCylinder::computeModelCoefficients] Invalid set of samples given (%zu)!\n", samples.size ());
61  return (false);
62  }
63 
64  if (!normals_)
65  {
66  PCL_ERROR ("[pcl::SampleConsensusModelCylinder::computeModelCoefficients] No input dataset containing normals was given!\n");
67  return (false);
68  }
69 
70  Eigen::Vector4f p1 (input_->points[samples[0]].x, input_->points[samples[0]].y, input_->points[samples[0]].z, 0);
71  Eigen::Vector4f p2 (input_->points[samples[1]].x, input_->points[samples[1]].y, input_->points[samples[1]].z, 0);
72 
73  Eigen::Vector4f n1 (normals_->points[samples[0]].normal[0], normals_->points[samples[0]].normal[1], normals_->points[samples[0]].normal[2], 0);
74  Eigen::Vector4f n2 (normals_->points[samples[1]].normal[0], normals_->points[samples[1]].normal[1], normals_->points[samples[1]].normal[2], 0);
75  Eigen::Vector4f w = n1 + p1 - p2;
76 
77  float a = n1.dot (n1);
78  float b = n1.dot (n2);
79  float c = n2.dot (n2);
80  float d = n1.dot (w);
81  float e = n2.dot (w);
82  float denominator = a*c - b*b;
83  float sc, tc;
84  // Compute the line parameters of the two closest points
85  if (denominator < 1e-8) // The lines are almost parallel
86  {
87  sc = 0.0f;
88  tc = (b > c ? d / b : e / c); // Use the largest denominator
89  }
90  else
91  {
92  sc = (b*e - c*d) / denominator;
93  tc = (a*e - b*d) / denominator;
94  }
95 
96  // point_on_axis, axis_direction
97  Eigen::Vector4f line_pt = p1 + n1 + sc * n1;
98  Eigen::Vector4f line_dir = p2 + tc * n2 - line_pt;
99  line_dir.normalize ();
100 
101  model_coefficients.resize (7);
102  // model_coefficients.template head<3> () = line_pt.template head<3> ();
103  model_coefficients[0] = line_pt[0];
104  model_coefficients[1] = line_pt[1];
105  model_coefficients[2] = line_pt[2];
106  // model_coefficients.template segment<3> (3) = line_dir.template head<3> ();
107  model_coefficients[3] = line_dir[0];
108  model_coefficients[4] = line_dir[1];
109  model_coefficients[5] = line_dir[2];
110  // cylinder radius
111  model_coefficients[6] = static_cast<float> (sqrt (pcl::sqrPointToLineDistance (p1, line_pt, line_dir)));
112 
113  if (model_coefficients[6] > radius_max_ || model_coefficients[6] < radius_min_)
114  return (false);
115 
116  return (true);
117 }
118 
120 template <typename PointT, typename PointNT> void
122  const Eigen::VectorXf &model_coefficients, std::vector<double> &distances)
123 {
124  // Check if the model is valid given the user constraints
125  if (!isModelValid (model_coefficients))
126  {
127  distances.clear ();
128  return;
129  }
130 
131  distances.resize (indices_->size ());
132 
133  Eigen::Vector4f line_pt (model_coefficients[0], model_coefficients[1], model_coefficients[2], 0);
134  Eigen::Vector4f line_dir (model_coefficients[3], model_coefficients[4], model_coefficients[5], 0);
135  float ptdotdir = line_pt.dot (line_dir);
136  float dirdotdir = 1.0f / line_dir.dot (line_dir);
137  // Iterate through the 3d points and calculate the distances from them to the sphere
138  for (size_t i = 0; i < indices_->size (); ++i)
139  {
140  // Aproximate the distance from the point to the cylinder as the difference between
141  // dist(point,cylinder_axis) and cylinder radius
142  // @note need to revise this.
143  Eigen::Vector4f pt (input_->points[(*indices_)[i]].x, input_->points[(*indices_)[i]].y, input_->points[(*indices_)[i]].z, 0);
144  Eigen::Vector4f n (normals_->points[(*indices_)[i]].normal[0], normals_->points[(*indices_)[i]].normal[1], normals_->points[(*indices_)[i]].normal[2], 0);
145 
146  double d_euclid = fabs (pointToLineDistance (pt, model_coefficients) - model_coefficients[6]);
147 
148  // Calculate the point's projection on the cylinder axis
149  float k = (pt.dot (line_dir) - ptdotdir) * dirdotdir;
150  Eigen::Vector4f pt_proj = line_pt + k * line_dir;
151  Eigen::Vector4f dir = pt - pt_proj;
152  dir.normalize ();
153 
154  // Calculate the angular distance between the point normal and the (dir=pt_proj->pt) vector
155  double d_normal = fabs (getAngle3D (n, dir));
156  d_normal = (std::min) (d_normal, M_PI - d_normal);
157 
158  distances[i] = fabs (normal_distance_weight_ * d_normal + (1 - normal_distance_weight_) * d_euclid);
159  }
160 }
161 
163 template <typename PointT, typename PointNT> void
165  const Eigen::VectorXf &model_coefficients, const double threshold, std::vector<int> &inliers)
166 {
167  // Check if the model is valid given the user constraints
168  if (!isModelValid (model_coefficients))
169  {
170  inliers.clear ();
171  return;
172  }
173 
174  int nr_p = 0;
175  inliers.resize (indices_->size ());
176 
177  Eigen::Vector4f line_pt (model_coefficients[0], model_coefficients[1], model_coefficients[2], 0);
178  Eigen::Vector4f line_dir (model_coefficients[3], model_coefficients[4], model_coefficients[5], 0);
179  float ptdotdir = line_pt.dot (line_dir);
180  float dirdotdir = 1.0f / line_dir.dot (line_dir);
181  // Iterate through the 3d points and calculate the distances from them to the sphere
182  for (size_t i = 0; i < indices_->size (); ++i)
183  {
184  // Aproximate the distance from the point to the cylinder as the difference between
185  // dist(point,cylinder_axis) and cylinder radius
186  Eigen::Vector4f pt (input_->points[(*indices_)[i]].x, input_->points[(*indices_)[i]].y, input_->points[(*indices_)[i]].z, 0);
187  Eigen::Vector4f n (normals_->points[(*indices_)[i]].normal[0], normals_->points[(*indices_)[i]].normal[1], normals_->points[(*indices_)[i]].normal[2], 0);
188  double d_euclid = fabs (pointToLineDistance (pt, model_coefficients) - model_coefficients[6]);
189 
190  // Calculate the point's projection on the cylinder axis
191  float k = (pt.dot (line_dir) - ptdotdir) * dirdotdir;
192  Eigen::Vector4f pt_proj = line_pt + k * line_dir;
193  Eigen::Vector4f dir = pt - pt_proj;
194  dir.normalize ();
195 
196  // Calculate the angular distance between the point normal and the (dir=pt_proj->pt) vector
197  double d_normal = fabs (getAngle3D (n, dir));
198  d_normal = (std::min) (d_normal, M_PI - d_normal);
199 
200  if (fabs (normal_distance_weight_ * d_normal + (1 - normal_distance_weight_) * d_euclid) < threshold)
201  {
202  // Returns the indices of the points whose distances are smaller than the threshold
203  inliers[nr_p] = (*indices_)[i];
204  nr_p++;
205  }
206  }
207  inliers.resize (nr_p);
208 }
209 
211 template <typename PointT, typename PointNT> int
213  const Eigen::VectorXf &model_coefficients, const double threshold)
214 {
215  // Check if the model is valid given the user constraints
216  if (!isModelValid (model_coefficients))
217  return (0);
218 
219  int nr_p = 0;
220 
221  Eigen::Vector4f line_pt (model_coefficients[0], model_coefficients[1], model_coefficients[2], 0);
222  Eigen::Vector4f line_dir (model_coefficients[3], model_coefficients[4], model_coefficients[5], 0);
223  float ptdotdir = line_pt.dot (line_dir);
224  float dirdotdir = 1.0f / line_dir.dot (line_dir);
225  // Iterate through the 3d points and calculate the distances from them to the sphere
226  for (size_t i = 0; i < indices_->size (); ++i)
227  {
228  // Aproximate the distance from the point to the cylinder as the difference between
229  // dist(point,cylinder_axis) and cylinder radius
230  Eigen::Vector4f pt (input_->points[(*indices_)[i]].x, input_->points[(*indices_)[i]].y, input_->points[(*indices_)[i]].z, 0);
231  Eigen::Vector4f n (normals_->points[(*indices_)[i]].normal[0], normals_->points[(*indices_)[i]].normal[1], normals_->points[(*indices_)[i]].normal[2], 0);
232  double d_euclid = fabs (pointToLineDistance (pt, model_coefficients) - model_coefficients[6]);
233 
234  // Calculate the point's projection on the cylinder axis
235  float k = (pt.dot (line_dir) - ptdotdir) * dirdotdir;
236  Eigen::Vector4f pt_proj = line_pt + k * line_dir;
237  Eigen::Vector4f dir = pt - pt_proj;
238  dir.normalize ();
239 
240  // Calculate the angular distance between the point normal and the (dir=pt_proj->pt) vector
241  double d_normal = fabs (getAngle3D (n, dir));
242  d_normal = (std::min) (d_normal, M_PI - d_normal);
243 
244  if (fabs (normal_distance_weight_ * d_normal + (1 - normal_distance_weight_) * d_euclid) < threshold)
245  nr_p++;
246  }
247  return (nr_p);
248 }
249 
251 template <typename PointT, typename PointNT> void
253  const std::vector<int> &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients)
254 {
255  optimized_coefficients = model_coefficients;
256 
257  // Needs a set of valid model coefficients
258  if (model_coefficients.size () != 7)
259  {
260  PCL_ERROR ("[pcl::SampleConsensusModelCylinder::optimizeModelCoefficients] Invalid number of model coefficients given (%zu)!\n", model_coefficients.size ());
261  return;
262  }
263 
264  if (inliers.empty ())
265  {
266  PCL_DEBUG ("[pcl::SampleConsensusModelCylinder:optimizeModelCoefficients] Inliers vector empty! Returning the same coefficients.\n");
267  return;
268  }
269 
270  tmp_inliers_ = &inliers;
271 
272  OptimizationFunctor functor (static_cast<int> (inliers.size ()), this);
273  Eigen::NumericalDiff<OptimizationFunctor > num_diff (functor);
274  Eigen::LevenbergMarquardt<Eigen::NumericalDiff<OptimizationFunctor>, float> lm (num_diff);
275  int info = lm.minimize (optimized_coefficients);
276 
277  // Compute the L2 norm of the residuals
278  PCL_DEBUG ("[pcl::SampleConsensusModelCylinder::optimizeModelCoefficients] LM solver finished with exit code %i, having a residual norm of %g. \nInitial solution: %g %g %g %g %g %g %g \nFinal solution: %g %g %g %g %g %g %g\n",
279  info, lm.fvec.norm (), model_coefficients[0], model_coefficients[1], model_coefficients[2], model_coefficients[3],
280  model_coefficients[4], model_coefficients[5], model_coefficients[6], optimized_coefficients[0], optimized_coefficients[1], optimized_coefficients[2], optimized_coefficients[3], optimized_coefficients[4], optimized_coefficients[5], optimized_coefficients[6]);
281 
282  Eigen::Vector3f line_dir (optimized_coefficients[3], optimized_coefficients[4], optimized_coefficients[5]);
283  line_dir.normalize ();
284  optimized_coefficients[3] = line_dir[0];
285  optimized_coefficients[4] = line_dir[1];
286  optimized_coefficients[5] = line_dir[2];
287 }
288 
290 template <typename PointT, typename PointNT> void
292  const std::vector<int> &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields)
293 {
294  // Needs a valid set of model coefficients
295  if (model_coefficients.size () != 7)
296  {
297  PCL_ERROR ("[pcl::SampleConsensusModelCylinder::projectPoints] Invalid number of model coefficients given (%zu)!\n", model_coefficients.size ());
298  return;
299  }
300 
301  projected_points.header = input_->header;
302  projected_points.is_dense = input_->is_dense;
303 
304  Eigen::Vector4f line_pt (model_coefficients[0], model_coefficients[1], model_coefficients[2], 0);
305  Eigen::Vector4f line_dir (model_coefficients[3], model_coefficients[4], model_coefficients[5], 0);
306  float ptdotdir = line_pt.dot (line_dir);
307  float dirdotdir = 1.0f / line_dir.dot (line_dir);
308 
309  // Copy all the data fields from the input cloud to the projected one?
310  if (copy_data_fields)
311  {
312  // Allocate enough space and copy the basics
313  projected_points.points.resize (input_->points.size ());
314  projected_points.width = input_->width;
315  projected_points.height = input_->height;
316 
317  typedef typename pcl::traits::fieldList<PointT>::type FieldList;
318  // Iterate over each point
319  for (size_t i = 0; i < projected_points.points.size (); ++i)
320  // Iterate over each dimension
321  pcl::for_each_type <FieldList> (NdConcatenateFunctor <PointT, PointT> (input_->points[i], projected_points.points[i]));
322 
323  // Iterate through the 3d points and calculate the distances from them to the cylinder
324  for (size_t i = 0; i < inliers.size (); ++i)
325  {
326  Eigen::Vector4f p (input_->points[inliers[i]].x,
327  input_->points[inliers[i]].y,
328  input_->points[inliers[i]].z,
329  1);
330 
331  float k = (p.dot (line_dir) - ptdotdir) * dirdotdir;
332 
333  pcl::Vector4fMap pp = projected_points.points[inliers[i]].getVector4fMap ();
334  pp = line_pt + k * line_dir;
335 
336  Eigen::Vector4f dir = p - pp;
337  dir.normalize ();
338 
339  // Calculate the projection of the point onto the cylinder
340  pp += dir * model_coefficients[6];
341  }
342  }
343  else
344  {
345  // Allocate enough space and copy the basics
346  projected_points.points.resize (inliers.size ());
347  projected_points.width = static_cast<uint32_t> (inliers.size ());
348  projected_points.height = 1;
349 
350  typedef typename pcl::traits::fieldList<PointT>::type FieldList;
351  // Iterate over each point
352  for (size_t i = 0; i < inliers.size (); ++i)
353  // Iterate over each dimension
354  pcl::for_each_type <FieldList> (NdConcatenateFunctor <PointT, PointT> (input_->points[inliers[i]], projected_points.points[i]));
355 
356  // Iterate through the 3d points and calculate the distances from them to the plane
357  for (size_t i = 0; i < inliers.size (); ++i)
358  {
359  pcl::Vector4fMap pp = projected_points.points[i].getVector4fMap ();
360  pcl::Vector4fMapConst p = input_->points[inliers[i]].getVector4fMap ();
361 
362  float k = (p.dot (line_dir) - ptdotdir) * dirdotdir;
363  // Calculate the projection of the point on the line
364  pp = line_pt + k * line_dir;
365 
366  Eigen::Vector4f dir = p - pp;
367  dir.normalize ();
368 
369  // Calculate the projection of the point onto the cylinder
370  pp += dir * model_coefficients[6];
371  }
372  }
373 }
374 
376 template <typename PointT, typename PointNT> bool
378  const std::set<int> &indices, const Eigen::VectorXf &model_coefficients, const double threshold)
379 {
380  // Needs a valid model coefficients
381  if (model_coefficients.size () != 7)
382  {
383  PCL_ERROR ("[pcl::SampleConsensusModelCylinder::doSamplesVerifyModel] Invalid number of model coefficients given (%zu)!\n", model_coefficients.size ());
384  return (false);
385  }
386 
387  for (std::set<int>::const_iterator it = indices.begin (); it != indices.end (); ++it)
388  {
389  // Aproximate the distance from the point to the cylinder as the difference between
390  // dist(point,cylinder_axis) and cylinder radius
391  // @note need to revise this.
392  Eigen::Vector4f pt (input_->points[*it].x, input_->points[*it].y, input_->points[*it].z, 0);
393  if (fabs (pointToLineDistance (pt, model_coefficients) - model_coefficients[6]) > threshold)
394  return (false);
395  }
396 
397  return (true);
398 }
399 
401 template <typename PointT, typename PointNT> double
403  const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients)
404 {
405  Eigen::Vector4f line_pt (model_coefficients[0], model_coefficients[1], model_coefficients[2], 0);
406  Eigen::Vector4f line_dir (model_coefficients[3], model_coefficients[4], model_coefficients[5], 0);
407  return sqrt(pcl::sqrPointToLineDistance (pt, line_pt, line_dir));
408 }
409 
411 template <typename PointT, typename PointNT> void
413  const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients, Eigen::Vector4f &pt_proj)
414 {
415  Eigen::Vector4f line_pt (model_coefficients[0], model_coefficients[1], model_coefficients[2], 0);
416  Eigen::Vector4f line_dir (model_coefficients[3], model_coefficients[4], model_coefficients[5], 0);
417 
418  float k = (pt.dot (line_dir) - line_pt.dot (line_dir)) * line_dir.dot (line_dir);
419  pt_proj = line_pt + k * line_dir;
420 
421  Eigen::Vector4f dir = pt - pt_proj;
422  dir.normalize ();
423 
424  // Calculate the projection of the point onto the cylinder
425  pt_proj += dir * model_coefficients[6];
426 }
427 
429 template <typename PointT, typename PointNT> bool
430 pcl::SampleConsensusModelCylinder<PointT, PointNT>::isModelValid (const Eigen::VectorXf &model_coefficients)
431 {
432  // Needs a valid model coefficients
433  if (model_coefficients.size () != 7)
434  {
435  PCL_ERROR ("[pcl::SampleConsensusModelCylinder::isModelValid] Invalid number of model coefficients given (%zu)!\n", model_coefficients.size ());
436  return (false);
437  }
438 
439  // Check against template, if given
440  if (eps_angle_ > 0.0)
441  {
442  // Obtain the cylinder direction
443  Eigen::Vector4f coeff;
444  coeff[0] = model_coefficients[3];
445  coeff[1] = model_coefficients[4];
446  coeff[2] = model_coefficients[5];
447  coeff[3] = 0;
448 
449  Eigen::Vector4f axis (axis_[0], axis_[1], axis_[2], 0);
450  double angle_diff = fabs (getAngle3D (axis, coeff));
451  angle_diff = (std::min) (angle_diff, M_PI - angle_diff);
452  // Check whether the current cylinder model satisfies our angle threshold criterion with respect to the given axis
453  if (angle_diff > eps_angle_)
454  return (false);
455  }
456 
457  if (radius_min_ != -std::numeric_limits<double>::max() && model_coefficients[6] < radius_min_)
458  return (false);
459  if (radius_max_ != std::numeric_limits<double>::max() && model_coefficients[6] > radius_max_)
460  return (false);
461 
462  return (true);
463 }
464 
465 #define PCL_INSTANTIATE_SampleConsensusModelCylinder(PointT, PointNT) template class PCL_EXPORTS pcl::SampleConsensusModelCylinder<PointT, PointNT>;
466 
467 #endif // PCL_SAMPLE_CONSENSUS_IMPL_SAC_MODEL_CYLINDER_H_
468