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 #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
00056
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
00067 jsk_recognition_msgs::ClusterPointIndices result;
00068 result.header = input->header;
00069 result_pub_.publish(result);
00070
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
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
00109
00110
00111
00112
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);
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
00129 result.cluster_indices[i].header
00130 = pcl_conversions::fromPCL(cluster_indices[i].header);
00131 #else
00132
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
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
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
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
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
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
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
00333
00334 std::vector<int> dummy;
00335 return dummy;
00336 }
00337 pivot_table[minimum_previous_index] = minimum_next_index;
00338
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
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