Segmenter.cpp
Go to the documentation of this file.
00001 
00013 // RAIL Segmentation
00014 #include "rail_segmentation/Segmenter.h"
00015 
00016 // ROS
00017 #include <pcl_ros/transforms.h>
00018 #include <ros/package.h>
00019 #include <sensor_msgs/point_cloud_conversion.h>
00020 #include <sensor_msgs/image_encodings.h>
00021 #include <tf2/LinearMath/Matrix3x3.h>
00022 
00023 // PCL
00024 #include <pcl/common/common.h>
00025 #include <pcl/filters/extract_indices.h>
00026 #include <pcl/filters/passthrough.h>
00027 #include <pcl/filters/project_inliers.h>
00028 #include <pcl/filters/voxel_grid.h>
00029 #include <pcl/ModelCoefficients.h>
00030 #include <pcl/segmentation/sac_segmentation.h>
00031 #include <pcl/segmentation/extract_clusters.h>
00032 
00033 // YAML
00034 #include <yaml-cpp/yaml.h>
00035 
00036 // C++ Standard Library
00037 #include <fstream>
00038 
00039 using namespace std;
00040 using namespace rail::segmentation;
00041 
00042 Segmenter::Segmenter() : private_node_("~"), tf2_(tf_buffer_)
00043 {
00044   // flag for the first point cloud coming in
00045   first_pc_in_ = false;
00046 
00047   // set defaults
00048   debug_ = DEFAULT_DEBUG;
00049   string point_cloud_topic("/camera/depth_registered/points");
00050   string zones_file(ros::package::getPath("rail_segmentation") + "/config/zones.yaml");
00051   min_cluster_size_ = DEFAULT_MIN_CLUSTER_SIZE;
00052   max_cluster_size_ = DEFAULT_MAX_CLUSTER_SIZE;
00053 
00054   // grab any parameters we need
00055   private_node_.getParam("debug", debug_);
00056   private_node_.getParam("point_cloud_topic", point_cloud_topic);
00057   private_node_.getParam("zones_config", zones_file);
00058   private_node_.getParam("min_cluster_size", min_cluster_size_);
00059   private_node_.getParam("max_cluster_size", max_cluster_size_);
00060 
00061   // setup publishers/subscribers we need
00062   segment_srv_ = private_node_.advertiseService("segment", &Segmenter::segmentCallback, this);
00063   clear_srv_ = private_node_.advertiseService("clear", &Segmenter::clearCallback, this);
00064   remove_object_srv_ = private_node_.advertiseService("remove_object", &Segmenter::removeObjectCallback, this);
00065   segmented_objects_pub_ = private_node_.advertise<rail_manipulation_msgs::SegmentedObjectList>(
00066       "segmented_objects", 1, true
00067   );
00068   markers_pub_ = private_node_.advertise<visualization_msgs::MarkerArray>("markers", 1, true);
00069   point_cloud_sub_ = node_.subscribe(point_cloud_topic, 1, &Segmenter::pointCloudCallback, this);
00070   // setup a debug publisher if we need it
00071   if (debug_)
00072   {
00073     debug_pc_pub_ = private_node_.advertise<pcl::PointCloud<pcl::PointXYZRGB> >("debug_pc", 1, true);
00074     debug_img_pub_ = private_node_.advertise<sensor_msgs::Image>("debug_img", 1, true);
00075   }
00076 
00077   // check the YAML version
00078 #ifdef YAMLCPP_GT_0_5_0
00079   // parse the segmentation zones
00080   YAML::Node zones_config = YAML::LoadFile(zones_file);
00081   for (size_t i = 0; i < zones_config.size(); i++)
00082   {
00083     YAML::Node cur = zones_config[i];
00084     // create a zone with the frame ID information
00085     SegmentationZone zone(cur["name"].as<string>(), cur["parent_frame_id"].as<string>(),
00086                           cur["child_frame_id"].as<string>(), cur["bounding_frame_id"].as<string>(),
00087                           cur["segmentation_frame_id"].as<string>());
00088 
00089     // check for the remove surface flag
00090     if (cur["remove_surface"].IsDefined())
00091     {
00092       zone.setRemoveSurface(cur["remove_surface"].as<bool>());
00093     }
00094 
00095     // check for any set limits
00096     if (cur["roll_min"].IsDefined())
00097     {
00098       zone.setRollMin(cur["roll_min"].as<double>());
00099     }
00100     if (cur["roll_max"].IsDefined())
00101     {
00102       zone.setRollMax(cur["roll_max"].as<double>());
00103     }
00104     if (cur["pitch_min"].IsDefined())
00105     {
00106       zone.setPitchMin(cur["pitch_min"].as<double>());
00107     }
00108     if (cur["pitch_max"].IsDefined())
00109     {
00110       zone.setPitchMax(cur["pitch_max"].as<double>());
00111     }
00112     if (cur["yaw_min"].IsDefined())
00113     {
00114       zone.setYawMin(cur["yaw_min"].as<double>());
00115     }
00116     if (cur["yaw_max"].IsDefined())
00117     {
00118       zone.setYawMax(cur["yaw_max"].as<double>());
00119     }
00120     if (cur["x_min"].IsDefined())
00121     {
00122       zone.setXMin(cur["x_min"].as<double>());
00123     }
00124     if (cur["x_max"].IsDefined())
00125     {
00126       zone.setXMax(cur["x_max"].as<double>());
00127     }
00128     if (cur["y_min"].IsDefined())
00129     {
00130       zone.setYMin(cur["y_min"].as<double>());
00131     }
00132     if (cur["y_max"].IsDefined())
00133     {
00134       zone.setYMax(cur["y_max"].as<double>());
00135     }
00136     if (cur["z_min"].IsDefined())
00137     {
00138       zone.setZMin(cur["z_min"].as<double>());
00139     }
00140     if (cur["z_max"].IsDefined())
00141     {
00142       zone.setZMax(cur["z_max"].as<double>());
00143     }
00144 
00145     zones_.push_back(zone);
00146   }
00147 #else
00148   // parse the segmentation zones
00149   ifstream fin(zones_file.c_str());
00150   YAML::Parser zones_parser(fin);
00151   YAML::Node zones_config;
00152   zones_parser.GetNextDocument(zones_config);
00153   for (size_t i = 0; i < zones_config.size(); i++)
00154   {
00155     // parse the required information
00156     string name, parent_frame_id, child_frame_id, bounding_frame_id, segmentation_frame_id;
00157     zones_config[i]["name"] >> name;
00158     zones_config[i]["parent_frame_id"] >> parent_frame_id;
00159     zones_config[i]["child_frame_id"] >> child_frame_id;
00160     zones_config[i]["bounding_frame_id"] >> bounding_frame_id;
00161     zones_config[i]["segmentation_frame_id"] >> segmentation_frame_id;
00162 
00163     // create a zone with the frame ID information
00164     SegmentationZone zone(name, parent_frame_id, child_frame_id, bounding_frame_id, segmentation_frame_id);
00165 
00166     // check for the remove surface flag
00167     if (zones_config[i].FindValue("remove_surface") != NULL)
00168     {
00169       bool remove_surface;
00170       zones_config[i]["remove_surface"] >> remove_surface;
00171       zone.setRemoveSurface(remove_surface);
00172     }
00173 
00174     // check for any set limits
00175     if (zones_config[i].FindValue("roll_min") != NULL)
00176     {
00177       double roll_min;
00178       zones_config[i]["roll_min"] >> roll_min;
00179       zone.setRollMin(roll_min);
00180     }
00181     if (zones_config[i].FindValue("roll_max") != NULL)
00182     {
00183       double roll_max;
00184       zones_config[i]["roll_max"] >> roll_max;
00185       zone.setRollMax(roll_max);
00186     }
00187     if (zones_config[i].FindValue("pitch_min") != NULL)
00188     {
00189       double pitch_min;
00190       zones_config[i]["pitch_min"] >> pitch_min;
00191       zone.setPitchMin(pitch_min);
00192     }
00193     if (zones_config[i].FindValue("pitch_max") != NULL)
00194     {
00195       double pitch_max;
00196       zones_config[i]["pitch_max"] >> pitch_max;
00197       zone.setPitchMax(pitch_max);
00198     }
00199     if (zones_config[i].FindValue("yaw_min") != NULL)
00200     {
00201       double yaw_min;
00202       zones_config[i]["yaw_min"] >> yaw_min;
00203       zone.setYawMin(yaw_min);
00204     }
00205     if (zones_config[i].FindValue("yaw_max") != NULL)
00206     {
00207       double yaw_max;
00208       zones_config[i]["yaw_max"] >> yaw_max;
00209       zone.setYawMax(yaw_max);
00210     }
00211     if (zones_config[i].FindValue("x_min") != NULL)
00212     {
00213       double x_min;
00214       zones_config[i]["x_min"] >> x_min;
00215       zone.setXMin(x_min);
00216     }
00217     if (zones_config[i].FindValue("x_max") != NULL)
00218     {
00219       double x_max;
00220       zones_config[i]["x_max"] >> x_max;
00221       zone.setXMax(x_max);
00222     }
00223     if (zones_config[i].FindValue("y_min") != NULL)
00224     {
00225       double y_min;
00226       zones_config[i]["y_min"] >> y_min;
00227       zone.setYMin(y_min);
00228     }
00229     if (zones_config[i].FindValue("y_max") != NULL)
00230     {
00231       double y_max;
00232       zones_config[i]["y_max"] >> y_max;
00233       zone.setYMax(y_max);
00234     }
00235     if (zones_config[i].FindValue("z_min") != NULL)
00236     {
00237       double z_min;
00238       zones_config[i]["z_min"] >> z_min;
00239       zone.setZMin(z_min);
00240     }
00241     if (zones_config[i].FindValue("z_max") != NULL)
00242     {
00243       double z_max;
00244       zones_config[i]["z_max"] >> z_max;
00245       zone.setZMax(z_max);
00246     }
00247 
00248     zones_.push_back(zone);
00249   }
00250 #endif
00251 
00252   // check how many zones we have
00253   if (zones_.size() > 0)
00254   {
00255     ROS_INFO("%d segmenation zone(s) parsed.", (int) zones_.size());
00256     ROS_INFO("Segmenter Successfully Initialized");
00257     okay_ = true;
00258   } else
00259   {
00260     ROS_ERROR("No valid segmenation zones defined. Check %s.", zones_file.c_str());
00261     okay_ = false;
00262   }
00263 }
00264 
00265 bool Segmenter::okay() const
00266 {
00267   return okay_;
00268 }
00269 
00270 void Segmenter::pointCloudCallback(const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &pc)
00271 {
00272   // lock for the point cloud
00273   boost::mutex::scoped_lock lock(pc_mutex_);
00274   // simply store the latest point cloud
00275   first_pc_in_ = true;
00276   pc_ = pc;
00277 }
00278 
00279 const SegmentationZone &Segmenter::getCurrentZone() const
00280 {
00281   // check each zone
00282   for (size_t i = 0; i < zones_.size(); i++)
00283   {
00284     // get the current TF information
00285     geometry_msgs::TransformStamped tf = tf_buffer_.lookupTransform(zones_[i].getParentFrameID(),
00286                                                                     zones_[i].getChildFrameID(), ros::Time(0));
00287 
00288     // convert to a Matrix3x3 to get RPY
00289     tf2::Matrix3x3 mat(tf2::Quaternion(tf.transform.rotation.x, tf.transform.rotation.y, tf.transform.rotation.z,
00290                                        tf.transform.rotation.w));
00291     double roll, pitch, yaw;
00292     mat.getRPY(roll, pitch, yaw);
00293 
00294     // check if all the bounds meet
00295     if (roll >= zones_[i].getRollMin() && pitch >= zones_[i].getPitchMin() && yaw >= zones_[i].getYawMin() &&
00296         roll <= zones_[i].getRollMax() && pitch <= zones_[i].getPitchMax() && yaw <= zones_[i].getYawMax())
00297     {
00298       return zones_[i];
00299     }
00300   }
00301 
00302   ROS_WARN("Current state not in a valid segmentation zone. Defaulting to first zone.");
00303   return zones_[0];
00304 }
00305 
00306 bool Segmenter::removeObjectCallback(rail_segmentation::RemoveObject::Request &req,
00307     rail_segmentation::RemoveObject::Response &res)
00308 {
00309   // lock for the messages
00310   boost::mutex::scoped_lock lock(msg_mutex_);
00311   // check the index
00312   if (req.index < object_list_.objects.size() && req.index < markers_.markers.size())
00313   {
00314     // remove
00315     object_list_.objects.erase(object_list_.objects.begin() + req.index);
00316     // set header information
00317     object_list_.header.seq++;
00318     object_list_.header.stamp = ros::Time::now();
00319     object_list_.cleared = false;
00320     // republish
00321     segmented_objects_pub_.publish(object_list_);
00322     // delete marker
00323     markers_.markers[req.index].action = visualization_msgs::Marker::DELETE;
00324     markers_pub_.publish(markers_);
00325     markers_.markers.erase(markers_.markers.begin() + req.index);
00326     return true;
00327   } else
00328   {
00329     ROS_ERROR("Attempted to remove index %d from list of size %ld.", req.index, object_list_.objects.size());
00330     return false;
00331   }
00332 }
00333 
00334 bool Segmenter::clearCallback(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
00335 {
00336   // lock for the messages
00337   boost::mutex::scoped_lock lock(msg_mutex_);
00338   // empty the list
00339   object_list_.objects.clear();
00340   object_list_.cleared = true;
00341   // set header information
00342   object_list_.header.seq++;
00343   object_list_.header.stamp = ros::Time::now();
00344   // republish
00345   segmented_objects_pub_.publish(object_list_);
00346   // delete markers
00347   for (size_t i = 0; i < markers_.markers.size(); i++)
00348   {
00349     markers_.markers[i].action = visualization_msgs::Marker::DELETE;
00350   }
00351   markers_pub_.publish(markers_);
00352   markers_.markers.clear();
00353   return true;
00354 }
00355 
00356 bool Segmenter::segmentCallback(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
00357 {
00358   // check if we have a point cloud first
00359   {
00360     boost::mutex::scoped_lock lock(pc_mutex_);
00361     if (!first_pc_in_)
00362     {
00363       ROS_WARN("No point cloud received yet. Ignoring segmentation request.");
00364       return false;
00365     }
00366   }
00367 
00368   // clear the objects first
00369   this->clearCallback(req, res);
00370 
00371   // determine the correct segmentation zone
00372   const SegmentationZone &zone = this->getCurrentZone();
00373   ROS_INFO("Segmenting in zone '%s'.", zone.getName().c_str());
00374 
00375   // transform the point cloud to the fixed frame
00376   pcl::PointCloud<pcl::PointXYZRGB>::Ptr transformed_pc(new pcl::PointCloud<pcl::PointXYZRGB>);
00377   // lock on the point cloud
00378   {
00379     boost::mutex::scoped_lock lock(pc_mutex_);
00380     // perform the copy/transform using TF
00381     pcl_ros::transformPointCloud(zone.getBoundingFrameID(), ros::Time(0), *pc_, pc_->header.frame_id,
00382                                  *transformed_pc, tf_);
00383     transformed_pc->header.frame_id = zone.getBoundingFrameID();
00384     transformed_pc->header.seq = pc_->header.seq;
00385     transformed_pc->header.stamp = pc_->header.stamp;
00386   }
00387 
00388   // start with every index
00389   pcl::IndicesPtr filter_indices(new vector<int>);
00390   filter_indices->resize(transformed_pc->points.size());
00391   for (size_t i = 0; i < transformed_pc->points.size(); i++)
00392   {
00393     filter_indices->at(i) = i;
00394   }
00395 
00396   // check if we need to remove a surface
00397   double z_min = zone.getZMin();
00398   if (zone.getRemoveSurface())
00399   {
00400     double z_surface = this->findSurface(transformed_pc, filter_indices, zone.getZMin(), zone.getZMax(),
00401                                          filter_indices);
00402     // check the new bound for Z
00403     z_min = max(zone.getZMin(), z_surface + SURFACE_REMOVAL_PADDING);
00404   }
00405 
00406   // check bounding areas (bound the inverse of what we want since PCL will return the removed indicies)
00407   pcl::ConditionOr<pcl::PointXYZRGB>::Ptr bounds(new pcl::ConditionOr<pcl::PointXYZRGB>);
00408   if (z_min > -numeric_limits<double>::infinity())
00409   {
00410     bounds->addComparison(pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr(
00411         new pcl::FieldComparison<pcl::PointXYZRGB>("z", pcl::ComparisonOps::LE, z_min))
00412     );
00413   }
00414   if (zone.getZMax() < numeric_limits<double>::infinity())
00415   {
00416     bounds->addComparison(pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr(
00417         new pcl::FieldComparison<pcl::PointXYZRGB>("z", pcl::ComparisonOps::GE, zone.getZMax()))
00418     );
00419   }
00420   if (zone.getYMin() > -numeric_limits<double>::infinity())
00421   {
00422     bounds->addComparison(pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr(
00423         new pcl::FieldComparison<pcl::PointXYZRGB>("y", pcl::ComparisonOps::LE, zone.getYMin()))
00424     );
00425   }
00426   if (zone.getYMax() < numeric_limits<double>::infinity())
00427   {
00428     bounds->addComparison(pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr(
00429         new pcl::FieldComparison<pcl::PointXYZRGB>("y", pcl::ComparisonOps::GE, zone.getYMax()))
00430     );
00431   }
00432   if (zone.getXMin() > -numeric_limits<double>::infinity())
00433   {
00434     bounds->addComparison(pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr(
00435         new pcl::FieldComparison<pcl::PointXYZRGB>("x", pcl::ComparisonOps::LE, zone.getXMin()))
00436     );
00437   }
00438   if (zone.getXMax() < numeric_limits<double>::infinity())
00439   {
00440     bounds->addComparison(pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr(
00441         new pcl::FieldComparison<pcl::PointXYZRGB>("x", pcl::ComparisonOps::GE, zone.getXMax()))
00442     );
00443   }
00444 
00445   // remove past the given bounds
00446   this->inverseBound(transformed_pc, filter_indices, bounds, filter_indices);
00447 
00448   // publish the filtered and bounded PC pre-segmentation
00449   if (debug_)
00450   {
00451     pcl::PointCloud<pcl::PointXYZRGB>::Ptr debug_pc(new pcl::PointCloud<pcl::PointXYZRGB>);
00452     this->extract(transformed_pc, filter_indices, debug_pc);
00453     debug_pc_pub_.publish(debug_pc);
00454   }
00455 
00456   // extract clusters
00457   vector<pcl::PointIndices> clusters;
00458   this->extractClusters(transformed_pc, filter_indices, clusters);
00459 
00460   if (clusters.size() > 0)
00461   {
00462     // lock for the messages
00463     boost::mutex::scoped_lock lock(msg_mutex_);
00464     // check each cluster
00465     for (size_t i = 0; i < clusters.size(); i++)
00466     {
00467       // grab the points we need
00468       pcl::PointCloud<pcl::PointXYZRGB>::Ptr cluster(new pcl::PointCloud<pcl::PointXYZRGB>);
00469       for (size_t j = 0; j < clusters[i].indices.size(); j++)
00470       {
00471         cluster->points.push_back(transformed_pc->points[clusters[i].indices[j]]);
00472       }
00473       cluster->width = cluster->points.size();
00474       cluster->height = 1;
00475       cluster->is_dense = true;
00476       cluster->header.frame_id = transformed_pc->header.frame_id;
00477 
00478       // check if we need to transform to a different frame
00479       pcl::PointCloud<pcl::PointXYZRGB>::Ptr transformed_cluster(new pcl::PointCloud<pcl::PointXYZRGB>);
00480       pcl::PCLPointCloud2::Ptr converted(new pcl::PCLPointCloud2);
00481       if (zone.getBoundingFrameID() != zone.getSegmentationFrameID())
00482       {
00483         // perform the copy/transform using TF
00484         pcl_ros::transformPointCloud(zone.getSegmentationFrameID(), ros::Time(0), *cluster, cluster->header.frame_id,
00485                                      *transformed_cluster, tf_);
00486         transformed_cluster->header.frame_id = zone.getSegmentationFrameID();
00487         transformed_cluster->header.seq = cluster->header.seq;
00488         transformed_cluster->header.stamp = cluster->header.stamp;
00489         pcl::toPCLPointCloud2(*transformed_cluster, *converted);
00490       } else
00491       {
00492         pcl::toPCLPointCloud2(*cluster, *converted);
00493       }
00494 
00495       // convert to a SegmentedObject message
00496       rail_manipulation_msgs::SegmentedObject segmented_object;
00497       segmented_object.recognized = false;
00498 
00499       // set the RGB image
00500       segmented_object.image = this->createImage(transformed_pc, clusters[i]);
00501 
00502       // check if we want to publish the image
00503       if (debug_)
00504       {
00505         debug_img_pub_.publish(segmented_object.image);
00506       }
00507 
00508       // set the point cloud
00509       pcl_conversions::fromPCL(*converted, segmented_object.point_cloud);
00510       segmented_object.point_cloud.header.stamp = ros::Time::now();
00511       // create a marker and set the extra fields
00512       segmented_object.marker = this->createMarker(converted);
00513       segmented_object.marker.id = i;
00514 
00515       // set the centroid
00516       Eigen::Vector4f centroid;
00517       if (zone.getBoundingFrameID() != zone.getSegmentationFrameID())
00518       {
00519         pcl::compute3DCentroid(*transformed_cluster, centroid);
00520       } else
00521       {
00522         pcl::compute3DCentroid(*cluster, centroid);
00523       }
00524       segmented_object.centroid.x = centroid[0];
00525       segmented_object.centroid.y = centroid[1];
00526       segmented_object.centroid.z = centroid[2];
00527 
00528       // calculate the bounding box
00529       Eigen::Vector4f min_pt, max_pt;
00530       pcl::getMinMax3D(*cluster, min_pt, max_pt);
00531       segmented_object.width = max_pt[0] - min_pt[0];
00532       segmented_object.depth = max_pt[1] - min_pt[1];
00533       segmented_object.height = max_pt[2] - min_pt[2];
00534 
00535       // calculate the center
00536       segmented_object.center.x = (max_pt[0] + min_pt[0]) / 2.0;
00537       segmented_object.center.y = (max_pt[1] + min_pt[1]) / 2.0;
00538       segmented_object.center.z = (max_pt[2] + min_pt[2]) / 2.0;
00539 
00540       // calculate the orientation
00541       pcl::PointCloud<pcl::PointXYZRGB>::Ptr projected_cluster(new pcl::PointCloud<pcl::PointXYZRGB>);
00542       // project point cloud onto the xy plane
00543       pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients());
00544       coefficients->values.resize(4);
00545       coefficients->values[0] = 0;
00546       coefficients->values[1] = 0;
00547       coefficients->values[2] = 1.0;
00548       coefficients->values[3] = 0;
00549       pcl::ProjectInliers<pcl::PointXYZRGB> proj;
00550       proj.setModelType(pcl::SACMODEL_PLANE);
00551       if (zone.getBoundingFrameID() != zone.getSegmentationFrameID())
00552       {
00553         proj.setInputCloud(transformed_cluster);
00554       } else
00555       {
00556         proj.setInputCloud(cluster);
00557       }
00558       proj.setModelCoefficients(coefficients);
00559       proj.filter(*projected_cluster);
00560 
00561       //calculate the Eigen vectors of the projected point cloud's covariance matrix, used to determine orientation
00562       Eigen::Vector4f projected_centroid;
00563       Eigen::Matrix3f covariance_matrix;
00564       pcl::compute3DCentroid(*projected_cluster, projected_centroid);
00565       pcl::computeCovarianceMatrixNormalized(*projected_cluster, projected_centroid, covariance_matrix);
00566       Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> eigen_solver(covariance_matrix, Eigen::ComputeEigenvectors);
00567       Eigen::Matrix3f eigen_vectors = eigen_solver.eigenvectors();
00568       eigen_vectors.col(2) = eigen_vectors.col(0).cross(eigen_vectors.col(1));
00569       //calculate rotation from eigenvectors
00570       const Eigen::Quaternionf qfinal(eigen_vectors);
00571 
00572       //convert orientation to a single angle on the 2D plane defined by the segmentation coordinate frame
00573       tf::Quaternion tf_quat;
00574       tf_quat.setValue(qfinal.x(), qfinal.y(), qfinal.z(), qfinal.w());
00575       double r, p, y;
00576       tf::Matrix3x3 m(tf_quat);
00577       m.getRPY(r, p, y);
00578       double angle = r + y;
00579       while (angle < -M_PI)
00580       {
00581         angle += 2 * M_PI;
00582       }
00583       while (angle > M_PI)
00584       {
00585         angle -= 2 * M_PI;
00586       }
00587       segmented_object.orientation = tf::createQuaternionMsgFromYaw(angle);
00588 
00589       // add to the final list
00590       object_list_.objects.push_back(segmented_object);
00591       // add to the markers
00592       markers_.markers.push_back(segmented_object.marker);
00593     }
00594 
00595     // publish the new list
00596     object_list_.header.seq++;
00597     object_list_.header.stamp = ros::Time::now();
00598     object_list_.header.frame_id = zone.getSegmentationFrameID();
00599     object_list_.cleared = false;
00600     segmented_objects_pub_.publish(object_list_);
00601 
00602     // publish the new marker array
00603     markers_pub_.publish(markers_);
00604   } else
00605   {
00606     ROS_WARN("No segmented objects found.");
00607   }
00608 
00609   return true;
00610 }
00611 
00612 double Segmenter::findSurface(const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &in,
00613     const pcl::IndicesConstPtr &indices_in, const double z_min, const double z_max,
00614     const pcl::IndicesPtr &indices_out) const
00615 {
00616   // use a plane (SAC) segmenter
00617   pcl::SACSegmentation<pcl::PointXYZRGB> plane_seg;
00618   // set the segmenation parameters
00619   plane_seg.setOptimizeCoefficients(true);
00620   plane_seg.setModelType(pcl::SACMODEL_PERPENDICULAR_PLANE);
00621   plane_seg.setAxis(Eigen::Vector3f(0, 0, 1));
00622   plane_seg.setEpsAngle(SAC_EPS_ANGLE);
00623   plane_seg.setMethodType(pcl::SAC_RANSAC);
00624   plane_seg.setMaxIterations(SAC_MAX_ITERATIONS);
00625   plane_seg.setDistanceThreshold(SAC_DISTANCE_THRESHOLD);
00626 
00627   // create a copy to work with
00628   pcl::PointCloud<pcl::PointXYZRGB>::Ptr pc_copy(new pcl::PointCloud<pcl::PointXYZRGB>(*in));
00629   plane_seg.setInputCloud(pc_copy);
00630   plane_seg.setIndices(indices_in);
00631 
00632   // Check point height -- if the plane is too low or high, extract another
00633   while (true)
00634   {
00635     // points included in the plane (surface)
00636     pcl::PointIndices::Ptr inliers_ptr(new pcl::PointIndices);
00637 
00638     // segment the the current cloud
00639     pcl::ModelCoefficients coefficients;
00640     plane_seg.segment(*inliers_ptr, coefficients);
00641 
00642     // check if we found a surface
00643     if (inliers_ptr->indices.size() == 0)
00644     {
00645       ROS_WARN("Could not find a surface above %fm and below %fm.", z_min, z_max);
00646       *indices_out = *indices_in;
00647       return -numeric_limits<double>::infinity();
00648     }
00649 
00650     // remove the plane
00651     pcl::PointCloud<pcl::PointXYZRGB> plane;
00652     pcl::ExtractIndices<pcl::PointXYZRGB> extract(true);
00653     extract.setInputCloud(pc_copy);
00654     extract.setIndices(inliers_ptr);
00655     extract.setNegative(false);
00656     extract.filter(plane);
00657     extract.setKeepOrganized(true);
00658     plane_seg.setIndices(extract.getRemovedIndices());
00659 
00660     // check the height
00661     double height = this->averageZ(plane.points);
00662     if (height >= z_min && height <= z_max)
00663     {
00664       ROS_INFO("Surface found at %fm.", height);
00665       *indices_out = *plane_seg.getIndices();
00666       return height;
00667     }
00668   }
00669 }
00670 
00671 void Segmenter::extractClusters(const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &in,
00672     const pcl::IndicesConstPtr &indices_in, vector<pcl::PointIndices> &clusters) const
00673 {
00674   // ignore NaN and infinite values
00675   pcl::IndicesPtr valid(new vector<int>);
00676   for (size_t i = 0; i < indices_in->size(); i++)
00677   {
00678     if (pcl_isfinite(in->points[indices_in->at(i)].x) & pcl_isfinite(in->points[indices_in->at(i)].y) &
00679         pcl_isfinite(in->points[indices_in->at(i)].z))
00680     {
00681       valid->push_back(indices_in->at(i));
00682     }
00683   }
00684 
00685   pcl::EuclideanClusterExtraction<pcl::PointXYZRGB> seg;
00686   pcl::search::KdTree<pcl::PointXYZRGB>::Ptr kd_tree(new pcl::search::KdTree<pcl::PointXYZRGB>);
00687   kd_tree->setInputCloud(in);
00688   seg.setClusterTolerance(CLUSTER_TOLERANCE);
00689   seg.setMinClusterSize(min_cluster_size_);
00690   seg.setMaxClusterSize(max_cluster_size_);
00691   seg.setSearchMethod(kd_tree);
00692   seg.setInputCloud(in);
00693   seg.setIndices(valid);
00694   seg.extract(clusters);
00695 }
00696 
00697 sensor_msgs::Image Segmenter::createImage(const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &in,
00698     const pcl::PointIndices &cluster) const
00699 {
00700   // determine the bounds of the cluster
00701   int row_min = numeric_limits<int>::max();
00702   int row_max = numeric_limits<int>::min();
00703   int col_min = numeric_limits<int>::max();
00704   int col_max = numeric_limits<int>::min();
00705 
00706   for (size_t i = 0; i < cluster.indices.size(); i++)
00707   {
00708     // calculate row and column of this point
00709     int row = cluster.indices[i] / in->width;
00710     int col = cluster.indices[i] - (row * in->width);
00711 
00712     if (row < row_min)
00713     {
00714       row_min = row;
00715     } else if (row > row_max)
00716     {
00717       row_max = row;
00718     }
00719     if (col < col_min)
00720     {
00721       col_min = col;
00722     } else if (col > col_max)
00723     {
00724       col_max = col;
00725     }
00726   }
00727 
00728   // create a ROS image
00729   sensor_msgs::Image msg;
00730 
00731   // set basic information
00732   msg.header.frame_id = in->header.frame_id;
00733   msg.header.stamp = ros::Time::now();
00734   msg.width = col_max - col_min;
00735   msg.height = row_max - row_min;
00736   // RGB data
00737   msg.step = 3 * msg.width;
00738   msg.data.resize(msg.step * msg.height);
00739   msg.encoding = sensor_msgs::image_encodings::BGR8;
00740 
00741   // extract the points
00742   for (int h = 0; h < msg.height; h++)
00743   {
00744     for (int w = 0; w < msg.width; w++)
00745     {
00746       // extract RGB information
00747       const pcl::PointXYZRGB &point = in->at(col_min + w, row_min + h);
00748       // set RGB data
00749       int index = (msg.step * h) + (w * 3);
00750       msg.data[index] = point.b;
00751       msg.data[index + 1] = point.g;
00752       msg.data[index + 2] = point.r;
00753     }
00754   }
00755 
00756   return msg;
00757 }
00758 
00759 visualization_msgs::Marker Segmenter::createMarker(const pcl::PCLPointCloud2::ConstPtr &pc) const
00760 {
00761   visualization_msgs::Marker marker;
00762   // set header field
00763   marker.header.frame_id = pc->header.frame_id;
00764 
00765   // default position
00766   marker.pose.position.x = 0.0;
00767   marker.pose.position.y = 0.0;
00768   marker.pose.position.z = 0.0;
00769   marker.pose.orientation.x = 0.0;
00770   marker.pose.orientation.y = 0.0;
00771   marker.pose.orientation.z = 0.0;
00772   marker.pose.orientation.w = 1.0;
00773 
00774   // default scale
00775   marker.scale.x = MARKER_SCALE;
00776   marker.scale.y = MARKER_SCALE;
00777   marker.scale.z = MARKER_SCALE;
00778 
00779   // set the type of marker and our color of choice
00780   marker.type = visualization_msgs::Marker::CUBE_LIST;
00781   marker.color.a = 1.0;
00782 
00783   // downsample point cloud for visualization
00784   pcl::PCLPointCloud2 downsampled;
00785   pcl::VoxelGrid<pcl::PCLPointCloud2> voxel_grid;
00786   voxel_grid.setInputCloud(pc);
00787   voxel_grid.setLeafSize(DOWNSAMPLE_LEAF_SIZE, DOWNSAMPLE_LEAF_SIZE, DOWNSAMPLE_LEAF_SIZE);
00788   voxel_grid.filter(downsampled);
00789 
00790   // convert to an easy to use point cloud message
00791   sensor_msgs::PointCloud2 pc2_msg;
00792   pcl_conversions::fromPCL(downsampled, pc2_msg);
00793   sensor_msgs::PointCloud pc_msg;
00794   sensor_msgs::convertPointCloud2ToPointCloud(pc2_msg, pc_msg);
00795 
00796   // place in the marker message
00797   marker.points.resize(pc_msg.points.size());
00798   int r = 0, g = 0, b = 0;
00799   for (size_t j = 0; j < pc_msg.points.size(); j++)
00800   {
00801     marker.points[j].x = pc_msg.points[j].x;
00802     marker.points[j].y = pc_msg.points[j].y;
00803     marker.points[j].z = pc_msg.points[j].z;
00804 
00805     // use average RGB
00806     uint32_t rgb = *reinterpret_cast<int *>(&pc_msg.channels[0].values[j]);
00807     r += (int) ((rgb >> 16) & 0x0000ff);
00808     g += (int) ((rgb >> 8) & 0x0000ff);
00809     b += (int) ((rgb) & 0x0000ff);
00810   }
00811 
00812   // set average RGB
00813   marker.color.r = ((float) r / (float) pc_msg.points.size()) / 255.0;
00814   marker.color.g = ((float) g / (float) pc_msg.points.size()) / 255.0;
00815   marker.color.b = ((float) b / (float) pc_msg.points.size()) / 255.0;
00816   marker.color.a = 1.0;
00817 
00818   return marker;
00819 }
00820 
00821 void Segmenter::extract(const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &in, const pcl::IndicesConstPtr &indices_in,
00822     const pcl::PointCloud<pcl::PointXYZRGB>::Ptr &out) const
00823 {
00824   pcl::ExtractIndices<pcl::PointXYZRGB> extract;
00825   extract.setInputCloud(in);
00826   extract.setIndices(indices_in);
00827   extract.filter(*out);
00828 }
00829 
00830 void Segmenter::inverseBound(const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &in,
00831     const pcl::IndicesConstPtr &indices_in,
00832     const pcl::ConditionBase<pcl::PointXYZRGB>::Ptr &conditions,
00833     const pcl::IndicesPtr &indices_out) const
00834 {
00835   // use a temp point cloud to extract the indices
00836   pcl::PointCloud<pcl::PointXYZRGB> tmp;
00837   pcl::ConditionalRemoval<pcl::PointXYZRGB> removal(conditions, true);
00838   removal.setInputCloud(in);
00839   removal.setIndices(indices_in);
00840   removal.filter(tmp);
00841   *indices_out = *removal.getRemovedIndices();
00842 }
00843 
00844 double Segmenter::averageZ(const vector<pcl::PointXYZRGB, Eigen::aligned_allocator<pcl::PointXYZRGB> > &v) const
00845 {
00846   double avg = 0.0;
00847   for (size_t i = 0; i < v.size(); i++)
00848   {
00849     avg += v[i].z;
00850   }
00851   return (avg / (double) v.size());
00852 }


rail_segmentation
Author(s): Russell Toris , David Kent
autogenerated on Fri Aug 28 2015 12:20:35