point_cloud_segmentation.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2012, Georgia Institute of Technology
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/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the Georgia Institute of Technology nor the names of
00018  *     its 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 #include <ros/ros.h>
00035 
00036 // TF
00037 #include <tf/transform_datatypes.h>
00038 
00039 // OpenCV
00040 #include <opencv2/highgui/highgui.hpp>
00041 
00042 // PCL
00043 #include <pcl16/common/common.h>
00044 #include <pcl16/common/eigen.h>
00045 #include <pcl16/common/centroid.h>
00046 #include <pcl16/ModelCoefficients.h>
00047 #include <pcl16/sample_consensus/method_types.h>
00048 #include <pcl16/sample_consensus/model_types.h>
00049 #include <pcl16/segmentation/sac_segmentation.h>
00050 #include <pcl16/segmentation/extract_clusters.h>
00051 #include <pcl16/segmentation/segment_differences.h>
00052 #include <pcl16/segmentation/organized_multi_plane_segmentation.h>
00053 #include <pcl16/search/search.h>
00054 #include <pcl16/search/kdtree.h>
00055 #include <pcl16/filters/voxel_grid.h>
00056 #include <pcl16/filters/passthrough.h>
00057 #include <pcl16/filters/extract_indices.h>
00058 #include <pcl16/surface/concave_hull.h>
00059 #include <pcl16/registration/icp.h>
00060 #include <pcl16/registration/icp_nl.h>
00061 #include <pcl16/features/integral_image_normal.h>
00062 #include <pcl16/features/normal_3d.h>
00063 
00064 // STL
00065 #include <sstream>
00066 // Local
00067 #include <tabletop_pushing/point_cloud_segmentation.h>
00068 #include <tabletop_pushing/extern/Timer.hpp>
00069 
00070 // Debugging IFDEFS
00071 // #define DISPLAY_CLOUD_DIFF 1
00072 // #define PROFILE_OBJECT_SEGMENTATION_TIME 1
00073 // #define PROFILE_TABLE_SEGMENTATION_TIME 1
00074 // #define PROFILE_OBJECT_CLUSTER_TIME 1
00075 
00076 #define randf() static_cast<float>(rand())/RAND_MAX
00077 
00078 typedef pcl16::search::KdTree<pcl16::PointXYZ>::Ptr KdTreePtr;
00079 typedef pcl16::search::KdTree<pcl16::PointXYZ>::KdTreeFLANNPtr KdTreeFLANNPtr;
00080 using pcl16::PointXYZ;
00081 
00082 namespace tabletop_pushing
00083 {
00084 
00085 PointCloudSegmentation::PointCloudSegmentation(boost::shared_ptr<tf::TransformListener> tf) : tf_(tf)
00086 {
00087   for (int i = 0; i < 200; ++i)
00088   {
00089     cv::Vec3f rand_color;
00090     rand_color[0] = randf();
00091     rand_color[1] = randf();
00092     rand_color[2] = randf();
00093     colors_.push_back(rand_color);
00094   }
00095 }
00096 
00097 void PointCloudSegmentation::getTablePlane(XYZPointCloud& cloud, XYZPointCloud& objs_cloud, XYZPointCloud& plane_cloud,
00098                                            Eigen::Vector4f& table_centroid, bool find_hull, bool find_centroid)
00099 {
00100 #ifdef PROFILE_TABLE_SEGMENTATION_TIME
00101   long long get_table_plane_start_time = Timer::nanoTime();
00102 #endif
00103 
00104   XYZPointCloud cloud_downsampled;
00105   if (use_voxel_down_)
00106   {
00107     pcl16::VoxelGrid<PointXYZ> downsample;
00108     downsample.setInputCloud(cloud.makeShared());
00109     downsample.setLeafSize(voxel_down_res_, voxel_down_res_, voxel_down_res_);
00110     downsample.filter(cloud_downsampled);
00111   }
00112 #ifdef PROFILE_TABLE_SEGMENTATION_TIME
00113   double downsample_elapsed_time = (((double)(Timer::nanoTime() - get_table_plane_start_time)) /
00114                                   Timer::NANOSECONDS_PER_SECOND);
00115   long long filter_cloud_z_start_time = Timer::nanoTime();
00116 #endif
00117   // Filter Cloud to not look for table planes on the ground
00118   XYZPointCloud cloud_z_filtered, cloud_filtered;
00119   pcl16::PassThrough<PointXYZ> z_pass;
00120   if (use_voxel_down_)
00121   {
00122     z_pass.setInputCloud(cloud_downsampled.makeShared());
00123   }
00124   else
00125   {
00126     z_pass.setInputCloud(cloud.makeShared());
00127   }
00128   z_pass.setFilterFieldName("z");
00129   z_pass.setFilterLimits(min_table_z_, max_table_z_);
00130   z_pass.filter(cloud_z_filtered);
00131 #ifdef PROFILE_TABLE_SEGMENTATION_TIME
00132   double filter_cloud_z_elapsed_time = (((double)(Timer::nanoTime() - filter_cloud_z_start_time)) /
00133                                   Timer::NANOSECONDS_PER_SECOND);
00134   long long filter_cloud_x_start_time = Timer::nanoTime();
00135 #endif
00136   // Filter to be just in the range in front of the robot
00137   pcl16::PassThrough<PointXYZ> x_pass;
00138   x_pass.setInputCloud(cloud_z_filtered.makeShared());
00139   x_pass.setFilterFieldName("x");
00140   x_pass.setFilterLimits(min_workspace_x_, max_workspace_x_);
00141   x_pass.filter(cloud_filtered);
00142 #ifdef PROFILE_TABLE_SEGMENTATION_TIME
00143   double filter_cloud_x_elapsed_time = (((double)(Timer::nanoTime() - filter_cloud_x_start_time)) /
00144                                   Timer::NANOSECONDS_PER_SECOND);
00145   long long RANSAC_start_time = Timer::nanoTime();
00146 #endif
00147   // Segment the tabletop from the points using RANSAC plane fitting
00148   pcl16::ModelCoefficients coefficients;
00149   pcl16::PointIndices plane_inliers;
00150 
00151   // Create the segmentation object
00152   pcl16::SACSegmentation<PointXYZ> plane_seg;
00153   plane_seg.setOptimizeCoefficients(true);
00154   // plane_seg.setModelType(pcl16::SACMODEL_PLANE);
00155   plane_seg.setModelType(pcl16::SACMODEL_PERPENDICULAR_PLANE);
00156   plane_seg.setMethodType(pcl16::SAC_RANSAC);
00157   plane_seg.setDistanceThreshold(table_ransac_thresh_);
00158   plane_seg.setInputCloud(cloud_filtered.makeShared());
00159   Eigen::Vector3f z_axis(0.0, 0.0, 1.0);
00160   plane_seg.setAxis(z_axis);
00161   // plane_seg.setEpsAngle(table_ransac_angle_thresh_);
00162   plane_seg.segment(plane_inliers, coefficients);
00163   pcl16::copyPointCloud(cloud_filtered, plane_inliers, plane_cloud);
00164 
00165   // Extract the outliers from the point clouds
00166   pcl16::ExtractIndices<PointXYZ> extract;
00167   extract.setInputCloud(cloud_filtered.makeShared());
00168   extract.setIndices(boost::make_shared<pcl16::PointIndices>(plane_inliers));
00169   extract.setNegative(true);
00170   extract.filter(objs_cloud);
00171   objs_cloud.header = cloud.header;
00172 #ifdef PROFILE_TABLE_SEGMENTATION_TIME
00173   double RANSAC_elapsed_time = (((double)(Timer::nanoTime() - RANSAC_start_time)) / Timer::NANOSECONDS_PER_SECOND);
00174   long long find_centroid_start_time = Timer::nanoTime();
00175 #endif
00176   // Estimate hull from the inlier points
00177   if (find_hull)
00178   {
00179     ROS_INFO_STREAM("finding concave hull. Plane size: " <<
00180                     plane_cloud.size());
00181     XYZPointCloud hull_cloud;
00182     pcl16::ConcaveHull<PointXYZ> hull;
00183     hull.setInputCloud(plane_cloud.makeShared());
00184     hull.setAlpha(hull_alpha_);
00185     hull.reconstruct(hull_cloud);
00186     ROS_INFO_STREAM("hull_cloud.size() " << hull_cloud.size());
00187     // TODO: Return the hull_cloud
00188     // TODO: Figure out if stuff is inside the hull
00189   }
00190 
00191   // Extract the plane members into their own point cloud
00192   if (find_centroid)
00193   {
00194     pcl16::compute3DCentroid(plane_cloud, table_centroid);
00195     min_workspace_z_ = table_centroid[2];
00196   }
00197   // cv::Size img_size(320, 240);
00198   // cv::Mat plane_img(img_size, CV_8UC1, cv::Scalar(0));
00199   // projectPointCloudIntoImage(plane_cloud, plane_img, cur_camera_header_.frame_id, 255);
00200   // cv::imshow("table plane", plane_img);
00201 
00202 #ifdef PROFILE_TABLE_SEGMENTATION_TIME
00203   double get_table_plane_elapsed_time = (((double)(Timer::nanoTime() - get_table_plane_start_time)) /
00204                                         Timer::NANOSECONDS_PER_SECOND);
00205   double find_centroid_elapsed_time = (((double)(Timer::nanoTime() - find_centroid_start_time)) /
00206                                        Timer::NANOSECONDS_PER_SECOND);
00207   ROS_INFO_STREAM("\t get Table Plane Elapsed Time " << get_table_plane_elapsed_time);
00208   ROS_INFO_STREAM("\t\t downsample Elapsed Time " << downsample_elapsed_time);
00209   ROS_INFO_STREAM("\t\t filter Z Elapsed Time " << filter_cloud_z_elapsed_time);
00210   ROS_INFO_STREAM("\t\t filter X Elapsed Time " << filter_cloud_x_elapsed_time);
00211   ROS_INFO_STREAM("\t\t RANSAC Elapsed Time " << RANSAC_elapsed_time);
00212   ROS_INFO_STREAM("\t\t find Centroid Elapsed Time " << find_centroid_elapsed_time);
00213 #endif
00214 
00215 }
00216 
00217 void PointCloudSegmentation::getTablePlaneMPS(XYZPointCloud& input_cloud, XYZPointCloud& objs_cloud,
00218                                               XYZPointCloud& plane_cloud, Eigen::Vector4f& center,
00219                                               bool find_hull, bool find_centroid)
00220 {
00221   pcl16::IntegralImageNormalEstimation<PointXYZ, pcl16::Normal> ne;
00222   ne.setNormalEstimationMethod (ne.COVARIANCE_MATRIX);
00223   ne.setMaxDepthChangeFactor(0.03f);
00224   ne.setNormalSmoothingSize(20.0f);
00225   pcl16::PointCloud<pcl16::Normal>::Ptr normal_cloud(new pcl16::PointCloud<pcl16::Normal>);
00226   ne.setInputCloud(input_cloud.makeShared());
00227   ne.compute(*normal_cloud);
00228 
00229   // cv::Mat normal_img(cv::Size(input_cloud.width, input_cloud.height), CV_32FC3, cv::Scalar(0));
00230   // for (int x = 0; x < normal_img.cols; ++x)
00231   //   for (int y = 0; y < normal_img.rows; ++y)
00232   // {
00233   //   cv::Vec3f norm;
00234   //   norm[0] = abs(normal_cloud->at(x,y).normal_x);
00235   //   norm[1] = abs(normal_cloud->at(x,y).normal_y);
00236   //   norm[2] = abs(normal_cloud->at(x,y).normal_z);
00237   //   normal_img.at<cv::Vec3f>(y,x) = norm;
00238   // }
00239   // cv::imshow("normals", normal_img);
00240   // cv::waitKey();
00241 
00242   pcl16::OrganizedMultiPlaneSegmentation<PointXYZ, pcl16::Normal, pcl16::Label> mps;
00243   mps.setMinInliers(mps_min_inliers_);
00244   mps.setAngularThreshold(mps_min_angle_thresh_*M_PI/180.);
00245   mps.setDistanceThreshold(mps_min_dist_thresh_);
00246   mps.setInputNormals(normal_cloud);
00247   mps.setInputCloud(input_cloud.makeShared());
00248   std::vector<pcl16::PlanarRegion<PointXYZ>,
00249               Eigen::aligned_allocator<pcl16::PlanarRegion<PointXYZ> > > regions;
00250   regions.clear();
00251   std::vector<pcl16::ModelCoefficients> coefficients;
00252   std::vector<pcl16::PointIndices> point_indices;
00253   pcl16::PointCloud<pcl16::Label>::Ptr labels(new pcl16::PointCloud<pcl16::Label>());
00254   std::vector<pcl16::PointIndices> label_indices;
00255   std::vector<pcl16::PointIndices> boundary_indices;
00256   point_indices.clear();
00257   label_indices.clear();
00258   boundary_indices.clear();
00259   mps.segmentAndRefine(regions, coefficients, point_indices, labels, label_indices, boundary_indices);
00260 
00261   // TODO: Figure out which ones are part of the table
00262 
00263   // for (size_t i = 0; i < regions.size (); i++)
00264   if (regions.size() > 0)
00265   {
00266     pcl16::copyPointCloud(input_cloud, point_indices[0], plane_cloud);
00267     center[0] = regions[0].getCentroid()[0];
00268     center[1] = regions[0].getCentroid()[1];
00269     center[2] = regions[0].getCentroid()[2];
00270     center[3] = 1.0;
00271 
00272     // Extract the outliers from the point clouds
00273     pcl16::ExtractIndices<PointXYZ> extract;
00274     extract.setInputCloud(input_cloud.makeShared());
00275     extract.setIndices(boost::make_shared<pcl16::PointIndices>(point_indices[0]));
00276     extract.setNegative(true);
00277     extract.filter(objs_cloud);
00278   }
00279   // cv::Size img_size(320, 240);
00280   // cv::Mat plane_img(img_size, CV_8UC1, cv::Scalar(0));
00281   // for (int i = 0; i < regions.size(); i++)
00282   // {
00283   //   XYZPointCloud cloud_i;
00284   //   pcl16::copyPointCloud(input_cloud, point_indices[i], cloud_i);
00285   //   projectPointCloudIntoImage(cloud_i, plane_img, cur_camera_header_.frame_id, i+1);
00286   // }
00287   // displayObjectImage(plane_img, "MPS regions", true);
00288   // ROS_INFO_STREAM("Num regions: " << regions.size());
00289   // ROS_INFO_STREAM("Table center: " << center);
00290 }
00291 
00292 
00301 void PointCloudSegmentation::findTabletopObjects(XYZPointCloud& input_cloud, ProtoObjects& objs, bool use_mps)
00302 {
00303   XYZPointCloud objs_cloud;
00304   findTabletopObjects(input_cloud, objs, objs_cloud, use_mps);
00305 }
00306 
00316 void PointCloudSegmentation::findTabletopObjects(XYZPointCloud& input_cloud, ProtoObjects& objs,
00317                                                  XYZPointCloud& objs_cloud, bool use_mps)
00318 {
00319   XYZPointCloud table_cloud;
00320   findTabletopObjects(input_cloud, objs, objs_cloud, table_cloud, use_mps);
00321 }
00322 
00333 void PointCloudSegmentation::findTabletopObjects(XYZPointCloud& input_cloud, ProtoObjects& objs,
00334                                                  XYZPointCloud& objs_cloud,
00335                                                  XYZPointCloud& plane_cloud, bool use_mps)
00336 {
00337 #ifdef PROFILE_OBJECT_SEGMENTATION_TIME
00338   long long find_tabletop_objects_start_time = Timer::nanoTime();
00339 #endif
00340 
00341   // Get table plane
00342   if (use_mps)
00343   {
00344     getTablePlaneMPS(input_cloud, objs_cloud, plane_cloud, table_centroid_, false);
00345   }
00346   else
00347   {
00348     getTablePlane(input_cloud, objs_cloud, plane_cloud, table_centroid_, false);
00349   }
00350 
00351 #ifdef PROFILE_OBJECT_SEGMENTATION_TIME
00352   double segment_table_elapsed_time = (((double)(Timer::nanoTime() - find_tabletop_objects_start_time)) /
00353                                     Timer::NANOSECONDS_PER_SECOND);
00354   long long cluster_objects_start_time = Timer::nanoTime();
00355 #endif
00356 
00357   // TODO: Check the downsample rate vs not downsampling
00358   // TODO: Check the tracking quality
00359   // TODO: Make a switch to easily choose between these two methods
00360   XYZPointCloud objects_cloud_down;
00361   downsampleCloud(objs_cloud, objects_cloud_down);
00362   // Find independent regions
00363   if (objects_cloud_down.size() > 0)
00364   {
00365     clusterProtoObjects(objects_cloud_down, objs);
00366   }
00367   // clusterProtoObjects(objs_cloud, objs);
00368 #ifdef PROFILE_OBJECT_SEGMENTATION_TIME
00369   double find_tabletop_objects_elapsed_time = (((double)(Timer::nanoTime() - find_tabletop_objects_start_time)) /
00370                                            Timer::NANOSECONDS_PER_SECOND);
00371   double cluster_objects_elapsed_time = (((double)(Timer::nanoTime() - cluster_objects_start_time)) /
00372                                       Timer::NANOSECONDS_PER_SECOND);
00373   ROS_INFO_STREAM("find_tabletop_objects_elapsed_time " << find_tabletop_objects_elapsed_time);
00374   if (use_mps)
00375   {
00376     ROS_INFO_STREAM("\t segment Table MPS Time " << segment_table_elapsed_time);
00377   }
00378   else
00379   {
00380     ROS_INFO_STREAM("\t segment table RANSAC Time " << segment_table_elapsed_time);
00381   }
00382   ROS_INFO_STREAM("\t cluster_objects_elapsed_time " << cluster_objects_elapsed_time << "\n");
00383 #endif
00384 
00385 }
00386 
00393 void PointCloudSegmentation::clusterProtoObjects(XYZPointCloud& objects_cloud, ProtoObjects& objs)
00394 {
00395 #ifdef PROFILE_OBJECT_CLUSTER_TIME
00396   long long cluster_objects_start_time = Timer::nanoTime();
00397 #endif
00398   std::vector<pcl16::PointIndices> clusters;
00399   pcl16::EuclideanClusterExtraction<PointXYZ> pcl_cluster;
00400   const KdTreePtr clusters_tree(new pcl16::search::KdTree<PointXYZ>);
00401   clusters_tree->setInputCloud(objects_cloud.makeShared());
00402 
00403   pcl_cluster.setClusterTolerance(cluster_tolerance_);
00404   pcl_cluster.setMinClusterSize(min_cluster_size_);
00405   pcl_cluster.setMaxClusterSize(max_cluster_size_);
00406   pcl_cluster.setSearchMethod(clusters_tree);
00407   pcl_cluster.setInputCloud(objects_cloud.makeShared());
00408   pcl_cluster.extract(clusters);
00409   ROS_DEBUG_STREAM("Number of clusters found matching the given constraints: "
00410                    << clusters.size());
00411 #ifdef PROFILE_OBJECT_CLUSTER_TIME
00412   double pcl_cluster_elapsed_time = (((double)(Timer::nanoTime() - cluster_objects_start_time)) /
00413                                      Timer::NANOSECONDS_PER_SECOND);
00414   long long proto_object_start_time = Timer::nanoTime();
00415 #endif
00416 
00417   for (unsigned int i = 0; i < clusters.size(); ++i)
00418   {
00419     // Create proto objects from the point cloud
00420     ProtoObject po;
00421     po.push_history.clear();
00422     po.boundary_angle_dist.clear();
00423     pcl16::copyPointCloud(objects_cloud, clusters[i], po.cloud);
00424     pcl16::compute3DCentroid(po.cloud, po.centroid);
00425     po.id = i;
00426     po.moved = false;
00427     po.transform = Eigen::Matrix4f::Identity();
00428     po.singulated = false;
00429     objs.push_back(po);
00430   }
00431 #ifdef PROFILE_OBJECT_CLUSTER_TIME
00432   double cluster_objects_elapsed_time = (((double)(Timer::nanoTime() - cluster_objects_start_time)) /
00433                                          Timer::NANOSECONDS_PER_SECOND);
00434   double proto_object_elapsed_time = (((double)(Timer::nanoTime() - proto_object_start_time)) /
00435                                       Timer::NANOSECONDS_PER_SECOND);
00436   ROS_INFO_STREAM("\t cluster_objects_elapsed_time " << cluster_objects_elapsed_time);
00437   ROS_INFO_STREAM("\t\t pcl_cluster_elapsed_time " << pcl_cluster_elapsed_time);
00438   ROS_INFO_STREAM("\t\t proto_object_elapsed_time " << proto_object_elapsed_time);
00439 #endif
00440 
00441 }
00442 
00451 double PointCloudSegmentation::ICPProtoObjects(ProtoObject& a, ProtoObject& b,
00452                                                Eigen::Matrix4f& transform)
00453 {
00454   // TODO: Investigate this!
00455   // pcl16::IterativeClosestPointNonLinear<PointXYZ, PointXYZ> icp;
00456   pcl16::IterativeClosestPoint<PointXYZ, PointXYZ> icp;
00457   icp.setMaximumIterations(icp_max_iters_);
00458   icp.setTransformationEpsilon(icp_transform_eps_);
00459   icp.setMaxCorrespondenceDistance(icp_max_cor_dist_);
00460   icp.setRANSACOutlierRejectionThreshold(icp_ransac_thresh_);
00461   icp.setInputCloud(boost::make_shared<XYZPointCloud>(a.cloud));
00462   icp.setInputTarget(boost::make_shared<XYZPointCloud>(b.cloud));
00463   XYZPointCloud aligned;
00464   icp.align(aligned);
00465   double score = icp.getFitnessScore();
00466   transform = icp.getFinalTransformation();
00467   return score;
00468 }
00469 
00479 double PointCloudSegmentation::ICPBoundarySamples(XYZPointCloud& hull_t_0, XYZPointCloud& hull_t_1,
00480                                                   Eigen::Matrix4f& transform, XYZPointCloud& aligned)
00481 {
00482   // TODO: Investigate this!
00483   pcl16::IterativeClosestPointNonLinear<PointXYZ, PointXYZ> icp;
00484   // pcl16::IterativeClosestPoint<PointXYZ, PointXYZ> icp;
00485   icp.setMaximumIterations(icp_max_iters_);
00486   icp.setTransformationEpsilon(icp_transform_eps_);
00487   icp.setMaxCorrespondenceDistance(icp_max_cor_dist_);
00488   icp.setRANSACOutlierRejectionThreshold(icp_ransac_thresh_);
00489   icp.setInputCloud(boost::make_shared<XYZPointCloud>(hull_t_0));
00490   icp.setInputTarget(boost::make_shared<XYZPointCloud>(hull_t_1));
00491   icp.align(aligned);
00492   double score = icp.getFitnessScore();
00493   transform = icp.getFinalTransformation();
00494   return score;
00495 }
00496 
00505 void PointCloudSegmentation::getMovedRegions(XYZPointCloud& prev_cloud, XYZPointCloud& cur_cloud,
00506                                              ProtoObjects& moved, std::string suf)
00507 {
00508   // cloud_out = prev_cloud - cur_cloud
00509   pcl16::SegmentDifferences<PointXYZ> pcl_diff;
00510   pcl_diff.setDistanceThreshold(cloud_diff_thresh_);
00511   pcl_diff.setInputCloud(prev_cloud.makeShared());
00512   pcl_diff.setTargetCloud(cur_cloud.makeShared());
00513   XYZPointCloud cloud_out;
00514   pcl_diff.segment(cloud_out);
00515   if (cloud_out.size() < 1)
00516   {
00517     ROS_INFO_STREAM("Returning nothing moved as there are no points.");
00518     return;
00519   }
00520 
00521   clusterProtoObjects(cloud_out, moved);
00522 
00523 #ifdef DISPLAY_CLOUD_DIFF
00524   cv::Size img_size(320, 240);
00525   cv::Mat moved_img = projectProtoObjectsIntoImage(moved, img_size, prev_cloud.header.frame_id);
00526   std::stringstream cluster_title;
00527   cluster_title << "moved clusters" << suf;
00528   displayObjectImage(moved_img, cluster_title.str());
00529 #endif // DISPLAY_CLOUD_DIFF
00530 }
00531 
00539 void PointCloudSegmentation::matchMovedRegions(ProtoObjects& objs,
00540                                                ProtoObjects& moved_regions)
00541 {
00542   // Determining which previous objects have moved
00543   for (unsigned int i = 0; i < moved_regions.size(); ++i)
00544   {
00545     for (unsigned int j = 0; j < objs.size(); ++j)
00546     {
00547       if(cloudsIntersect(objs[j].cloud, moved_regions[i].cloud))
00548       {
00549         if (!objs[j].moved)
00550         {
00551           objs[j].moved = true;
00552         }
00553       }
00554     }
00555   }
00556 }
00557 
00565 void PointCloudSegmentation::fitCylinderRANSAC(ProtoObject& obj, XYZPointCloud& cylinder_cloud,
00566                                                pcl16::ModelCoefficients& coefficients)
00567 {
00568   pcl16::NormalEstimation<PointXYZ, pcl16::Normal> ne;
00569   ne.setInputCloud(obj.cloud.makeShared());
00570   pcl16::search::KdTree<PointXYZ>::Ptr tree (new pcl16::search::KdTree<PointXYZ> ());
00571   ne.setSearchMethod (tree);
00572   ne.setRadiusSearch (0.03);
00573   ne.compute(obj.normals);
00574 
00575   // Create the segmentation object
00576   Eigen::Vector3f z_axis(0.0,0.0,1.0);
00577   pcl16::PointIndices cylinder_inliers;
00578   pcl16::SACSegmentationFromNormals<PointXYZ,pcl16::Normal> cylinder_seg;
00579   cylinder_seg.setOptimizeCoefficients(optimize_cylinder_coefficients_);
00580   cylinder_seg.setModelType(pcl16::SACMODEL_CYLINDER);
00581   cylinder_seg.setMethodType(pcl16::SAC_RANSAC);
00582   cylinder_seg.setDistanceThreshold(cylinder_ransac_thresh_);
00583   cylinder_seg.setAxis(z_axis);
00584   // cylinder_seg.setEpsAngle(cylinder_ransac_angle_thresh_);
00585   cylinder_seg.setInputCloud(obj.cloud.makeShared());
00586   cylinder_seg.setInputNormals(obj.normals.makeShared());
00587   cylinder_seg.segment(cylinder_inliers, coefficients);
00588 
00589   pcl16::copyPointCloud(obj.cloud, cylinder_inliers, cylinder_cloud);
00590 }
00591 
00599 void PointCloudSegmentation::fitSphereRANSAC(ProtoObject& obj, XYZPointCloud& sphere_cloud,
00600                                              pcl16::ModelCoefficients& coefficients)
00601 {
00602   // Create the segmentation object
00603   pcl16::PointIndices sphere_inliers;
00604   pcl16::SACSegmentation<PointXYZ> sphere_seg;
00605   sphere_seg.setOptimizeCoefficients(true);
00606   sphere_seg.setModelType(pcl16::SACMODEL_SPHERE);
00607   sphere_seg.setMethodType(pcl16::SAC_RANSAC);
00608   sphere_seg.setDistanceThreshold(sphere_ransac_thresh_);
00609   sphere_seg.setInputCloud(obj.cloud.makeShared());
00610   sphere_seg.segment(sphere_inliers, coefficients);
00611 
00612   pcl16::copyPointCloud(obj.cloud, sphere_inliers, sphere_cloud);
00613 }
00614 
00625 bool PointCloudSegmentation::cloudsIntersect(XYZPointCloud cloud0, XYZPointCloud cloud1)
00626 {
00627   int moved_count = 0;
00628   for (unsigned int i = 0; i < cloud0.size(); ++i)
00629   {
00630     const PointXYZ pt0 = cloud0.at(i);
00631     for (unsigned int j = 0; j < cloud1.size(); ++j)
00632     {
00633       const PointXYZ pt1 = cloud1.at(j);
00634       if (dist(pt0, pt1) < cloud_intersect_thresh_)
00635       {
00636         moved_count++;
00637       }
00638       if (moved_count > moved_count_thresh_)
00639       {
00640         return true;
00641       }
00642     }
00643   }
00644   return false;
00645 }
00646 
00647 bool PointCloudSegmentation::cloudsIntersect(XYZPointCloud cloud0, XYZPointCloud cloud1, double thresh)
00648 {
00649   for (unsigned int i = 0; i < cloud0.size(); ++i)
00650   {
00651     const PointXYZ pt0 = cloud0.at(i);
00652     for (unsigned int j = 0; j < cloud1.size(); ++j)
00653     {
00654       const PointXYZ pt1 = cloud1.at(j);
00655       if (dist(pt0, pt1) < thresh) return true;
00656     }
00657   }
00658   return false;
00659 }
00660 
00661 bool PointCloudSegmentation::pointIntersectsCloud(XYZPointCloud cloud, geometry_msgs::Point pt, double thresh)
00662 {
00663   for (unsigned int i = 0; i < cloud.size(); ++i)
00664   {
00665     const PointXYZ pt_c = cloud.at(i);
00666     if (dist(pt_c, pt) < thresh) return true;
00667   }
00668   return false;
00669 }
00670 
00671 float PointCloudSegmentation::pointLineXYDist(PointXYZ p, Eigen::Vector3f vec, Eigen::Vector4f base)
00672 {
00673   Eigen::Vector3f x0(p.x,p.y,0.0);
00674   Eigen::Vector3f x1(base[0],base[1],0.0);
00675   Eigen::Vector3f x2 = x1+vec;
00676   Eigen::Vector3f num = (x0 - x1);
00677   num = num.cross(x0 - x2);
00678   Eigen::Vector3f den = x2 - x1;
00679   float d = num.norm()/den.norm();
00680   return d;
00681 }
00682 
00683 void PointCloudSegmentation::lineCloudIntersection(XYZPointCloud& cloud, Eigen::Vector3f vec,
00684                                                    Eigen::Vector4f base, XYZPointCloud& line_cloud)
00685 {
00686   // Define parametric model of the line defined by base and vec and
00687   // test cloud memebers for distance from the line, if the distance is less
00688   // than epsilon say it intersects and add to the output set.
00689   pcl16::PointIndices line_inliers;
00690   for (unsigned int i = 0; i < cloud.size(); ++i)
00691   {
00692     const PointXYZ pt = cloud.at(i);
00693     if (pointLineXYDist(pt, vec, base) < cloud_intersect_thresh_)
00694     {
00695       line_inliers.indices.push_back(i);
00696     }
00697   }
00698 
00699   // Extract the interesecting points of the line.
00700   pcl16::ExtractIndices<PointXYZ> extract;
00701   extract.setInputCloud(cloud.makeShared());
00702   extract.setIndices(boost::make_shared<pcl16::PointIndices>(line_inliers));
00703   extract.filter(line_cloud);
00704 }
00705 
00706 void PointCloudSegmentation::lineCloudIntersectionEndPoints(XYZPointCloud& cloud, Eigen::Vector3f vec,
00707                                                             Eigen::Vector4f base, std::vector<PointXYZ>& points)
00708 {
00709   XYZPointCloud intersection;
00710   lineCloudIntersection(cloud, vec, base, intersection);
00711   unsigned int min_y_idx = intersection.size();
00712   unsigned int max_y_idx = intersection.size();
00713   unsigned int min_x_idx = intersection.size();
00714   unsigned int max_x_idx = intersection.size();
00715   float min_y = FLT_MAX;
00716   float max_y = -FLT_MAX;
00717   float min_x = FLT_MAX;
00718   float max_x = -FLT_MAX;
00719   for (unsigned int i = 0; i < intersection.size(); ++i)
00720   {
00721     if (intersection.at(i).y < min_y)
00722     {
00723       min_y = intersection.at(i).y;
00724       min_y_idx = i;
00725     }
00726     if (intersection.at(i).y > max_y)
00727     {
00728       max_y = intersection.at(i).y;
00729       max_y_idx = i;
00730     }
00731     if (intersection.at(i).x < min_x)
00732     {
00733       min_x = intersection.at(i).x;
00734       min_x_idx = i;
00735     }
00736     if (intersection.at(i).x > max_x)
00737     {
00738       max_x = intersection.at(i).x;
00739       max_x_idx = i;
00740     }
00741   }
00742   const double y_dist_obs = max_y - min_y;
00743   const double x_dist_obs = max_x - min_x;
00744   int start_idx = min_x_idx;
00745   int end_idx = max_x_idx;
00746 
00747   if (x_dist_obs > y_dist_obs)
00748   {
00749     // Use X index
00750     if (vec[0] > 0)
00751     {
00752       // Use min
00753       start_idx = min_x_idx;
00754       end_idx = max_x_idx;
00755     }
00756     else
00757     {
00758       // use max
00759       start_idx = max_x_idx;
00760       end_idx = min_x_idx;
00761     }
00762   }
00763   else
00764   {
00765     // Use Y index
00766     if (vec[1] > 0)
00767     {
00768       // Use min
00769       start_idx = min_y_idx;
00770       end_idx = max_y_idx;
00771     }
00772     else
00773     {
00774       // use max
00775       start_idx = max_y_idx;
00776       end_idx = min_y_idx;
00777     }
00778   }
00779   PointXYZ start_point, end_point;
00780   start_point.x = intersection.at(start_idx).x;
00781   start_point.y = intersection.at(start_idx).y;
00782   start_point.z = intersection.at(start_idx).z;
00783   end_point.x = intersection.at(end_idx).x;
00784   end_point.y = intersection.at(end_idx).y;
00785   end_point.z = intersection.at(end_idx).z;
00786   points.push_back(start_point);
00787   points.push_back(end_point);
00788 }
00789 
00798 void PointCloudSegmentation::downsampleCloud(XYZPointCloud& cloud_in, XYZPointCloud& cloud_down)
00799 {
00800   XYZPointCloud cloud_z_filtered, cloud_x_filtered;
00801   pcl16::PassThrough<PointXYZ> z_pass;
00802   z_pass.setFilterFieldName("z");
00803   ROS_DEBUG_STREAM("Number of points in cloud_in is: " <<
00804                    cloud_in.size());
00805   z_pass.setInputCloud(cloud_in.makeShared());
00806   z_pass.setFilterLimits(min_workspace_z_, max_workspace_z_);
00807   z_pass.filter(cloud_z_filtered);
00808   ROS_DEBUG_STREAM("Number of points in cloud_z_filtered is: " <<
00809                    cloud_z_filtered.size());
00810 
00811   pcl16::PassThrough<PointXYZ> x_pass;
00812   x_pass.setInputCloud(cloud_z_filtered.makeShared());
00813   x_pass.setFilterFieldName("x");
00814   x_pass.setFilterLimits(min_workspace_x_, max_workspace_x_);
00815   x_pass.filter(cloud_x_filtered);
00816 
00817   pcl16::VoxelGrid<PointXYZ> downsample_outliers;
00818   downsample_outliers.setInputCloud(cloud_x_filtered.makeShared());
00819   downsample_outliers.setLeafSize(voxel_down_res_, voxel_down_res_,
00820                                   voxel_down_res_);
00821   downsample_outliers.filter(cloud_down);
00822   ROS_DEBUG_STREAM("Number of points in objs_downsampled: " <<
00823                    cloud_down.size());
00824 }
00825 
00835 cv::Mat PointCloudSegmentation::projectProtoObjectsIntoImage(ProtoObjects& objs, cv::Size img_size,
00836                                                              std::string target_frame)
00837 {
00838   cv::Mat obj_img(img_size, CV_8UC1, cv::Scalar(0));
00839   for (unsigned int i = 0; i < objs.size(); ++i)
00840   {
00841     projectPointCloudIntoImage(objs[i].cloud, obj_img,
00842                                cur_camera_header_.frame_id, i+1);
00843   }
00844 
00845   return obj_img;
00846 }
00847 
00857 cv::Mat PointCloudSegmentation::projectProtoObjectIntoImage(ProtoObject& obj, cv::Size img_size,
00858                                                             std::string target_frame)
00859 {
00860   cv::Mat obj_img(img_size, CV_8UC1, cv::Scalar(0));
00861   projectPointCloudIntoImage(obj.cloud, obj_img, cur_camera_header_.frame_id, 1);
00862   return obj_img;
00863 }
00864 
00871 cv::Mat PointCloudSegmentation::displayObjectImage(cv::Mat& obj_img,
00872                                                    std::string win_name,
00873                                                    bool use_display)
00874 {
00875   cv::Mat obj_disp_img(obj_img.size(), CV_32FC3, cv::Scalar(0.0,0.0,0.0));
00876   for (int r = 0; r < obj_img.rows; ++r)
00877   {
00878     for (int c = 0; c < obj_img.cols; ++c)
00879     {
00880       unsigned int id = obj_img.at<uchar>(r,c);
00881       if (id > 0)
00882       {
00883         obj_disp_img.at<cv::Vec3f>(r,c) = colors_[id-1];
00884       }
00885     }
00886   }
00887   if (use_display)
00888   {
00889     cv::imshow(win_name, obj_disp_img);
00890   }
00891   return obj_disp_img;
00892 }
00893 
00894 void PointCloudSegmentation::projectPointCloudIntoImage(XYZPointCloud& cloud,
00895                                                         cv::Mat& lbl_img,
00896                                                         std::string target_frame,
00897                                                         unsigned int id)
00898 {
00899   for (unsigned int i = 0; i < cloud.size(); ++i)
00900   {
00901     cv::Point img_idx = projectPointIntoImage(cloud.at(i),
00902                                               cloud.header.frame_id,
00903                                               target_frame);
00904     lbl_img.at<uchar>(img_idx.y, img_idx.x) = id;
00905   }
00906 }
00907 
00908 cv::Point PointCloudSegmentation::projectPointIntoImage(Eigen::Vector3f cur_point_eig,
00909                                                         std::string point_frame,
00910                                                         std::string target_frame)
00911 {
00912   geometry_msgs::PointStamped cur_point;
00913   cur_point.header.frame_id = point_frame;
00914   cur_point.point.x = cur_point_eig[0];
00915   cur_point.point.y = cur_point_eig[1];
00916   cur_point.point.z = cur_point_eig[2];
00917   return projectPointIntoImage(cur_point, target_frame);
00918 }
00919 
00920 cv::Point PointCloudSegmentation::projectPointIntoImage(PointXYZ cur_point_pcl,
00921                                                         std::string point_frame,
00922                                                         std::string target_frame)
00923 {
00924   geometry_msgs::PointStamped cur_point;
00925   cur_point.header.frame_id = point_frame;
00926   cur_point.point.x = cur_point_pcl.x;
00927   cur_point.point.y = cur_point_pcl.y;
00928   cur_point.point.z = cur_point_pcl.z;
00929   return projectPointIntoImage(cur_point, target_frame);
00930 }
00931 
00932 void PointCloudSegmentation::projectPointCloudIntoImage(XYZPointCloud& cloud,
00933                                                         cv::Mat& lbl_img)
00934 {
00935   for (unsigned int i = 0; i < cloud.size(); ++i)
00936   {
00937     cv::Point img_idx = projectPointIntoImage(cloud.at(i),
00938                                               cloud.header.frame_id,
00939                                               cur_camera_header_.frame_id);
00940     lbl_img.at<uchar>(img_idx.y, img_idx.x) = 1;
00941   }
00942 }
00943 
00944 cv::Point PointCloudSegmentation::projectPointIntoImage(
00945     geometry_msgs::PointStamped cur_point)
00946 {
00947   return projectPointIntoImage(cur_point, cur_camera_header_.frame_id);
00948 }
00949 
00950 cv::Point PointCloudSegmentation::projectPointIntoImage(
00951     geometry_msgs::PointStamped cur_point, std::string target_frame)
00952 {
00953   cv::Point img_loc;
00954   try
00955   {
00956     // Transform point into the camera frame
00957     geometry_msgs::PointStamped image_frame_loc_m;
00958     tf_->transformPoint(target_frame, cur_point, image_frame_loc_m);
00959 
00960     // Project point onto the image
00961     img_loc.x = static_cast<int>((cam_info_.K[0]*image_frame_loc_m.point.x +
00962                                   cam_info_.K[2]*image_frame_loc_m.point.z) /
00963                                  image_frame_loc_m.point.z);
00964     img_loc.y = static_cast<int>((cam_info_.K[4]*image_frame_loc_m.point.y +
00965                                   cam_info_.K[5]*image_frame_loc_m.point.z) /
00966                                  image_frame_loc_m.point.z);
00967 
00968     // Downsample poses if the image is downsampled
00969     for (int i = 0; i < num_downsamples_; ++i)
00970     {
00971       img_loc.x /= 2;
00972       img_loc.y /= 2;
00973     }
00974   }
00975   catch (tf::TransformException e)
00976   {
00977     ROS_ERROR_STREAM("Error projecting 3D point into image plane.");
00978     // ROS_ERROR_STREAM(e.what());
00979     ROS_ERROR_STREAM("cur point header is: " << cur_point.header.frame_id);
00980     ROS_ERROR_STREAM("target frame is: " << target_frame);
00981   }
00982   return img_loc;
00983 }
00984 
00985 };


tabletop_pushing
Author(s): Tucker Hermans
autogenerated on Wed Nov 27 2013 11:59:44