euclidean_cluster_extraction_nodelet.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2013, Ryohei Ueda and JSK Lab
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/o2r other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the JSK Lab nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00035 #include <pluginlib/class_list_macros.h>
00036 #include "jsk_pcl_ros/euclidean_cluster_extraction_nodelet.h" 
00037 #include <jsk_recognition_utils/pcl_util.h>
00038 
00039 using namespace std;
00040 using namespace pcl;
00041 
00042 namespace jsk_pcl_ros
00043 {
00044 
00045   void EuclideanClustering::extract(
00046     const sensor_msgs::PointCloud2ConstPtr &input)
00047   {
00048     boost::mutex::scoped_lock lock(mutex_);
00049     vital_checker_->poke();
00050     pcl::PointCloud<pcl::PointXYZ>::Ptr cloud
00051       (new pcl::PointCloud<pcl::PointXYZ>);
00052     pcl::fromROSMsg(*input, *cloud);
00053     
00054     std::vector<pcl::PointIndices> cluster_indices;
00055     // list up indices which are not NaN to use
00056     // organized pointcloud
00057     pcl::PointIndices::Ptr nonnan_indices (new pcl::PointIndices);
00058     for (size_t i = 0; i < cloud->points.size(); i++) {
00059       pcl::PointXYZ p = cloud->points[i];
00060       if (!std::isnan(p.x) && !std::isnan(p.y) && !std::isnan(p.z)) {
00061         nonnan_indices->indices.push_back(i);
00062       }
00063     }
00064 
00065     if (nonnan_indices->indices.size() == 0) {
00066       // if input points is 0, publish empty data as result
00067       jsk_recognition_msgs::ClusterPointIndices result;
00068       result.header = input->header;
00069       result_pub_.publish(result);
00070       // do nothing and return it
00071       jsk_recognition_msgs::Int32Stamped::Ptr cluster_num_msg (new jsk_recognition_msgs::Int32Stamped);
00072       cluster_num_msg->header = input->header;
00073       cluster_num_msg->data = 0;
00074       cluster_num_pub_.publish(cluster_num_msg);
00075       return;
00076     }
00077     
00078     EuclideanClusterExtraction<pcl::PointXYZ> impl;
00079     {
00080       jsk_topic_tools::ScopedTimer timer = kdtree_acc_.scopedTimer();
00081 #if ( PCL_MAJOR_VERSION >= 1 && PCL_MINOR_VERSION >= 5 )
00082       pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
00083       tree = boost::make_shared< pcl::search::KdTree<pcl::PointXYZ> > ();
00084 #else
00085       pcl::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::KdTreeFLANN<pcl::PointXYZ>);
00086       tree = boost::make_shared<pcl::KdTreeFLANN<pcl::PointXYZ> > ();
00087 #endif
00088       tree->setInputCloud (cloud);
00089       impl.setClusterTolerance (tolerance);
00090       impl.setMinClusterSize (minsize_);
00091       impl.setMaxClusterSize (maxsize_);
00092       impl.setSearchMethod (tree);
00093       impl.setIndices(nonnan_indices);
00094       impl.setInputCloud (cloud);
00095     }
00096     
00097     {
00098       jsk_topic_tools::ScopedTimer timer = segmentation_acc_.scopedTimer();
00099       impl.extract (cluster_indices);
00100     }
00101     
00102     // Publish result indices
00103     jsk_recognition_msgs::ClusterPointIndices result;
00104     result.cluster_indices.resize(cluster_indices.size());
00105     cluster_counter_.add(cluster_indices.size());
00106     result.header = input->header;
00107     if (cogs_.size() != 0 && cogs_.size() == cluster_indices.size()) {
00108       // tracking the labels
00109       //ROS_INFO("computing distance matrix");
00110       // compute distance matrix
00111       // D[i][j] --> distance between the i-th previous cluster
00112       //             and the current j-th cluster
00113       Vector4fVector new_cogs;
00114       computeCentroidsOfClusters(new_cogs, cloud, cluster_indices);
00115       double D[cogs_.size() * new_cogs.size()];
00116       computeDistanceMatrix(D, cogs_, new_cogs);
00117       std::vector<int> pivot_table = buildLabelTrackingPivotTable(D, cogs_, new_cogs, label_tracking_tolerance);
00118       if (pivot_table.size() != 0) {
00119         cluster_indices = pivotClusterIndices(pivot_table, cluster_indices);
00120       }
00121     }
00122     Vector4fVector tmp_cogs;
00123     computeCentroidsOfClusters(tmp_cogs, cloud, cluster_indices); // NB: not efficient
00124     cogs_ = tmp_cogs;
00125       
00126     for (size_t i = 0; i < cluster_indices.size(); i++) {
00127 #if ROS_VERSION_MINIMUM(1, 10, 0)
00128       // hydro and later
00129       result.cluster_indices[i].header
00130         = pcl_conversions::fromPCL(cluster_indices[i].header);
00131 #else
00132       // groovy
00133       result.cluster_indices[i].header = cluster_indices[i].header;
00134 #endif
00135       result.cluster_indices[i].indices = cluster_indices[i].indices;
00136     }
00137 
00138     result_pub_.publish(result);
00139     
00140     jsk_recognition_msgs::Int32Stamped::Ptr cluster_num_msg (new jsk_recognition_msgs::Int32Stamped);
00141     cluster_num_msg->header = input->header;
00142     cluster_num_msg->data = cluster_indices.size();
00143     cluster_num_pub_.publish(cluster_num_msg);
00144 
00145     diagnostic_updater_->update();
00146   }
00147 
00148 
00149   bool EuclideanClustering::serviceCallback(
00150     jsk_recognition_msgs::EuclideanSegment::Request &req,
00151     jsk_recognition_msgs::EuclideanSegment::Response &res)
00152   {
00153     pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
00154     pcl::fromROSMsg(req.input, *cloud);
00155 
00156 #if ( PCL_MAJOR_VERSION >= 1 && PCL_MINOR_VERSION >= 5 )
00157     pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
00158     tree = boost::make_shared< pcl::search::KdTree<pcl::PointXYZ> > ();
00159 #else
00160     pcl::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::KdTreeFLANN<pcl::PointXYZ>);
00161     tree = boost::make_shared<pcl::KdTreeFLANN<pcl::PointXYZ> > ();
00162 #endif
00163 
00164     vector<pcl::PointIndices> cluster_indices;
00165     EuclideanClusterExtraction<pcl::PointXYZ> impl;
00166     double tor;
00167     if ( req.tolerance < 0) {
00168       tor = tolerance;
00169     } else {
00170       tor = req.tolerance;
00171     }
00172     impl.setClusterTolerance (tor);
00173     impl.setMinClusterSize (minsize_);
00174     impl.setMaxClusterSize (maxsize_);
00175     impl.setSearchMethod (tree);
00176     impl.setInputCloud (cloud);
00177     impl.extract (cluster_indices);
00178 
00179     res.output.resize( cluster_indices.size() );
00180 #if ( PCL_MAJOR_VERSION >= 1 && PCL_MINOR_VERSION >= 7 )
00181     pcl::PCLPointCloud2::Ptr pcl_cloud(new pcl::PCLPointCloud2);
00182     pcl_conversions::toPCL(req.input, *pcl_cloud);
00183     pcl::ExtractIndices<pcl::PCLPointCloud2> ex;
00184     ex.setInputCloud(pcl_cloud);
00185 #else
00186     pcl::ExtractIndices<sensor_msgs::PointCloud2> ex;
00187     ex.setInputCloud ( boost::make_shared< sensor_msgs::PointCloud2 > (req.input) );
00188 #endif
00189     for ( size_t i = 0; i < cluster_indices.size(); i++ ) {
00190       ex.setIndices ( boost::make_shared< pcl::PointIndices > (cluster_indices[i]) );
00191       ex.setNegative ( false );
00192 #if ( PCL_MAJOR_VERSION >= 1 && PCL_MINOR_VERSION >= 7 )
00193       pcl::PCLPointCloud2 output_cloud;
00194       ex.filter ( output_cloud );
00195       pcl_conversions::fromPCL(output_cloud, res.output[i]);
00196 #else
00197       ex.filter ( res.output[i] );
00198 #endif
00199     }
00200     return true;
00201   }
00202 
00203   void EuclideanClustering::onInit()
00204   {
00205     ConnectionBasedNodelet::onInit();
00206     
00208     // diagnostics
00210     diagnostic_updater_.reset(
00211       new jsk_recognition_utils::TimeredDiagnosticUpdater(*pnh_, ros::Duration(1.0)));
00212     diagnostic_updater_->setHardwareID(getName());
00213     diagnostic_updater_->add(
00214       getName() + "::EuclideanClustering",
00215       boost::bind(
00216         &EuclideanClustering::updateDiagnostic,
00217         this,
00218         _1));
00219     double vital_rate;
00220     pnh_->param("vital_rate", vital_rate, 1.0);
00221     vital_checker_.reset(
00222       new jsk_topic_tools::VitalChecker(1 / vital_rate));
00223     diagnostic_updater_->start();
00224     
00226     // dynamic reconfigure
00228     srv_ = boost::make_shared <dynamic_reconfigure::Server<Config> > (*pnh_);
00229     dynamic_reconfigure::Server<Config>::CallbackType f =
00230       boost::bind (&EuclideanClustering::configCallback, this, _1, _2);
00231     srv_->setCallback (f);
00232     
00234     // Publisher
00236     result_pub_ = advertise<jsk_recognition_msgs::ClusterPointIndices> (*pnh_, "output", 1);
00237     cluster_num_pub_ = advertise<jsk_recognition_msgs::Int32Stamped> (*pnh_, "cluster_num", 1);
00238     service_ = pnh_->advertiseService(pnh_->resolveName("euclidean_clustering"),
00239                                       &EuclideanClustering::serviceCallback, this);
00240 
00241     onInitPostProcess();
00242   }
00243 
00244   void EuclideanClustering::subscribe()
00245   {
00247     // Subscription
00249     sub_input_ = pnh_->subscribe("input", 1, &EuclideanClustering::extract, this);
00250   }
00251 
00252   void EuclideanClustering::unsubscribe()
00253   {
00254     sub_input_.shutdown();
00255   }
00256   
00257   void EuclideanClustering::updateDiagnostic(
00258     diagnostic_updater::DiagnosticStatusWrapper &stat)
00259   {
00260     if (vital_checker_->isAlive()) {
00261       stat.summary(
00262       diagnostic_msgs::DiagnosticStatus::OK,
00263       "EuclideanSegmentation running");
00264 
00265       jsk_recognition_utils::addDiagnosticInformation(
00266         "Kdtree Construction", kdtree_acc_, stat);
00267       jsk_recognition_utils::addDiagnosticInformation(
00268         "Euclidean Segmentation", segmentation_acc_, stat);
00269       stat.add("Cluster Num (Avg.)", cluster_counter_.mean());
00270       stat.add("Max Size of the cluster", maxsize_);
00271       stat.add("Min Size of the cluster", minsize_);
00272       stat.add("Cluster tolerance", tolerance);
00273       stat.add("Tracking tolerance", label_tracking_tolerance);
00274     }
00275     else {
00276       jsk_recognition_utils::addDiagnosticErrorSummary(
00277         "EuclideanClustering", vital_checker_, stat);
00278     }
00279   }
00280   
00281   void EuclideanClustering::configCallback (Config &config, uint32_t level)
00282   {
00283     boost::mutex::scoped_lock lock(mutex_);
00284     tolerance = config.tolerance;
00285     label_tracking_tolerance = config.label_tracking_tolerance;
00286     maxsize_ = config.max_size;
00287     minsize_ = config.min_size;
00288   }
00289 
00290   std::vector<pcl::PointIndices> EuclideanClustering::pivotClusterIndices(
00291     std::vector<int>& pivot_table,
00292     std::vector<pcl::PointIndices>& cluster_indices)
00293   {
00294     std::vector<pcl::PointIndices> new_cluster_indices;
00295     new_cluster_indices.resize(pivot_table.size());
00296     for (size_t i = 0; i < pivot_table.size(); i++) {
00297       new_cluster_indices[i] = cluster_indices[pivot_table[i]];
00298     }
00299     return new_cluster_indices;
00300   }
00301   std::vector<int> EuclideanClustering::buildLabelTrackingPivotTable(
00302     double* D, Vector4fVector cogs, Vector4fVector new_cogs,
00303     double label_tracking_tolerance)
00304   {
00305     std::vector<int> pivot_table;
00306     // initialize pivot table
00307     pivot_table.resize(cogs.size());
00308     for (size_t i = 0; i < pivot_table.size(); i++)
00309       pivot_table[i] = i;
00310     for (size_t pivot_counter = 0; pivot_counter < pivot_table.size();
00311          pivot_counter++)
00312     {
00313       double minimum_distance = DBL_MAX;
00314       size_t minimum_previous_index = 0;
00315       size_t minimum_next_index = 0;
00316       for (size_t i = 0; i < cogs.size(); i++)
00317       {
00318         for (size_t j = 0; j < new_cogs.size(); j++)
00319         {
00320           double distance = D[i * cogs.size() + j];
00321           //ROS_INFO("distance %lux%lu: %f", i, j, distance);
00322           if (distance < minimum_distance)
00323           {
00324             minimum_distance = distance;
00325             minimum_previous_index = i;
00326             minimum_next_index = j;
00327           }
00328         }
00329       }
00330       if (minimum_distance > label_tracking_tolerance)
00331       {
00332         // ROS_WARN("minimum tracking distance exceeds tolerance: %f > %f",
00333         //          minimum_distance, label_tracking_tolerance);
00334         std::vector<int> dummy;
00335         return dummy;
00336       }
00337       pivot_table[minimum_previous_index] = minimum_next_index;
00338       // fill the D matrix with DBL_MAX
00339       for (size_t j = 0; j < new_cogs.size(); j++)
00340       {
00341         D[minimum_previous_index * cogs.size() + j] = DBL_MAX;
00342       }
00343     }
00344     return pivot_table;
00345   }
00346 
00347   void EuclideanClustering::computeDistanceMatrix(
00348     double* D,
00349     Vector4fVector& old_cogs,
00350     Vector4fVector& new_cogs)
00351   {
00352     for (size_t i = 0; i < old_cogs.size(); i++) {
00353       Eigen::Vector4f previous_cog = old_cogs[i];
00354       for (size_t j = 0; j < new_cogs.size(); j++) {
00355         Eigen::Vector4f next_cog = new_cogs[j];
00356         double distance = (next_cog - previous_cog).norm();
00357         D[i * old_cogs.size() + j] = distance;
00358       }
00359     }
00360   }
00361 
00362   void EuclideanClustering::computeCentroidsOfClusters(
00363     Vector4fVector& ret,
00364     pcl::PointCloud<pcl::PointXYZ>::Ptr cloud,
00365     std::vector<pcl::PointIndices> cluster_indices)
00366   {
00367     pcl::ExtractIndices<pcl::PointXYZ> extract;
00368     extract.setInputCloud(cloud);
00369     ret.resize(cluster_indices.size());
00370     for (size_t i = 0; i < cluster_indices.size(); i++)
00371     {
00372       // build pointcloud
00373       pcl::PointCloud<pcl::PointXYZ>::Ptr segmented_cloud (new pcl::PointCloud<pcl::PointXYZ>);
00374       pcl::PointIndices::Ptr segmented_indices (new pcl::PointIndices);
00375       for (size_t j = 0; j < cluster_indices[i].indices.size(); j++)
00376       {
00377         segmented_indices->indices.push_back(cluster_indices[i].indices[j]);
00378       }
00379       extract.setIndices(segmented_indices);
00380       extract.filter(*segmented_cloud);
00381       Eigen::Vector4f center;
00382       pcl::compute3DCentroid(*segmented_cloud, center);
00383       ret[i] = center;
00384     }
00385   }
00386   
00387 }
00388 
00389 #include <pluginlib/class_list_macros.h>
00390 PLUGINLIB_EXPORT_CLASS (jsk_pcl_ros::EuclideanClustering, nodelet::Nodelet);
00391 


jsk_pcl_ros
Author(s): Yohei Kakiuchi
autogenerated on Sun Oct 8 2017 02:43:49