1111
1212#include < limits>
1313
14- #include < unsupported/Eigen/NonLinearOptimization> // for LevenbergMarquardt
1514#include < pcl/sample_consensus/sac_model_ellipse3d.h>
1615#include < pcl/common/concatenate.h>
1716
@@ -225,7 +224,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::computeModelCoefficients (const Indi
225224 // Retrieve the ellipse point at the tilt angle t (par_t), along the local x-axis
226225 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, par_h, par_k, par_t ).finished ();
227226 Eigen::Vector3f p_th_ (0.0 , 0.0 , 0.0 );
228- get_ellipse_point (params, par_t , p_th_ (0 ), p_th_ (1 ));
227+ internal:: get_ellipse_point (params, par_t , p_th_ (0 ), p_th_ (1 ));
229228
230229 // Redefinition of the x-axis of the ellipse's local reference frame
231230 x_axis = (Rot * p_th_).normalized ();
@@ -280,16 +279,11 @@ pcl::SampleConsensusModelEllipse3D<PointT>::getDistancesToModel (const Eigen::Ve
280279 float th_opt;
281280
282281 // Iterate through the 3D points and calculate the distances from them to the ellipse
283- for (std::size_t i = 0 ; i < indices_->size (); ++i)
284- // Calculate the distance from the point to the ellipse:
285- // 1. calculate intersection point of the plane in which the ellipse lies and the
286- // line from the sample point with the direction of the plane normal (projected point)
287- // 2. calculate the intersection point of the line from the ellipse center to the projected point
288- // with the ellipse
289- // 3. calculate distance from corresponding point on the ellipse to the sample point
282+ std::size_t i = 0 ;
283+ for (const auto & index : *indices_)
290284 {
291285 // p : Sample Point
292- const Eigen::Vector3f p ((*input_)[(*indices_)[i]] .x , (*input_)[(*indices_)[i]] .y , (*input_)[(*indices_)[i] ].z );
286+ const Eigen::Vector3f p ((*input_)[index] .x , (*input_)[index] .y , (*input_)[index ].z );
293287
294288 // Local coordinates of sample point p
295289 const Eigen::Vector3f p_ = Rot_T * (p - c);
@@ -298,9 +292,9 @@ pcl::SampleConsensusModelEllipse3D<PointT>::getDistancesToModel (const Eigen::Ve
298292 // Calculate the shortest distance from the point to the ellipse which is given by
299293 // the norm of a vector that is normal to the ellipse tangent calculated at the
300294 // point it intersects the tangent.
301- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
295+ const Eigen::Vector2f distanceVector = internal:: dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
302296
303- distances[i] = distanceVector.norm ();
297+ distances[i++ ] = distanceVector.norm ();
304298 }
305299}
306300
@@ -343,10 +337,10 @@ pcl::SampleConsensusModelEllipse3D<PointT>::selectWithinDistance (
343337
344338 const auto squared_threshold = threshold * threshold;
345339 // Iterate through the 3d points and calculate the distances from them to the ellipse
346- for (std:: size_t i = 0 ; i < indices_-> size (); ++i )
340+ for (const auto & index : * indices_)
347341 {
348342 // p : Sample Point
349- const Eigen::Vector3f p ((*input_)[(*indices_)[i]] .x , (*input_)[(*indices_)[i]] .y , (*input_)[(*indices_)[i] ].z );
343+ const Eigen::Vector3f p ((*input_)[index] .x , (*input_)[index] .y , (*input_)[index ].z );
350344
351345 // Local coordinates of sample point p
352346 const Eigen::Vector3f p_ = Rot_T * (p - c);
@@ -357,13 +351,13 @@ pcl::SampleConsensusModelEllipse3D<PointT>::selectWithinDistance (
357351 // point it intersects the tangent.
358352 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
359353 float th_opt;
360- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
354+ const Eigen::Vector2f distanceVector = internal:: dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
361355
362356 const double sqr_dist = distanceVector.squaredNorm ();
363357 if (sqr_dist < squared_threshold)
364358 {
365359 // Returns the indices of the points whose distances are smaller than the threshold
366- inliers.push_back ((*indices_)[i] );
360+ inliers.push_back (index );
367361 error_sqr_dists_.push_back (sqr_dist);
368362 }
369363 }
@@ -402,10 +396,10 @@ pcl::SampleConsensusModelEllipse3D<PointT>::countWithinDistance (
402396
403397 const auto squared_threshold = threshold * threshold;
404398 // Iterate through the 3d points and calculate the distances from them to the ellipse
405- for (std:: size_t i = 0 ; i < indices_-> size (); ++i )
399+ for (const auto & index : * indices_)
406400 {
407401 // p : Sample Point
408- const Eigen::Vector3f p ((*input_)[(*indices_)[i]] .x , (*input_)[(*indices_)[i]] .y , (*input_)[(*indices_)[i] ].z );
402+ const Eigen::Vector3f p ((*input_)[index] .x , (*input_)[index] .y , (*input_)[index ].z );
409403
410404 // Local coordinates of sample point p
411405 const Eigen::Vector3f p_ = Rot_T * (p - c);
@@ -416,7 +410,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::countWithinDistance (
416410 // point it intersects the tangent.
417411 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
418412 float th_opt;
419- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
413+ const Eigen::Vector2f distanceVector = internal:: dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
420414
421415 if (distanceVector.squaredNorm () < squared_threshold)
422416 nr_p++;
@@ -443,51 +437,29 @@ pcl::SampleConsensusModelEllipse3D<PointT>::optimizeModelCoefficients (
443437 // Need more than the minimum sample size to make a difference
444438 if (inliers.size () <= sample_size_)
445439 {
446- PCL_ERROR (" [pcl::SampleConsensusModelEllipse3D::optimizeModelCoefficients] Not enough inliers to refine/ optimize the model's coefficients (%lu)! Returning the same coefficients.\n " , inliers.size ());
440+ PCL_ERROR (" [pcl::SampleConsensusModelEllipse3D::optimizeModelCoefficients] Not enough inliers found to optimize model coefficients (%lu)! Returning the same coefficients.\n " , inliers.size ());
447441 return ;
448442 }
449443
450- OptimizationFunctor functor (this , inliers);
451- Eigen::NumericalDiff<OptimizationFunctor> num_diff (functor);
452- Eigen::LevenbergMarquardt<Eigen::NumericalDiff<OptimizationFunctor>, double > lm (num_diff);
453- Eigen::VectorXd coeff = model_coefficients.cast <double >();
454- int info = lm.minimize (coeff);
455- optimized_coefficients = coeff.cast <float >();
456-
457- // Compute the L2 norm of the residuals
458- PCL_DEBUG (" [pcl::SampleConsensusModelEllipse3D::optimizeModelCoefficients] LM solver finished with exit code %i, having a residual norm of %g. \n Initial solution: %g %g %g %g %g %g %g %g %g %g %g\n Final solution: %g %g %g %g %g %g %g %g %g %g %g\n " ,
459- info, lm.fvec .norm (),
460-
461- model_coefficients[0 ],
462- model_coefficients[1 ],
463- model_coefficients[2 ],
464- model_coefficients[3 ],
465- model_coefficients[4 ],
466- model_coefficients[5 ],
467- model_coefficients[6 ],
468- model_coefficients[7 ],
469- model_coefficients[8 ],
470- model_coefficients[9 ],
471- model_coefficients[10 ],
472-
473- optimized_coefficients[0 ],
474- optimized_coefficients[1 ],
475- optimized_coefficients[2 ],
476- optimized_coefficients[3 ],
477- optimized_coefficients[4 ],
478- optimized_coefficients[5 ],
479- optimized_coefficients[6 ],
480- optimized_coefficients[7 ],
481- optimized_coefficients[8 ],
482- optimized_coefficients[9 ],
483- optimized_coefficients[10 ]);
444+ Eigen::ArrayXf x (inliers.size ()), y (inliers.size ()), z (inliers.size ());
445+ std::size_t i = 0 ;
446+ for (const auto & inlier : inliers)
447+ {
448+ const auto & pt = (*input_)[inlier];
449+ x[i] = pt.x ; y[i] = pt.y ; z[i] = pt.z ;
450+ ++i;
451+ }
452+
453+ internal::optimizeModelCoefficientsEllipse3D (optimized_coefficients, x, y, z);
484454}
485455
486456// ////////////////////////////////////////////////////////////////////////
487457template <typename PointT> void
488458pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
489- const Indices &inliers, const Eigen::VectorXf &model_coefficients,
490- PointCloud &projected_points, bool copy_data_fields) const
459+ const Indices &inliers,
460+ const Eigen::VectorXf &model_coefficients,
461+ PointCloud &projected_points,
462+ bool copy_data_fields) const
491463{
492464 // Needs a valid set of model coefficients
493465 if (!isModelValid (model_coefficients))
@@ -537,10 +509,11 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
537509 const Eigen::Matrix3f Rot_T = Rot.transpose ();
538510
539511 // Iterate through the 3d points and calculate the distances from them to the plane
540- for (std::size_t i = 0 ; i < inliers.size (); ++i)
512+ std::size_t i = 0 ;
513+ for (const auto & inlier : inliers)
541514 {
542515 // p : Sample Point
543- const Eigen::Vector3f p ((*input_)[(*indices_)[i]] .x , (*input_)[(*indices_)[i]] .y , (*input_)[(*indices_)[i] ].z );
516+ const Eigen::Vector3f p ((*input_)[inlier] .x , (*input_)[inlier] .y , (*input_)[inlier ].z );
544517
545518 // Local coordinates of sample point p
546519 const Eigen::Vector3f p_ = Rot_T * (p - c);
@@ -551,17 +524,17 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
551524 // point it intersects the tangent.
552525 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
553526 float th_opt;
554- dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
527+ internal:: dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
555528
556529 // Retrieve the ellipse point at the tilt angle t, along the local x-axis
557530 Eigen::Vector3f k_ (0.0 , 0.0 , 0.0 );
558- get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
531+ internal:: get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
559532
560533 const Eigen::Vector3f k = c + Rot * k_;
561534
562535 projected_points[i].x = static_cast <float > (k[0 ]);
563536 projected_points[i].y = static_cast <float > (k[1 ]);
564- projected_points[i].z = static_cast <float > (k[2 ]);
537+ projected_points[i++ ].z = static_cast <float > (k[2 ]);
565538 }
566539 }
567540 else
@@ -599,10 +572,11 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
599572 const Eigen::Matrix3f Rot_T = Rot.transpose ();
600573
601574 // Iterate through the 3d points and calculate the distances from them to the plane
602- for (std::size_t i = 0 ; i < inliers.size (); ++i)
575+ std::size_t i = 0 ;
576+ for (const auto & inlier : inliers)
603577 {
604578 // p : Sample Point
605- const Eigen::Vector3f p ((*input_)[(*indices_)[i]] .x , (*input_)[(*indices_)[i]] .y , (*input_)[(*indices_)[i] ].z );
579+ const Eigen::Vector3f p ((*input_)[inlier] .x , (*input_)[inlier] .y , (*input_)[inlier ].z );
606580
607581 // Local coordinates of sample point p
608582 const Eigen::Vector3f p_ = Rot_T * (p - c);
@@ -613,18 +587,18 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
613587 // point it intersects the tangent.
614588 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
615589 float th_opt;
616- dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
590+ internal:: dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
617591
618592 // Retrieve the ellipse point at the tilt angle t, along the local x-axis
619593 // // model_coefficients[5] = static_cast<float>(par_t);
620594 Eigen::Vector3f k_ (0.0 , 0.0 , 0.0 );
621- get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
595+ internal:: get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
622596
623597 const Eigen::Vector3f k = c + Rot * k_;
624598
625599 projected_points[i].x = static_cast <float > (k[0 ]);
626600 projected_points[i].y = static_cast <float > (k[1 ]);
627- projected_points[i].z = static_cast <float > (k[2 ]);
601+ projected_points[i++ ].z = static_cast <float > (k[2 ]);
628602 }
629603 }
630604}
@@ -679,7 +653,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::doSamplesVerifyModel (
679653 // point it intersects the tangent.
680654 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
681655 float th_opt;
682- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
656+ const Eigen::Vector2f distanceVector = internal:: dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
683657
684658 if (distanceVector.squaredNorm () > squared_threshold)
685659 return (false );
@@ -710,152 +684,4 @@ pcl::SampleConsensusModelEllipse3D<PointT>::isModelValid (const Eigen::VectorXf
710684 return (true );
711685}
712686
713-
714-
715- // ////////////////////////////////////////////////////////////////////////
716- template <typename PointT>
717- void inline pcl::SampleConsensusModelEllipse3D<PointT>::get_ellipse_point(
718- const Eigen::VectorXf& par, float th, float & x, float & y)
719- {
720- /*
721- * Calculates a point on the ellipse model 'par' using the angle 'th'.
722- */
723-
724- // Parametric eq.params
725- const float par_a (par[0 ]);
726- const float par_b (par[1 ]);
727- const float par_h (par[2 ]);
728- const float par_k (par[3 ]);
729- const float par_t (par[4 ]);
730-
731- x = par_h + std::cos (par_t ) * par_a * std::cos (th) -
732- std::sin (par_t ) * par_b * std::sin (th);
733- y = par_k + std::sin (par_t ) * par_a * std::cos (th) +
734- std::cos (par_t ) * par_b * std::sin (th);
735-
736- return ;
737- }
738-
739- // ////////////////////////////////////////////////////////////////////////
740- template <typename PointT>
741- Eigen::Vector2f inline pcl::SampleConsensusModelEllipse3D<PointT>::dvec2ellipse(
742- const Eigen::VectorXf& par, float u, float v, float & th_opt)
743- {
744- /*
745- * Minimum distance vector from point p=(u,v) to the ellipse model 'par'.
746- */
747-
748- // Parametric eq.params
749- // (par_a, par_b, and par_t do not need to be declared)
750- const float par_h = par[2 ];
751- const float par_k = par[3 ];
752-
753- const Eigen::Vector2f center (par_h, par_k);
754- Eigen::Vector2f p (u, v);
755- p -= center;
756-
757- // Local x-axis of the ellipse
758- Eigen::Vector2f x_axis (0.0 , 0.0 );
759- get_ellipse_point (par, 0.0 , x_axis (0 ), x_axis (1 ));
760- x_axis -= center;
761-
762- // Local y-axis of the ellipse
763- Eigen::Vector2f y_axis (0.0 , 0.0 );
764- get_ellipse_point (par, M_PI / 2.0 , y_axis (0 ), y_axis (1 ));
765- y_axis -= center;
766-
767- // Convert the point p=(u,v) to local ellipse coordinates
768- const float x_proj = p.dot (x_axis) / x_axis.norm ();
769- const float y_proj = p.dot (y_axis) / y_axis.norm ();
770-
771- // Find the ellipse quandrant to where the point p=(u,v) belongs,
772- // and limit the search interval to 'th_min' and 'th_max'.
773- float th_min (0.0 ), th_max (0.0 );
774- const float th = std::atan2 (y_proj, x_proj);
775-
776- if (-M_PI <= th && th < -M_PI / 2.0 ) {
777- // Q3
778- th_min = -M_PI;
779- th_max = -M_PI / 2.0 ;
780- }
781- if (-M_PI / 2.0 <= th && th < 0.0 ) {
782- // Q4
783- th_min = -M_PI / 2.0 ;
784- th_max = 0.0 ;
785- }
786- if (0.0 <= th && th < M_PI / 2.0 ) {
787- // Q1
788- th_min = 0.0 ;
789- th_max = M_PI / 2.0 ;
790- }
791- if (M_PI / 2.0 <= th && th <= M_PI) {
792- // Q2
793- th_min = M_PI / 2.0 ;
794- th_max = M_PI;
795- }
796-
797- // Use an unconstrained line search optimizer to find the optimal th_opt
798- th_opt = golden_section_search (par, u, v, th_min, th_max, 1 .e -3 );
799-
800- // Distance vector from a point (u,v) to a given point in the ellipse model 'par' at an angle 'th_opt'.
801- float x (0.0 ), y (0.0 );
802- get_ellipse_point (par, th_opt, x, y);
803- Eigen::Vector2f distanceVector (u - x, v - y);
804- return distanceVector;
805- }
806-
807- // ////////////////////////////////////////////////////////////////////////
808- template <typename PointT>
809- float inline pcl::SampleConsensusModelEllipse3D<PointT>::golden_section_search(
810- const Eigen::VectorXf& par,
811- float u,
812- float v,
813- float th_min,
814- float th_max,
815- float epsilon)
816- {
817- /*
818- * Golden section search
819- */
820-
821- constexpr float phi (1 .61803398874989484820f ); // Golden ratio
822-
823- // tl (theta lower bound), tu (theta upper bound)
824- float tl (th_min), tu (th_max);
825- float ta = tl + (tu - tl) * (1 - 1 / phi);
826- float tb = tl + (tu - tl) * 1 / phi;
827-
828- while ((tu - tl) > epsilon) {
829-
830- // theta a
831- float x_a (0.0 ), y_a (0.0 );
832- get_ellipse_point (par, ta, x_a, y_a);
833- float squared_dist_ta = (u - x_a) * (u - x_a) + (v - y_a) * (v - y_a);
834-
835- // theta b
836- float x_b (0.0 ), y_b (0.0 );
837- get_ellipse_point (par, tb, x_b, y_b);
838- float squared_dist_tb = (u - x_b) * (u - x_b) + (v - y_b) * (v - y_b);
839-
840- if (squared_dist_ta < squared_dist_tb) {
841- tu = tb;
842- tb = ta;
843- ta = tl + (tu - tl) * (1 - 1 / phi);
844- }
845- else if (squared_dist_ta > squared_dist_tb) {
846- tl = ta;
847- ta = tb;
848- tb = tl + (tu - tl) * 1 / phi;
849- }
850- else {
851- tl = ta;
852- tu = tb;
853- ta = tl + (tu - tl) * (1 - 1 / phi);
854- tb = tl + (tu - tl) * 1 / phi;
855- }
856- }
857- return (tl + tu) / 2.0 ;
858- }
859-
860-
861687#define PCL_INSTANTIATE_SampleConsensusModelEllipse3D (T ) template class PCL_EXPORTS pcl::SampleConsensusModelEllipse3D<T>;
0 commit comments