00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 #ifndef PCL_SAMPLE_CONSENSUS_MODEL_H_
00042 #define PCL_SAMPLE_CONSENSUS_MODEL_H_
00043
00044 #include <cfloat>
00045 #include <ctime>
00046 #include <limits.h>
00047 #include <set>
00048
00049 #include <pcl/console/print.h>
00050 #include <pcl/point_cloud.h>
00051 #include <pcl/sample_consensus/boost.h>
00052 #include <pcl/sample_consensus/model_types.h>
00053
00054 #include <pcl/search/search.h>
00055
00056 namespace pcl
00057 {
00058 template<class T> class ProgressiveSampleConsensus;
00059
00065 template <typename PointT>
00066 class SampleConsensusModel
00067 {
00068 public:
00069 typedef typename pcl::PointCloud<PointT> PointCloud;
00070 typedef typename pcl::PointCloud<PointT>::ConstPtr PointCloudConstPtr;
00071 typedef typename pcl::PointCloud<PointT>::Ptr PointCloudPtr;
00072 typedef typename pcl::search::Search<PointT>::Ptr SearchPtr;
00073
00074 typedef boost::shared_ptr<SampleConsensusModel> Ptr;
00075 typedef boost::shared_ptr<const SampleConsensusModel> ConstPtr;
00076
00077 protected:
00081 SampleConsensusModel (bool random = false)
00082 : input_ ()
00083 , indices_ ()
00084 , radius_min_ (-std::numeric_limits<double>::max ())
00085 , radius_max_ (std::numeric_limits<double>::max ())
00086 , samples_radius_ (0.)
00087 , samples_radius_search_ ()
00088 , shuffled_indices_ ()
00089 , rng_alg_ ()
00090 , rng_dist_ (new boost::uniform_int<> (0, std::numeric_limits<int>::max ()))
00091 , rng_gen_ ()
00092 , error_sqr_dists_ ()
00093 {
00094
00095 if (random)
00096 rng_alg_.seed (static_cast<unsigned> (std::time(0)));
00097 else
00098 rng_alg_.seed (12345u);
00099
00100 rng_gen_.reset (new boost::variate_generator<boost::mt19937&, boost::uniform_int<> > (rng_alg_, *rng_dist_));
00101 }
00102
00103 public:
00108 SampleConsensusModel (const PointCloudConstPtr &cloud, bool random = false)
00109 : input_ ()
00110 , indices_ ()
00111 , radius_min_ (-std::numeric_limits<double>::max ())
00112 , radius_max_ (std::numeric_limits<double>::max ())
00113 , samples_radius_ (0.)
00114 , samples_radius_search_ ()
00115 , shuffled_indices_ ()
00116 , rng_alg_ ()
00117 , rng_dist_ (new boost::uniform_int<> (0, std::numeric_limits<int>::max ()))
00118 , rng_gen_ ()
00119 , error_sqr_dists_ ()
00120 {
00121 if (random)
00122 rng_alg_.seed (static_cast<unsigned> (std::time (0)));
00123 else
00124 rng_alg_.seed (12345u);
00125
00126
00127 setInputCloud (cloud);
00128
00129
00130 rng_gen_.reset (new boost::variate_generator<boost::mt19937&, boost::uniform_int<> > (rng_alg_, *rng_dist_));
00131 }
00132
00138 SampleConsensusModel (const PointCloudConstPtr &cloud,
00139 const std::vector<int> &indices,
00140 bool random = false)
00141 : input_ (cloud)
00142 , indices_ (new std::vector<int> (indices))
00143 , radius_min_ (-std::numeric_limits<double>::max ())
00144 , radius_max_ (std::numeric_limits<double>::max ())
00145 , samples_radius_ (0.)
00146 , samples_radius_search_ ()
00147 , shuffled_indices_ ()
00148 , rng_alg_ ()
00149 , rng_dist_ (new boost::uniform_int<> (0, std::numeric_limits<int>::max ()))
00150 , rng_gen_ ()
00151 , error_sqr_dists_ ()
00152 {
00153 if (random)
00154 rng_alg_.seed (static_cast<unsigned> (std::time(0)));
00155 else
00156 rng_alg_.seed (12345u);
00157
00158 if (indices_->size () > input_->points.size ())
00159 {
00160 PCL_ERROR ("[pcl::SampleConsensusModel] Invalid index vector given with size %zu while the input PointCloud has size %zu!\n", indices_->size (), input_->points.size ());
00161 indices_->clear ();
00162 }
00163 shuffled_indices_ = *indices_;
00164
00165
00166 rng_gen_.reset (new boost::variate_generator<boost::mt19937&, boost::uniform_int<> > (rng_alg_, *rng_dist_));
00167 };
00168
00170 virtual ~SampleConsensusModel () {};
00171
00177 virtual void
00178 getSamples (int &iterations, std::vector<int> &samples)
00179 {
00180
00181 if (indices_->size () < getSampleSize ())
00182 {
00183 PCL_ERROR ("[pcl::SampleConsensusModel::getSamples] Can not select %zu unique points out of %zu!\n",
00184 samples.size (), indices_->size ());
00185
00186 samples.clear ();
00187 iterations = INT_MAX - 1;
00188 return;
00189 }
00190
00191
00192 samples.resize (getSampleSize ());
00193 for (unsigned int iter = 0; iter < max_sample_checks_; ++iter)
00194 {
00195
00196 if (samples_radius_ < std::numeric_limits<double>::epsilon ())
00197 SampleConsensusModel<PointT>::drawIndexSample (samples);
00198 else
00199 SampleConsensusModel<PointT>::drawIndexSampleRadius (samples);
00200
00201
00202 if (isSampleGood (samples))
00203 {
00204 PCL_DEBUG ("[pcl::SampleConsensusModel::getSamples] Selected %zu samples.\n", samples.size ());
00205 return;
00206 }
00207 }
00208 PCL_DEBUG ("[pcl::SampleConsensusModel::getSamples] WARNING: Could not select %d sample points in %d iterations!\n", getSampleSize (), max_sample_checks_);
00209 samples.clear ();
00210 }
00211
00219 virtual bool
00220 computeModelCoefficients (const std::vector<int> &samples,
00221 Eigen::VectorXf &model_coefficients) = 0;
00222
00233 virtual void
00234 optimizeModelCoefficients (const std::vector<int> &inliers,
00235 const Eigen::VectorXf &model_coefficients,
00236 Eigen::VectorXf &optimized_coefficients) = 0;
00237
00243 virtual void
00244 getDistancesToModel (const Eigen::VectorXf &model_coefficients,
00245 std::vector<double> &distances) = 0;
00246
00255 virtual void
00256 selectWithinDistance (const Eigen::VectorXf &model_coefficients,
00257 const double threshold,
00258 std::vector<int> &inliers) = 0;
00259
00269 virtual int
00270 countWithinDistance (const Eigen::VectorXf &model_coefficients,
00271 const double threshold) = 0;
00272
00281 virtual void
00282 projectPoints (const std::vector<int> &inliers,
00283 const Eigen::VectorXf &model_coefficients,
00284 PointCloud &projected_points,
00285 bool copy_data_fields = true) = 0;
00286
00295 virtual bool
00296 doSamplesVerifyModel (const std::set<int> &indices,
00297 const Eigen::VectorXf &model_coefficients,
00298 const double threshold) = 0;
00299
00303 inline virtual void
00304 setInputCloud (const PointCloudConstPtr &cloud)
00305 {
00306 input_ = cloud;
00307 if (!indices_)
00308 indices_.reset (new std::vector<int> ());
00309 if (indices_->empty ())
00310 {
00311
00312 indices_->resize (cloud->points.size ());
00313 for (size_t i = 0; i < cloud->points.size (); ++i)
00314 (*indices_)[i] = static_cast<int> (i);
00315 }
00316 shuffled_indices_ = *indices_;
00317 }
00318
00320 inline PointCloudConstPtr
00321 getInputCloud () const { return (input_); }
00322
00326 inline void
00327 setIndices (const boost::shared_ptr <std::vector<int> > &indices)
00328 {
00329 indices_ = indices;
00330 shuffled_indices_ = *indices_;
00331 }
00332
00336 inline void
00337 setIndices (const std::vector<int> &indices)
00338 {
00339 indices_.reset (new std::vector<int> (indices));
00340 shuffled_indices_ = indices;
00341 }
00342
00344 inline boost::shared_ptr <std::vector<int> >
00345 getIndices () const { return (indices_); }
00346
00348 virtual SacModel
00349 getModelType () const = 0;
00350
00352 inline unsigned int
00353 getSampleSize () const
00354 {
00355 std::map<pcl::SacModel, unsigned int>::const_iterator it = SAC_SAMPLE_SIZE.find (getModelType ());
00356 if (it == SAC_SAMPLE_SIZE.end ())
00357 throw InvalidSACModelTypeException ("No sample size defined for given model type!\n");
00358 return (it->second);
00359 }
00360
00367 inline void
00368 setRadiusLimits (const double &min_radius, const double &max_radius)
00369 {
00370 radius_min_ = min_radius;
00371 radius_max_ = max_radius;
00372 }
00373
00380 inline void
00381 getRadiusLimits (double &min_radius, double &max_radius)
00382 {
00383 min_radius = radius_min_;
00384 max_radius = radius_max_;
00385 }
00386
00390 inline void
00391 setSamplesMaxDist (const double &radius, SearchPtr search)
00392 {
00393 samples_radius_ = radius;
00394 samples_radius_search_ = search;
00395 }
00396
00401 inline void
00402 getSamplesMaxDist (double &radius)
00403 {
00404 radius = samples_radius_;
00405 }
00406
00407 friend class ProgressiveSampleConsensus<PointT>;
00408
00412 inline double
00413 computeVariance (const std::vector<double> &error_sqr_dists)
00414 {
00415 std::vector<double> dists (error_sqr_dists);
00416 std::sort (dists.begin (), dists.end ());
00417 double median_error_sqr = dists[dists.size () >> 1];
00418 return (2.1981 * median_error_sqr);
00419 }
00420
00425 inline double
00426 computeVariance ()
00427 {
00428 if (error_sqr_dists_.empty ())
00429 {
00430 PCL_ERROR ("[pcl::SampleConsensusModel::computeVariance] The variance of the Sample Consensus model distances cannot be estimated, as the model has not been computed yet. Please compute the model first or at least run selectWithinDistance before continuing. Returning NAN!\n");
00431 return (std::numeric_limits<double>::quiet_NaN ());
00432 }
00433 return (computeVariance (error_sqr_dists_));
00434 }
00435
00436 protected:
00440 inline void
00441 drawIndexSample (std::vector<int> &sample)
00442 {
00443 size_t sample_size = sample.size ();
00444 size_t index_size = shuffled_indices_.size ();
00445 for (unsigned int i = 0; i < sample_size; ++i)
00446
00447
00448
00449 std::swap (shuffled_indices_[i], shuffled_indices_[i + (rnd () % (index_size - i))]);
00450 std::copy (shuffled_indices_.begin (), shuffled_indices_.begin () + sample_size, sample.begin ());
00451 }
00452
00457 inline void
00458 drawIndexSampleRadius (std::vector<int> &sample)
00459 {
00460 size_t sample_size = sample.size ();
00461 size_t index_size = shuffled_indices_.size ();
00462
00463 std::swap (shuffled_indices_[0], shuffled_indices_[0 + (rnd () % (index_size - 0))]);
00464
00465
00466 std::vector<int> indices;
00467 std::vector<float> sqr_dists;
00468
00469
00470
00471
00472
00473
00474 samples_radius_search_->radiusSearch (input_->at(shuffled_indices_[0]),
00475 samples_radius_, indices, sqr_dists );
00476
00477 if (indices.size () < sample_size - 1)
00478 {
00479
00480 for(unsigned int i = 1; i < sample_size; ++i)
00481 shuffled_indices_[i] = shuffled_indices_[0];
00482 }
00483 else
00484 {
00485 for (unsigned int i = 0; i < sample_size-1; ++i)
00486 std::swap (indices[i], indices[i + (rnd () % (indices.size () - i))]);
00487 for (unsigned int i = 1; i < sample_size; ++i)
00488 shuffled_indices_[i] = indices[i-1];
00489 }
00490
00491 std::copy (shuffled_indices_.begin (), shuffled_indices_.begin () + sample_size, sample.begin ());
00492 }
00493
00497 virtual inline bool
00498 isModelValid (const Eigen::VectorXf &model_coefficients) = 0;
00499
00504 virtual bool
00505 isSampleGood (const std::vector<int> &samples) const = 0;
00506
00508 PointCloudConstPtr input_;
00509
00511 boost::shared_ptr <std::vector<int> > indices_;
00512
00514 static const unsigned int max_sample_checks_ = 1000;
00515
00519 double radius_min_, radius_max_;
00520
00522 double samples_radius_;
00523
00525 SearchPtr samples_radius_search_;
00526
00528 std::vector<int> shuffled_indices_;
00529
00531 boost::mt19937 rng_alg_;
00532
00534 boost::shared_ptr<boost::uniform_int<> > rng_dist_;
00535
00537 boost::shared_ptr<boost::variate_generator< boost::mt19937&, boost::uniform_int<> > > rng_gen_;
00538
00540 std::vector<double> error_sqr_dists_;
00541
00543 inline int
00544 rnd ()
00545 {
00546 return ((*rng_gen_) ());
00547 }
00548 public:
00549 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
00550 };
00551
00555 template <typename PointT, typename PointNT>
00556 class SampleConsensusModelFromNormals
00557 {
00558 public:
00559 typedef typename pcl::PointCloud<PointNT>::ConstPtr PointCloudNConstPtr;
00560 typedef typename pcl::PointCloud<PointNT>::Ptr PointCloudNPtr;
00561
00562 typedef boost::shared_ptr<SampleConsensusModelFromNormals> Ptr;
00563 typedef boost::shared_ptr<const SampleConsensusModelFromNormals> ConstPtr;
00564
00566 SampleConsensusModelFromNormals () : normal_distance_weight_ (0.0), normals_ () {};
00567
00569 virtual ~SampleConsensusModelFromNormals () {}
00570
00576 inline void
00577 setNormalDistanceWeight (const double w)
00578 {
00579 normal_distance_weight_ = w;
00580 }
00581
00583 inline double
00584 getNormalDistanceWeight () { return (normal_distance_weight_); }
00585
00591 inline void
00592 setInputNormals (const PointCloudNConstPtr &normals)
00593 {
00594 normals_ = normals;
00595 }
00596
00598 inline PointCloudNConstPtr
00599 getInputNormals () { return (normals_); }
00600
00601 protected:
00605 double normal_distance_weight_;
00606
00610 PointCloudNConstPtr normals_;
00611 };
00612
00617 template<typename _Scalar, int NX=Eigen::Dynamic, int NY=Eigen::Dynamic>
00618 struct Functor
00619 {
00620 typedef _Scalar Scalar;
00621 enum
00622 {
00623 InputsAtCompileTime = NX,
00624 ValuesAtCompileTime = NY
00625 };
00626
00627 typedef Eigen::Matrix<Scalar,ValuesAtCompileTime,1> ValueType;
00628 typedef Eigen::Matrix<Scalar,InputsAtCompileTime,1> InputType;
00629 typedef Eigen::Matrix<Scalar,ValuesAtCompileTime,InputsAtCompileTime> JacobianType;
00630
00632 Functor () : m_data_points_ (ValuesAtCompileTime) {}
00633
00637 Functor (int m_data_points) : m_data_points_ (m_data_points) {}
00638
00639 virtual ~Functor () {}
00640
00642 int
00643 values () const { return (m_data_points_); }
00644
00645 private:
00646 const int m_data_points_;
00647 };
00648 }
00649
00650 #endif //#ifndef PCL_SAMPLE_CONSENSUS_MODEL_H_