Point Cloud Library (PCL)  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
extract_clusters.h
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Point Cloud Library (PCL) - www.pointclouds.org
00005  *  Copyright (c) 2010-2011, Willow Garage, Inc.
00006  *
00007  *  All rights reserved.
00008  *
00009  *  Redistribution and use in source and binary forms, with or without
00010  *  modification, are permitted provided that the following conditions
00011  *  are met:
00012  *
00013  *   * Redistributions of source code must retain the above copyright
00014  *     notice, this list of conditions and the following disclaimer.
00015  *   * Redistributions in binary form must reproduce the above
00016  *     copyright notice, this list of conditions and the following
00017  *     disclaimer in the documentation and/or other materials provided
00018  *     with the distribution.
00019  *   * Neither the name of Willow Garage, Inc. nor the names of its
00020  *     contributors may be used to endorse or promote products derived
00021  *     from this software without specific prior written permission.
00022  *
00023  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034  *  POSSIBILITY OF SUCH DAMAGE.
00035  *
00036  * $Id: extract_clusters.h 3752 2011-12-31 23:21:48Z rusu $
00037  *
00038  */
00039 
00040 #ifndef PCL_EXTRACT_CLUSTERS_H_
00041 #define PCL_EXTRACT_CLUSTERS_H_
00042 
00043 #include <pcl/pcl_base.h>
00044 
00045 #include "pcl/search/pcl_search.h"
00046 
00047 namespace pcl
00048 {
00050 
00060   template <typename PointT> void 
00061   extractEuclideanClusters (
00062       const PointCloud<PointT> &cloud, const boost::shared_ptr<search::Search<PointT> > &tree, 
00063       float tolerance, std::vector<PointIndices> &clusters, 
00064       unsigned int min_pts_per_cluster = 1, unsigned int max_pts_per_cluster = (std::numeric_limits<int>::max) ());
00065 
00067 
00078   template <typename PointT> void 
00079   extractEuclideanClusters (
00080       const PointCloud<PointT> &cloud, const std::vector<int> &indices, 
00081       const boost::shared_ptr<search::Search<PointT> > &tree, float tolerance, std::vector<PointIndices> &clusters, 
00082       unsigned int min_pts_per_cluster = 1, unsigned int max_pts_per_cluster = (std::numeric_limits<int>::max) ());
00083 
00085 
00098   template <typename PointT, typename Normal> void 
00099   extractEuclideanClusters (
00100       const PointCloud<PointT> &cloud, const PointCloud<Normal> &normals, 
00101       float tolerance, const boost::shared_ptr<KdTree<PointT> > &tree, 
00102       std::vector<PointIndices> &clusters, double eps_angle, 
00103       unsigned int min_pts_per_cluster = 1, 
00104       unsigned int max_pts_per_cluster = (std::numeric_limits<int>::max) ())
00105   {
00106     if (tree->getInputCloud ()->points.size () != cloud.points.size ())
00107     {
00108       PCL_ERROR ("[pcl::extractEuclideanClusters] Tree built for a different point cloud dataset (%lu) than the input cloud (%lu)!\n", (unsigned long)tree->getInputCloud ()->points.size (), (unsigned long)cloud.points.size ());
00109       return;
00110     }
00111     if (cloud.points.size () != normals.points.size ())
00112     {
00113       PCL_ERROR ("[pcl::extractEuclideanClusters] Number of points in the input point cloud (%lu) different than normals (%lu)!\n", (unsigned long)cloud.points.size (), (unsigned long)normals.points.size ());
00114       return;
00115     }
00116 
00117     // Create a bool vector of processed point indices, and initialize it to false
00118     std::vector<bool> processed (cloud.points.size (), false);
00119 
00120     std::vector<int> nn_indices;
00121     std::vector<float> nn_distances;
00122     // Process all points in the indices vector
00123     for (size_t i = 0; i < cloud.points.size (); ++i)
00124     {
00125       if (processed[i])
00126         continue;
00127 
00128       std::vector<unsigned int> seed_queue;
00129       int sq_idx = 0;
00130       seed_queue.push_back (i);
00131 
00132       processed[i] = true;
00133 
00134       while (sq_idx < (int)seed_queue.size ())
00135       {
00136         // Search for sq_idx
00137         if (!tree->radiusSearch (seed_queue[sq_idx], tolerance, nn_indices, nn_distances))
00138         {
00139           sq_idx++;
00140           continue;
00141         }
00142 
00143         for (size_t j = 1; j < nn_indices.size (); ++j)             // nn_indices[0] should be sq_idx
00144         {
00145           if (processed[nn_indices[j]])                         // Has this point been processed before ?
00146             continue;
00147 
00148           //processed[nn_indices[j]] = true;
00149           // [-1;1]
00150           double dot_p = normals.points[i].normal[0] * normals.points[nn_indices[j]].normal[0] +
00151                          normals.points[i].normal[1] * normals.points[nn_indices[j]].normal[1] +
00152                          normals.points[i].normal[2] * normals.points[nn_indices[j]].normal[2];
00153           if ( fabs (acos (dot_p)) < eps_angle )
00154           {
00155             processed[nn_indices[j]] = true;
00156             seed_queue.push_back (nn_indices[j]);
00157           }
00158         }
00159 
00160         sq_idx++;
00161       }
00162 
00163       // If this queue is satisfactory, add to the clusters
00164       if (seed_queue.size () >= min_pts_per_cluster && seed_queue.size () <= max_pts_per_cluster)
00165       {
00166         pcl::PointIndices r;
00167         r.indices.resize (seed_queue.size ());
00168         for (size_t j = 0; j < seed_queue.size (); ++j)
00169           r.indices[j] = seed_queue[j];
00170 
00171         std::sort (r.indices.begin (), r.indices.end ());
00172         r.indices.erase (std::unique (r.indices.begin (), r.indices.end ()), r.indices.end ());
00173 
00174         r.header = cloud.header;
00175         clusters.push_back (r);   // We could avoid a copy by working directly in the vector
00176       }
00177     }
00178   }
00179 
00180 
00182 
00196   template <typename PointT, typename Normal> 
00197   void extractEuclideanClusters (
00198       const PointCloud<PointT> &cloud, const PointCloud<Normal> &normals, 
00199       const std::vector<int> &indices, const boost::shared_ptr<KdTree<PointT> > &tree, 
00200       float tolerance, std::vector<PointIndices> &clusters, double eps_angle, 
00201       unsigned int min_pts_per_cluster = 1, 
00202       unsigned int max_pts_per_cluster = (std::numeric_limits<int>::max) ())
00203   {
00204     // \note If the tree was created over <cloud, indices>, we guarantee a 1-1 mapping between what the tree returns
00205     //and indices[i]
00206     if (tree->getInputCloud ()->points.size () != cloud.points.size ())
00207     {
00208       PCL_ERROR ("[pcl::extractEuclideanClusters] Tree built for a different point cloud dataset (%lu) than the input cloud (%lu)!\n", (unsigned long)tree->getInputCloud ()->points.size (), (unsigned long)cloud.points.size ());
00209       return;
00210     }
00211     if (tree->getIndices ()->size () != indices.size ())
00212     {
00213       PCL_ERROR ("[pcl::extractEuclideanClusters] Tree built for a different set of indices (%lu) than the input set (%lu)!\n", (unsigned long)tree->getIndices ()->size (), (unsigned long)indices.size ());
00214       return;
00215     }
00216     if (cloud.points.size () != normals.points.size ())
00217     {
00218       PCL_ERROR ("[pcl::extractEuclideanClusters] Number of points in the input point cloud (%lu) different than normals (%lu)!\n", (unsigned long)cloud.points.size (), (unsigned long)normals.points.size ());
00219       return;
00220     }
00221     // Create a bool vector of processed point indices, and initialize it to false
00222     std::vector<bool> processed (indices.size (), false);
00223 
00224     std::vector<int> nn_indices;
00225     std::vector<float> nn_distances;
00226     // Process all points in the indices vector
00227     for (size_t i = 0; i < indices.size (); ++i)
00228     {
00229       if (processed[i])
00230         continue;
00231 
00232       std::vector<int> seed_queue;
00233       int sq_idx = 0;
00234       seed_queue.push_back (i);
00235 
00236       processed[i] = true;
00237 
00238       while (sq_idx < (int)seed_queue.size ())
00239       {
00240         // Search for sq_idx
00241         if (!tree->radiusSearch (seed_queue[sq_idx], tolerance, nn_indices, nn_distances))
00242         {
00243           sq_idx++;
00244           continue;
00245         }
00246 
00247         for (size_t j = 1; j < nn_indices.size (); ++j)             // nn_indices[0] should be sq_idx
00248         {
00249           if (processed[nn_indices[j]])                             // Has this point been processed before ?
00250             continue;
00251 
00252           //processed[nn_indices[j]] = true;
00253           // [-1;1]
00254           double dot_p =
00255             normals.points[indices[i]].normal[0] * normals.points[indices[nn_indices[j]]].normal[0] +
00256             normals.points[indices[i]].normal[1] * normals.points[indices[nn_indices[j]]].normal[1] +
00257             normals.points[indices[i]].normal[2] * normals.points[indices[nn_indices[j]]].normal[2];
00258           if ( fabs (acos (dot_p)) < eps_angle )
00259           {
00260             processed[nn_indices[j]] = true;
00261             seed_queue.push_back (nn_indices[j]);
00262           }
00263         }
00264 
00265         sq_idx++;
00266       }
00267 
00268       // If this queue is satisfactory, add to the clusters
00269       if (seed_queue.size () >= min_pts_per_cluster && seed_queue.size () <= max_pts_per_cluster)
00270       {
00271         pcl::PointIndices r;
00272         r.indices.resize (seed_queue.size ());
00273         for (size_t j = 0; j < seed_queue.size (); ++j)
00274           r.indices[j] = indices[seed_queue[j]];
00275 
00276         std::sort (r.indices.begin (), r.indices.end ());
00277         r.indices.erase (std::unique (r.indices.begin (), r.indices.end ()), r.indices.end ());
00278 
00279         r.header = cloud.header;
00280         clusters.push_back (r);
00281       }
00282     }
00283   }
00284 
00288 
00292   template <typename PointT>
00293   class EuclideanClusterExtraction: public PCLBase<PointT>
00294   {
00295     typedef PCLBase<PointT> BasePCLBase;
00296 
00297     public:
00298       typedef pcl::PointCloud<PointT> PointCloud;
00299       typedef typename PointCloud::Ptr PointCloudPtr;
00300       typedef typename PointCloud::ConstPtr PointCloudConstPtr;
00301 
00302       typedef typename pcl::search::Search<PointT> KdTree;
00303       typedef typename pcl::search::Search<PointT>::Ptr KdTreePtr;
00304 
00305       typedef PointIndices::Ptr PointIndicesPtr;
00306       typedef PointIndices::ConstPtr PointIndicesConstPtr;
00307 
00309 
00310       EuclideanClusterExtraction () : tree_ (), min_pts_per_cluster_ (1), 
00311                                       max_pts_per_cluster_ (std::numeric_limits<int>::max ())
00312       {};
00313 
00317       inline void setSearchMethod (const KdTreePtr &tree) { tree_ = tree; }
00318 
00322       inline KdTreePtr getSearchMethod () { return (tree_); }
00323 
00327       inline void setClusterTolerance (double tolerance) { cluster_tolerance_ = tolerance; }
00328 
00330       inline double getClusterTolerance () { return (cluster_tolerance_); }
00331 
00335       inline void setMinClusterSize (int min_cluster_size) { min_pts_per_cluster_ = min_cluster_size; }
00336 
00338       inline int getMinClusterSize () { return (min_pts_per_cluster_); }
00339 
00343       inline void setMaxClusterSize (int max_cluster_size) { max_pts_per_cluster_ = max_cluster_size; }
00344 
00346       inline int getMaxClusterSize () { return (max_pts_per_cluster_); }
00347 
00351       void extract (std::vector<PointIndices> &clusters);
00352 
00353     protected:
00354       // Members derived from the base class
00355       using BasePCLBase::input_;
00356       using BasePCLBase::indices_;
00357       using BasePCLBase::initCompute;
00358       using BasePCLBase::deinitCompute;
00359 
00361       KdTreePtr tree_;
00362 
00364       double cluster_tolerance_;
00365 
00367       int min_pts_per_cluster_;
00368 
00370       int max_pts_per_cluster_;
00371 
00373       virtual std::string getClassName () const { return ("EuclideanClusterExtraction"); }
00374 
00375   };
00376 
00380   inline bool 
00381     comparePointClusters (const pcl::PointIndices &a, const pcl::PointIndices &b)
00382   {
00383     return (a.indices.size () < b.indices.size ());
00384   }
00385 }
00386 
00387 #endif  //#ifndef PCL_EXTRACT_CLUSTERS_H_
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines