44 * Point Cloud Library (PCL) - www.pointclouds.org
55 * Copyright (c) 2014-, Open Perception Inc.
66 *
7- * All rights reserved
7+ * All rights reserved.
88 */
99
1010#pragma once
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
20-
2121// ////////////////////////////////////////////////////////////////////////
2222template <typename PointT> bool
2323pcl::SampleConsensusModelEllipse3D<PointT>::isSampleGood (
@@ -30,9 +30,9 @@ pcl::SampleConsensusModelEllipse3D<PointT>::isSampleGood (
3030 }
3131
3232 // 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 );
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 );
3636
3737 // Check if the squared norm of the cross-product is non-zero, otherwise
3838 // common_helper_vec, which plays an important role in computeModelCoefficients,
@@ -225,7 +225,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::computeModelCoefficients (const Indi
225225 // Retrieve the ellipse point at the tilt angle t (par_t), along the local x-axis
226226 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, par_h, par_k, par_t ).finished ();
227227 Eigen::Vector3f p_th_ (0.0 , 0.0 , 0.0 );
228- get_ellipse_point (params, par_t , p_th_ (0 ), p_th_ (1 ));
228+ internal:: get_ellipse_point (params, par_t , p_th_ (0 ), p_th_ (1 ));
229229
230230 // Redefinition of the x-axis of the ellipse's local reference frame
231231 x_axis = (Rot * p_th_).normalized ();
@@ -241,7 +241,6 @@ pcl::SampleConsensusModelEllipse3D<PointT>::computeModelCoefficients (const Indi
241241 return (true );
242242}
243243
244-
245244// ////////////////////////////////////////////////////////////////////////
246245template <typename PointT> void
247246pcl::SampleConsensusModelEllipse3D<PointT>::getDistancesToModel (const Eigen::VectorXf &model_coefficients, std::vector<double > &distances) const
@@ -298,9 +297,14 @@ pcl::SampleConsensusModelEllipse3D<PointT>::getDistancesToModel (const Eigen::Ve
298297 // Calculate the shortest distance from the point to the ellipse which is given by
299298 // the norm of a vector that is normal to the ellipse tangent calculated at the
300299 // point it intersects the tangent.
301- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
300+ internal:: dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
302301
303- distances[i] = distanceVector.norm ();
302+ // Retrieve the ellipse point at the tilt angle t, along the local x-axis
303+ Eigen::Vector3f k_ (0.0 , 0.0 , 0.0 );
304+ internal::get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
305+
306+ const Eigen::Vector3f k = c + Rot * k_;
307+ distances[i] = (p - k).norm ();
304308 }
305309}
306310
@@ -357,9 +361,15 @@ pcl::SampleConsensusModelEllipse3D<PointT>::selectWithinDistance (
357361 // point it intersects the tangent.
358362 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
359363 float th_opt;
360- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
364+ internal::dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
365+
366+ // Retrieve the ellipse point at the tilt angle t, along the local x-axis
367+ Eigen::Vector3f k_ (0.0 , 0.0 , 0.0 );
368+ internal::get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
361369
362- const double sqr_dist = distanceVector.squaredNorm ();
370+ const Eigen::Vector3f k = c + Rot * k_;
371+
372+ const double sqr_dist = (p - k).squaredNorm ();
363373 if (sqr_dist < squared_threshold)
364374 {
365375 // Returns the indices of the points whose distances are smaller than the threshold
@@ -416,9 +426,15 @@ pcl::SampleConsensusModelEllipse3D<PointT>::countWithinDistance (
416426 // point it intersects the tangent.
417427 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
418428 float th_opt;
419- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
429+ internal::dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
430+
431+ // Retrieve the ellipse point at the tilt angle t, along the local x-axis
432+ Eigen::Vector3f k_ (0.0 , 0.0 , 0.0 );
433+ internal::get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
434+
435+ const Eigen::Vector3f k = c + Rot * k_;
420436
421- if (distanceVector .squaredNorm () < squared_threshold)
437+ if ((p - k) .squaredNorm () < squared_threshold)
422438 nr_p++;
423439 }
424440 return (nr_p);
@@ -427,13 +443,9 @@ pcl::SampleConsensusModelEllipse3D<PointT>::countWithinDistance (
427443// ////////////////////////////////////////////////////////////////////////
428444template <typename PointT> void
429445pcl::SampleConsensusModelEllipse3D<PointT>::optimizeModelCoefficients (
430- const Indices &inliers,
431- const Eigen::VectorXf &model_coefficients,
432- Eigen::VectorXf &optimized_coefficients) const
446+ const Indices &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) const
433447{
434- optimized_coefficients = model_coefficients;
435-
436- // Needs a set of valid model coefficients
448+ // Needs a valid set of model coefficients
437449 if (!isModelValid (model_coefficients))
438450 {
439451 PCL_ERROR (" [pcl::SampleConsensusModelEllipse3D::optimizeModelCoefficients] Given model is invalid!\n " );
@@ -443,44 +455,20 @@ pcl::SampleConsensusModelEllipse3D<PointT>::optimizeModelCoefficients (
443455 // Need more than the minimum sample size to make a difference
444456 if (inliers.size () <= sample_size_)
445457 {
446- PCL_ERROR (" [pcl::SampleConsensusModelEllipse3D::optimizeModelCoefficients] Not enough inliers to refine/optimize the model's coefficients (%lu)! Returning the same coefficients.\n " , inliers.size ());
458+ PCL_ERROR (" [pcl::SampleConsensusModelEllipse3D::optimizeModelCoefficients] Not enough inliers found to optimize model coefficients (%lu)! Returning the same coefficients.\n " , inliers.size ());
459+ optimized_coefficients = model_coefficients;
447460 return ;
448461 }
449462
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 ]);
463+ Eigen::ArrayXf x (inliers.size ()), y (inliers.size ()), z (inliers.size ());
464+ for (std::size_t i = 0 ; i < inliers.size (); ++i)
465+ {
466+ const auto & pt = (*input_)[inliers[i]];
467+ x[i] = pt.x ; y[i] = pt.y ; z[i] = pt.z ;
468+ }
469+
470+ optimized_coefficients = model_coefficients;
471+ internal::optimizeModelCoefficientsEllipse3D (optimized_coefficients, x, y, z);
484472}
485473
486474// ////////////////////////////////////////////////////////////////////////
@@ -540,7 +528,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
540528 for (std::size_t i = 0 ; i < inliers.size (); ++i)
541529 {
542530 // p : Sample Point
543- const Eigen::Vector3f p ((*input_)[(*indices_) [i]].x , (*input_)[(*indices_) [i]].y , (*input_)[(*indices_) [i]].z );
531+ const Eigen::Vector3f p ((*input_)[inliers [i]].x , (*input_)[inliers [i]].y , (*input_)[inliers [i]].z );
544532
545533 // Local coordinates of sample point p
546534 const Eigen::Vector3f p_ = Rot_T * (p - c);
@@ -551,17 +539,17 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
551539 // point it intersects the tangent.
552540 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
553541 float th_opt;
554- dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
542+ internal:: dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
555543
556544 // Retrieve the ellipse point at the tilt angle t, along the local x-axis
557545 Eigen::Vector3f k_ (0.0 , 0.0 , 0.0 );
558- get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
546+ internal:: get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
559547
560548 const Eigen::Vector3f k = c + Rot * k_;
561549
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 ]);
550+ projected_points[inliers[i] ].x = static_cast <float > (k[0 ]);
551+ projected_points[inliers[i] ].y = static_cast <float > (k[1 ]);
552+ projected_points[inliers[i] ].z = static_cast <float > (k[2 ]);
565553 }
566554 }
567555 else
@@ -602,7 +590,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
602590 for (std::size_t i = 0 ; i < inliers.size (); ++i)
603591 {
604592 // p : Sample Point
605- const Eigen::Vector3f p ((*input_)[(*indices_)[i]] .x , (*input_)[(*indices_)[i]] .y , (*input_)[(*indices_)[i] ].z );
593+ const Eigen::Vector3f p (projected_points[i] .x , projected_points[i] .y , projected_points[i ].z );
606594
607595 // Local coordinates of sample point p
608596 const Eigen::Vector3f p_ = Rot_T * (p - c);
@@ -613,12 +601,11 @@ pcl::SampleConsensusModelEllipse3D<PointT>::projectPoints (
613601 // point it intersects the tangent.
614602 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
615603 float th_opt;
616- dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
604+ internal:: dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
617605
618606 // Retrieve the ellipse point at the tilt angle t, along the local x-axis
619- // // model_coefficients[5] = static_cast<float>(par_t);
620607 Eigen::Vector3f k_ (0.0 , 0.0 , 0.0 );
621- get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
608+ internal:: get_ellipse_point (params, th_opt, k_[0 ], k_[1 ]);
622609
623610 const Eigen::Vector3f k = c + Rot * k_;
624611
@@ -679,7 +666,7 @@ pcl::SampleConsensusModelEllipse3D<PointT>::doSamplesVerifyModel (
679666 // point it intersects the tangent.
680667 const Eigen::VectorXf params = (Eigen::VectorXf (5 ) << par_a, par_b, 0.0 , 0.0 , 0.0 ).finished ();
681668 float th_opt;
682- const Eigen::Vector2f distanceVector = dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
669+ const Eigen::Vector2f distanceVector = internal:: dvec2ellipse (params, p_ (0 ), p_ (1 ), th_opt);
683670
684671 if (distanceVector.squaredNorm () > squared_threshold)
685672 return (false );
@@ -710,152 +697,4 @@ pcl::SampleConsensusModelEllipse3D<PointT>::isModelValid (const Eigen::VectorXf
710697 return (true );
711698}
712699
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-
861700#define PCL_INSTANTIATE_SampleConsensusModelEllipse3D (T ) template class PCL_EXPORTS pcl::SampleConsensusModelEllipse3D<T>;
0 commit comments