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>
16+ #include < pcl/common/io.h>
17+ #include < pcl/common/copy_point.h>
1718
1819#include < Eigen/Eigenvalues>
1920
@@ -30,9 +31,9 @@ pcl::SampleConsensusModelEllipse3D<PointT>::isSampleGood (
3031 }
3132
3233 // Use three points out of the 6 samples
33- const Eigen::Vector3d p0 ((*input_)[samples[ 0 ]]. x , (*input_)[samples[0 ]].y , (*input_)[samples[ 0 ]]. z );
34- const Eigen::Vector3d p1 ((*input_)[samples[ 1 ]]. x , (*input_)[samples[1 ]].y , (*input_)[samples[ 1 ]]. z );
35- const Eigen::Vector3d p2 ((*input_)[samples[ 2 ]]. x , (*input_)[samples[2 ]].y , (*input_)[samples[ 2 ]]. z );
34+ const Eigen::Vector3f p0 = (*input_)[samples[0 ]].getVector3fMap ( );
35+ const Eigen::Vector3f p1 = (*input_)[samples[1 ]].getVector3fMap ( );
36+ const Eigen::Vector3f p2 = (*input_)[samples[2 ]].getVector3fMap ( );
3637
3738 // Check if the squared norm of the cross-product is non-zero, otherwise
3839 // common_helper_vec, which plays an important role in computeModelCoefficients,
@@ -225,7 +226,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::computeModelCoefficients (const Indi
225226 // Retrieve the ellipse point at the tilt angle t (par_t), along the local x-axis
226227 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, par_h, par_k, par_t ).finished ();
227228 Eigen::Vector3f p_th_ (0.0 , 0.0 , 0.0 );
228- get_ellipse_point (params, par_t , p_th_ (0 ), p_th_ (1 ));
229+ internal::getEllipsePoint (params, par_t , p_th_ (0 ), p_th_ (1 ));
229230
230231 // Redefinition of the x-axis of the ellipse's local reference frame
231232 x_axis = (Rot * p_th_).normalized ();
@@ -298,7 +299,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::getDistancesToModel (const Eigen::Ve
298299 // Calculate the shortest distance from the point to the ellipse which is given by
299300 // the norm of a vector that is normal to the ellipse tangent calculated at the
300301 // point it intersects the tangent.
301- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
302+ const Eigen::Vector2f distanceVector = internal::dVec2Ellipse (params, p_ (0 ), p_ (1 ), th_opt);
302303
303304 distances[i] = distanceVector.norm ();
304305 }
@@ -357,7 +358,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::selectWithinDistance (
357358 // point it intersects the tangent.
358359 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
359360 float th_opt;
360- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
361+ const Eigen::Vector2f distanceVector = internal::dVec2Ellipse (params, p_ (0 ), p_ (1 ), th_opt);
361362
362363 const double sqr_dist = distanceVector.squaredNorm ();
363364 if (sqr_dist < squared_threshold)
@@ -416,7 +417,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::countWithinDistance (
416417 // point it intersects the tangent.
417418 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
418419 float th_opt;
419- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
420+ const Eigen::Vector2f distanceVector = internal::dVec2Ellipse (params, p_ (0 ), p_ (1 ), th_opt);
420421
421422 if (distanceVector.squaredNorm () < squared_threshold)
422423 nr_p++;
@@ -447,40 +448,14 @@ pcl::SampleConsensusModelEllipse3D<PointT>::optimizeModelCoefficients (
447448 return ;
448449 }
449450
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 ]);
451+ Eigen::ArrayXf x (inliers.size ()), y (inliers.size ()), z (inliers.size ());
452+ for (std::size_t i = 0 ; i < inliers.size (); ++i)
453+ {
454+ const auto & pt = (*input_)[inliers[i]];
455+ x[i] = pt.x ; y[i] = pt.y ; z[i] = pt.z ;
456+ }
457+
458+ internal::optimizeModelCoefficientsEllipse3D (x, y, z, model_coefficients, optimized_coefficients);
484459}
485460
486461// ////////////////////////////////////////////////////////////////////////
@@ -507,13 +482,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
507482 projected_points.width = input_->width ;
508483 projected_points.height = input_->height ;
509484
510- using FieldList = typename pcl::traits::fieldList<PointT>::type;
511- // Iterate over each point
512- for (std::size_t i = 0 ; i < projected_points.size (); ++i)
513- {
514- // Iterate over each dimension
515- pcl::for_each_type<FieldList>(NdConcatenateFunctor<PointT, PointT>((*input_)[i], projected_points[i]));
516- }
485+ pcl::copyPointCloud (*input_, projected_points);
517486
518487 // c : Ellipse Center
519488 const Eigen::Vector3f c (model_coefficients[0 ], model_coefficients[1 ], model_coefficients[2 ]);
@@ -540,7 +509,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
540509 for (std::size_t i = 0 ; i < inliers.size (); ++i)
541510 {
542511 // p : Sample Point
543- const Eigen::Vector3f p ((*input_)[(*indices_)[i]]. x , (*input_)[(*indices_)[i]]. y , (*input_)[(*indices_) [i]].z );
512+ const Eigen::Vector3f p = (*input_)[inliers [i]].getVector3fMap ( );
544513
545514 // Local coordinates of sample point p
546515 const Eigen::Vector3f p_ = Rot_T * (p - c);
@@ -551,17 +520,17 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
551520 // point it intersects the tangent.
552521 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
553522 float th_opt;
554- dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
523+ internal::dVec2Ellipse (params, p_ (0 ), p_ (1 ), th_opt);
555524
556525 // Retrieve the ellipse point at the tilt angle t, along the local x-axis
557526 Eigen::Vector3f k_ (0.0 , 0.0 , 0.0 );
558- get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
527+ internal::getEllipsePoint (params, th_opt, k_[0 ], k_[1 ]);
559528
560529 const Eigen::Vector3f k = c + Rot * k_;
561530
562- projected_points[i ].x = static_cast <float > (k[0 ]);
563- projected_points[i ].y = static_cast <float > (k[1 ]);
564- projected_points[i ].z = static_cast <float > (k[2 ]);
531+ projected_points[inliers[i] ].x = static_cast <float > (k[0 ]);
532+ projected_points[inliers[i] ].y = static_cast <float > (k[1 ]);
533+ projected_points[inliers[i] ].z = static_cast <float > (k[2 ]);
565534 }
566535 }
567536 else
@@ -571,11 +540,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
571540 projected_points.width = inliers.size ();
572541 projected_points.height = 1 ;
573542
574- using FieldList = typename pcl::traits::fieldList<PointT>::type;
575- // Iterate over each point
576- for (std::size_t i = 0 ; i < inliers.size (); ++i)
577- // Iterate over each dimension
578- pcl::for_each_type <FieldList> (NdConcatenateFunctor <PointT, PointT> ((*input_)[inliers[i]], projected_points[i]));
543+ pcl::copyPointCloud (*input_, inliers, projected_points);
579544
580545 // c : Ellipse Center
581546 const Eigen::Vector3f c (model_coefficients[0 ], model_coefficients[1 ], model_coefficients[2 ]);
@@ -602,7 +567,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
602567 for (std::size_t i = 0 ; i < inliers.size (); ++i)
603568 {
604569 // p : Sample Point
605- const Eigen::Vector3f p ((*input_)[(*indices_)[i]]. x , (*input_)[(*indices_)[i]]. y , (*input_)[(*indices_)[i]]. z );
570+ const Eigen::Vector3f p = projected_points[i]. getVector3fMap ( );
606571
607572 // Local coordinates of sample point p
608573 const Eigen::Vector3f p_ = Rot_T * (p - c);
@@ -613,12 +578,11 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
613578 // point it intersects the tangent.
614579 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
615580 float th_opt;
616- dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
581+ internal::dVec2Ellipse (params, p_ (0 ), p_ (1 ), th_opt);
617582
618583 // Retrieve the ellipse point at the tilt angle t, along the local x-axis
619- // // model_coefficients[5] = static_cast<float>(par_t);
620584 Eigen::Vector3f k_ (0.0 , 0.0 , 0.0 );
621- get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
585+ internal::getEllipsePoint (params, th_opt, k_[0 ], k_[1 ]);
622586
623587 const Eigen::Vector3f k = c + Rot * k_;
624588
@@ -679,7 +643,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::doSamplesVerifyModel (
679643 // point it intersects the tangent.
680644 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
681645 float th_opt;
682- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
646+ const Eigen::Vector2f distanceVector = internal::dVec2Ellipse (params, p_ (0 ), p_ (1 ), th_opt);
683647
684648 if (distanceVector.squaredNorm () > squared_threshold)
685649 return (false );
@@ -711,151 +675,4 @@ pcl::SampleConsensusModelEllipse3D<PointT>::isModelValid (const Eigen::VectorXf
711675}
712676
713677
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-
861678#define PCL_INSTANTIATE_SampleConsensusModelEllipse3D (T ) template class PCL_EXPORTS pcl::SampleConsensusModelEllipse3D<T>;
0 commit comments