segmenter.cc
Go to the documentation of this file.
00001 /*
00002  * segmenter.cc
00003  * Mac Mason <mmason@willowgarage.com>
00004  *
00005  * Implementation. See segmenter.hh.
00006  */
00007 
00008 #include <sstream>
00009 #include <boost/foreach.hpp>
00010 #define foreach BOOST_FOREACH
00011 #include <boost/format.hpp>
00012 
00013 #include "ros/ros.h"
00014 #include "pcl16_ros/transforms.h"
00015 #include "pcl16/filters/passthrough.h"
00016 #include "pcl16/filters/voxel_grid.h"
00017 #include "pcl16/filters/project_inliers.h"
00018 #include "pcl16/filters/extract_indices.h"
00019 #include "pcl16/filters/radius_outlier_removal.h"
00020 #include "pcl16/segmentation/extract_clusters.h"
00021 #include "pcl16/segmentation/extract_polygonal_prism_data.h"
00022 #include "pcl16/kdtree/kdtree.h"
00023 #include "pcl16/surface/convex_hull.h"
00024 #include "pcl16/ros/conversions.h"
00025 
00026 #include "semanticmodel/Plane.h"
00027 #include "semanticmodel/centroid.hh"
00028 #include "semanticmodel/segmenter.hh"
00029 #include "semanticmodel/Blobs.h"
00030 #include "semanticmodel/PlaneExchange.h"
00031 
00032 typedef pcl16::PointCloud<pcl16::Normal> NormalCloud;
00033 
00034 // These are for debugging; I should get rid of them.
00035 std::string CloudsToString(
00036     const std::vector< pcl16::PointCloud<pcl16::PointXYZRGB>::Ptr >& vec)
00037 {
00038   std::stringstream ss;
00039   ss << vec.size() << ": [ ";
00040   for (size_t i = 0 ; i < vec.size() ; ++i)
00041   {
00042     ss << vec[i]->size() << " ";
00043   }
00044   ss << "]";
00045   return ss.str();
00046 }
00047 
00048 double degrees(double radians)
00049 {
00050   return radians * 180.0 / M_PI;
00051 }
00052 
00053 // See above, re: debugging.
00054 class ScopedTickTock
00055 {
00056   public:
00057     std::string start;
00058     std::string end;
00059     ros::Time starttime;
00060     ScopedTickTock(const std::string& _start, const std::string& _end)
00061       : start(_start), end(_end), starttime(ros::Time::now())
00062     {
00063       ROS_INFO_STREAM("TickTock Start: " << start);
00064     }
00065     ~ScopedTickTock()
00066     {
00067       ROS_INFO_STREAM("TickTock Stop: " << 
00068                       end << 
00069                       " (" << 
00070                       (ros::Time::now() - starttime).toSec() <<
00071                       ") sec");
00072 
00073     }
00074 };
00075 
00076 namespace semanticmodel
00077 {
00078 
00079 Segmenter::Segmenter(const std::string& name_space,
00080                      const std::string& cloud_input_topic,
00081                      const std::string& camera_input_topic,
00082                      const std::string& depth_input_topic,
00083                      const std::string& planecloud_output_topic,
00084                      const std::string& plane_output_topic,
00085                      const std::string& object_output_topic,
00086                      const std::string& plane_service_name)
00087   : handle(name_space),
00088     config(),
00089     dynparam_srv(),
00090     dynparam_func(),
00091     config_mutex(),
00092     listener(NULL),
00093     broadcaster(NULL),
00094     sync(NULL),
00095     cloud_sub(),
00096     rgb_transport(handle),
00097     depth_transport(handle),
00098     cam_sub(),
00099     depth_sub(),
00100     map_points_pub(),
00101     planecloud_pub(),
00102     plane_pub(),
00103     object_pub(),
00104     planetracker_srv(),
00105     latest_planes_(10),
00106     short_circuit(false),
00107     processed(0),
00108     skipped(0)
00109 {
00110   // Enforce our input requirements.
00111   ROS_FATAL_COND(
00112       cloud_input_topic == "" || camera_input_topic == "",
00113       "You must provide both cloud_input_topic and image_input_topic!");
00114   ROS_ASSERT(cloud_input_topic != "" && camera_input_topic != "");
00115 
00116   // Set up dynamic_reconfigure.
00117   dynparam_func = 
00118     boost::bind(&Segmenter::dynamic_reconfigure_callback, this, _1, _2);
00119   dynparam_srv.setCallback(dynparam_func);
00120 
00121   listener.reset(new tf::TransformListener(ros::Duration(600)));
00122   broadcaster.reset(new tf::TransformBroadcaster());
00123   
00124   // Set up normal estimator
00125   normal_estimator_.setNormalEstimationMethod(
00126       normal_estimator_.COVARIANCE_MATRIX);
00127   normal_estimator_.setMaxDepthChangeFactor(0.02f);
00128   normal_estimator_.setNormalSmoothingSize(20.0f);
00129   
00130   // Segmenter
00131   plane_segmenter_.setMinInliers(1000);
00132   plane_segmenter_.setAngularThreshold(0.05); // About 3 degrees
00133   plane_segmenter_.setDistanceThreshold(0.02); // 2 cm
00134   comp_.reset(new pcl16::EdgeAwarePlaneComparator<Point, pcl16::Normal>());
00135   plane_segmenter_.setComparator(comp_);
00136 
00137   // See if we're short-circuiting.
00138   if (!handle.getParam("/short_circuit", short_circuit))
00139     ROS_DEBUG("short_circuit parameter is missing; using false.");
00140   else
00141     ROS_DEBUG_STREAM("Setting short_circuit to " << short_circuit);
00142 
00143   // Get the synchronizer spun up.
00144   sync.reset(new message_filters::Synchronizer<Sync>(Sync(QUEUE_SIZE)));
00145   sync->registerCallback(
00146       boost::bind(&Segmenter::synchronized_pipeline_callback, 
00147                   this, _1, _2, _3, _4, _5));
00148 
00149   // Subscribe to the topics we want.
00150   cloud_sub = handle.subscribe(cloud_input_topic, 
00151                                QUEUE_SIZE, 
00152                                &Segmenter::cloud_cb,
00153                                this);
00154   cam_sub = rgb_transport.subscribeCamera(camera_input_topic, 
00155                                           QUEUE_SIZE,
00156                                           &Segmenter::camera_cb,
00157                                           this);
00158   depth_sub = depth_transport.subscribeCamera(depth_input_topic, 
00159                                               QUEUE_SIZE,
00160                                               &Segmenter::depth_cb,
00161                                               this);
00162   // Advertise our outputs.
00163   map_points_pub = 
00164     handle.advertise<PointCloud>("/map_points", QUEUE_SIZE);
00165   planecloud_pub = 
00166     handle.advertise<PointCloud>(planecloud_output_topic, QUEUE_SIZE);
00167   plane_pub = 
00168     handle.advertise<Plane>(plane_output_topic, QUEUE_SIZE);
00169   object_pub = handle.advertise<Blobs>(object_output_topic, QUEUE_SIZE);
00170   latest_plane_pub_ =
00171     handle.advertise<PointCloud>("latest_planes", 10);
00172   latest_plane_timer_ = handle.createWallTimer(ros::WallDuration(1.0),
00173                                                &Segmenter::publishLatestPlanes,
00174                                                this);
00175 
00176   ros::service::waitForService(handle.resolveName(plane_service_name));
00177   planetracker_srv = 
00178     handle.serviceClient<semanticmodel::PlaneExchange>(plane_service_name);
00179   ROS_INFO_NAMED("segmenter", "Segmenter initialized");
00180 }
00181 
00182 void Segmenter::passthrough(PointCloud::Ptr& in, PointCloud::Ptr& out)
00183 {
00184   pcl16::PassThrough<Point> passfilter;
00185   passfilter.setFilterFieldName("z");
00186   passfilter.setFilterLimits(config.min_z, config.max_z);
00187   passfilter.setInputCloud(in);
00188   passfilter.filter(*out);
00189 }
00190 
00191 bool Segmenter::isHorizontal(const pcl16::ModelCoefficients& coeffs) const
00192 {
00193   return fabs(coeffs.values[2]) >= config.horizontal_dot;
00194 }
00195 
00196 bool Segmenter::isGroundPlane (const pcl16::ModelCoefficients& coeffs) const
00197 {
00198   return (isHorizontal(coeffs) && 
00199           fabs(coeffs.values[3]) < config.ground_plane_threshold); 
00200 }
00201 
00202 pcl16::ModelCoefficients
00203 Segmenter::transformCoeffs (const btTransform& trans,
00204                             const pcl16::ModelCoefficients& in)
00205 {
00206   ROS_ASSERT(in.values.size()==4);
00207   btVector3 normal(in.values[0], in.values[1], in.values[2]); 
00208   btTransform inv = trans.inverse();
00209   const btVector3& origin = inv.getOrigin();
00210   const btQuaternion rot = inv.getRotation();
00211   btVector3 new_normal = inv.getBasis().transpose()*normal;
00212   pcl16::ModelCoefficients trans_coeffs;
00213   trans_coeffs.values.resize(4);
00214   trans_coeffs.values[0] = new_normal.x();
00215   trans_coeffs.values[1] = new_normal.y();
00216   trans_coeffs.values[2] = new_normal.z();
00217   trans_coeffs.values[3] = in.values[3] + normal.dot(origin); 
00218  
00219   /*
00220   ROS_INFO("Transformed coefficients %.4f, %.4f, %.4f, %.4f based on transform "
00221            "(%.4f, %.4f, %.4f), (%.4f, %.4f, %.4f, %.4f).  New coeffs are "
00222            "%.4f, %.4f, %.4f, %.4f", in.values[0], in.values[1], in.values[2],
00223            in.values[3], origin.x(), origin.y(), origin.z(), rot.x(), rot.y(), 
00224            rot.z(), rot.w(), trans_coeffs.values[0], trans_coeffs.values[1], 
00225            trans_coeffs.values[2], trans_coeffs.values[3]);
00226   */
00227                    
00228                    
00229   
00230   return trans_coeffs;
00231 }
00232 
00233 void Segmenter::fit_planes(PointCloud::ConstPtr in,
00234                            NormalCloud::ConstPtr normals,
00235                            const tf::Transform& trans,
00236                            vector<PointCloud::Ptr>& planes,
00237                            vector<pcl16::ModelCoefficients>& good_coeffs,
00238                            vector<pcl16::ModelCoefficients>& bad_coeffs)
00239 {
00240   ROS_DEBUG_NAMED("planes", "In plane callback");
00241   typedef pcl16::PointCloud<pcl16::Normal> NormalCloud;
00242   typedef pcl16::PointCloud<pcl16::Label> LabelCloud;
00243   
00244   // Set up inputs and outputs
00245   vector<pcl16::PlanarRegion<Point> > regions;
00246   vector<pcl16::ModelCoefficients> coeffs;
00247   vector<pcl16::PointIndices> inliers, label_inds, boundary_inds;
00248   LabelCloud::Ptr labels(new LabelCloud());
00249   plane_segmenter_.setInputNormals(normals);
00250   plane_segmenter_.setInputCloud(in);
00251   planes.clear();
00252   good_coeffs.clear();
00253   bad_coeffs.clear();
00254   pcl16::ExtractIndices<Point> extract;
00255   extract.setInputCloud(in);
00256   extract.setNegative(false);
00257   
00258   // Segment
00259   // I originally used the version below, which is better about getting the
00260   // entire plane including the edge.  But it was running into some
00261   // undersegmentation problems.  E.g., the pool table in
00262   // 2011-08-16-20-59-00.bag.  So switched to the other version which seems
00263   // more accurate.  Alex Trevor is supposed to look at this in the next few
00264   // weeks (April 2012).
00265   //plane_segmenter_.segmentAndRefine(regions, coeffs, inliers, labels,
00266   //label_inds, boundary_inds);
00267   std::vector<Eigen::Vector4f, Eigen::aligned_allocator<Eigen::Vector4f> > centroids;
00268   std::vector <Eigen::Matrix3f, Eigen::aligned_allocator<Eigen::Matrix3f> > covariances;
00269   plane_segmenter_.segment(coeffs, inliers, centroids, covariances,
00270                            *labels, label_inds);
00271   ROS_DEBUG_NAMED("planes", "Found %zu segments", regions.size());
00272   
00273   // Divide into good and bad
00274   for (size_t i=0; i<coeffs.size(); i++)
00275   {
00276     pcl16::ModelCoefficients transformed_coeffs = 
00277       transformCoeffs(trans.asBt(), coeffs[i]);
00278 
00279     if (isHorizontal(transformed_coeffs))
00280     {
00281       PointCloud::Ptr plane_cloud(new PointCloud());
00282       PointCloud::Ptr transformed(new PointCloud());
00283       pcl16::PointIndices::Ptr inds(new pcl16::PointIndices(inliers[i]));
00284       extract.setIndices(inds);
00285       extract.filter(*plane_cloud);
00286       pcl16_ros::transformPointCloud(*plane_cloud, *transformed, trans);
00287       transformed->header.frame_id = config.canonical_frame;
00288       
00289       // Correct the z term to deal with numerical issues caused by the 
00290       // interaction of errors in the coefficient estimate getting multiplied
00291       // by the potentially large translation term.  
00292       //
00293       // Note: This depends on our
00294       // assumption that horizontal planes have normal (0,0,1) — it would
00295       // need to be a bit more complicated otherwise.
00296       float sum=0.0;
00297       BOOST_FOREACH (const Point& p, transformed->points)
00298         sum += p.z;
00299       transformed_coeffs.values[0] = 0;
00300       transformed_coeffs.values[1] = 0;
00301       transformed_coeffs.values[2] = 1;
00302       transformed_coeffs.values[3] = -sum/transformed->points.size();
00303       
00304       if (isGroundPlane(transformed_coeffs))
00305       {
00306         //ROS_INFO ("Skipping ground plane");
00307         bad_coeffs.push_back(transformed_coeffs);
00308         continue;
00309       }
00310       planes.push_back(transformed);
00311       good_coeffs.push_back(transformed_coeffs);
00312       /*
00313         ROS_INFO ("Adding plane with z = %.2f in frame %s", transformed_coeffs.values[3],
00314         transformed->header.frame_id.c_str()); 
00315         ROS_INFO ("A point in the plane is %.2f, %.2f, %.2f",
00316         transformed->points[0].x, transformed->points[0].y,
00317         transformed->points[0].z);
00318       */
00319     }
00320     else
00321     {
00322       bad_coeffs.push_back(transformed_coeffs);
00323     }
00324   }
00325   //ROS_WARN("%zu good planes and %zu bad",
00326   //                good_coeffs.size(), bad_coeffs.size());
00327 }
00328 
00329 void Segmenter::convex_hulls(const std::vector<PointCloud::Ptr>& planes,
00330                              std::vector<PointCloud::Ptr>& hulls)
00331 {
00332   pcl16::ConvexHull<Point> hull;
00333   hull.setDimension(2);
00334   foreach(const PointCloud::Ptr& plane, planes)
00335   {
00336     PointCloud::Ptr out(new PointCloud());
00337     hull.setInputCloud(plane);
00338     hull.reconstruct(*out);
00339     hulls.push_back(out);
00340   }
00341 }
00342 
00343 void Segmenter::publish_planes(
00344     const std::vector<PointCloud::Ptr>& planes,
00345     const std::vector<PointCloud::Ptr>& hulls,
00346     const std::vector<pcl16::ModelCoefficients>& coeffs,
00347     std::vector<Plane>& outputs)
00348 {
00349   ROS_ASSERT(planes.size() == hulls.size());
00350   ROS_ASSERT(hulls.size() == coeffs.size());
00351   // Pushing the plane messages is easy; the tricky part is that we also want
00352   // to push a tf frame for best_frame, for the benefit of the head pointer.
00353   // So, we need to know which one is the biggest before we start.
00354   //
00355   // At the moment, we're going to publish really boring names for the planes;
00356   // just the index of each plane.
00357   if (planes.size() == 0)
00358     return;
00359 
00360   size_t largest = planes[0]->points.size();
00361   size_t largest_idx = 0;
00362   for (size_t i = 0 ; i < planes.size() ; ++i)
00363   {
00364     if (planes[i]->points.size() > largest)
00365     {
00366       largest = planes[i]->points.size();
00367       largest_idx = i;
00368     }
00369   }
00370 
00371   // Now we have the index of the best plane; let's publish everything.
00372   PlaneExchange exchange;
00373   exchange.request.in_planes.planes.resize(planes.size());
00374   for (size_t i = 0 ; i < planes.size() ; ++i)
00375   {
00376     Plane planemsg;
00377     planemsg.header = planes[i]->header;
00378 
00379     planemsg.a = coeffs[i].values[0];
00380     planemsg.b = coeffs[i].values[1];
00381     planemsg.c = coeffs[i].values[2];
00382     planemsg.d = coeffs[i].values[3];
00383 
00384     Eigen::Vector4f center = centroid(planes[i]);
00385     planemsg.center.x = center[0];
00386     planemsg.center.y = center[1];
00387     planemsg.center.z = center[2];
00388 
00389     pcl16::toROSMsg(*hulls[i], planemsg.hull);
00390     pcl16::toROSMsg(*planes[i], planemsg.cloud);
00391 
00392     exchange.request.in_planes.planes[i] = planemsg;
00393     
00394     // planemsg is built and stored. Now, build the tf transform.
00395     tf::Transform transform;
00396     transform.setOrigin(tf::Vector3(center[0], center[1], center[2]));
00397     transform.setRotation(tf::Quaternion(0, 0, 1.0, 0.0));
00398     std::string name = (boost::format("plane_%02d") % i).str();
00399     tf::StampedTransform trans(transform, 
00400                                planes[i]->header.stamp,
00401                                config.canonical_frame,
00402                                name);
00403     broadcaster->sendTransform(trans);
00404     
00405     // An extra one for the best current plane.
00406     if (i == largest_idx)
00407     {
00408       tf::StampedTransform trans2(transform, 
00409                                   planes[i]->header.stamp,
00410                                   config.canonical_frame,
00411                                   "best_plane");
00412       broadcaster->sendTransform(trans2);
00413     }
00414   }
00415   // Now, transmit our planes, and get the new hulls back.
00416   planetracker_srv.call(exchange);
00417   outputs = exchange.response.out_planes.planes;
00418 }
00419 
00420 void Segmenter::get_aboves(
00421     const std::vector<PointCloud::Ptr>& hulls,
00422     const std::vector<pcl16::ModelCoefficients>& coeffs,
00423     const PointCloud::Ptr& densecloud,
00424     PointCloud::Ptr& out)
00425 {
00426   out->header = densecloud->header;
00427   foreach(Point& p, *densecloud)
00428   {
00429     // We want to use every point that's "above enough" a plane, but isn't
00430     // _below_ any planes at all. That's not perfect, but it's a start.
00431     bool add = false;
00432     for (size_t i = 0 ; i < hulls.size() ; ++i)
00433     {
00434       // Point-to-plane distance.
00435       double ppdist = coeffs[i].values[0] * p.x + 
00436                       coeffs[i].values[1] * p.y + 
00437                       coeffs[i].values[2] * p.z + 
00438                       coeffs[i].values[3];
00439       if (hulls[i]->points.size()>3 && pcl16::isXYPointIn2DXYPolygon(p, *hulls[i]))
00440       {
00441         if (ppdist > config.plane_distance)
00442         {
00443           add = true;
00444         }
00445         else
00446         {
00447           add = false;
00448           break;
00449         }
00450       }
00451     }
00452     if (add)
00453     {
00454       out->points.push_back(p);
00455     }
00456   }
00457 }
00458 
00459 void Segmenter::lose_bads(const PointCloud::ConstPtr& aboves,
00460                           PointCloud::Ptr good_aboves,
00461                           const vector<pcl16::ModelCoefficients>& bad_coeffs)
00462 {
00463   good_aboves->header = aboves->header;
00464   foreach(const Point& p, *aboves)
00465   {
00466     bool add = true;
00467     foreach(pcl16::ModelCoefficients model, bad_coeffs)
00468     {
00469       double ppdist = model.values[0] * p.x + 
00470                       model.values[1] * p.y + 
00471                       model.values[2] * p.z + 
00472                       model.values[3];
00473       if (fabs(ppdist) <= config.plane_tolerance)  // Fits the plane.
00474       {
00475         add = false;
00476         break;
00477       }
00478     }
00479     if (add)
00480       good_aboves->points.push_back(p);
00481   }
00482 }
00483 
00484 void Segmenter::publishLatestPlanes (const ros::WallTimerEvent& e)
00485 {
00486   static size_t i=0;
00487   boost::mutex::scoped_lock lock(config_mutex);
00488 
00489   if (latest_planes_.size()>0) 
00490   {
00491     i = (i+1)%latest_planes_.size();
00492     latest_plane_pub_.publish(*latest_planes_[i]);
00493   }
00494 }
00495 
00496 void Segmenter::synchronized_pipeline_callback(
00497     const sensor_msgs::ImageConstPtr& rgb_img,
00498     const sensor_msgs::CameraInfoConstPtr& rgb_info,
00499     const sensor_msgs::ImageConstPtr& depth_img,
00500     const sensor_msgs::CameraInfoConstPtr& depth_info,
00501     const PointCloud::ConstPtr& cloud)
00502 {
00503   // No changes during a frame, please.
00504   boost::mutex::scoped_lock lock(config_mutex);
00505 
00506   if (processed + skipped > 0)
00507   {
00508     ROS_DEBUG_NAMED("score", 
00509                     "Processed %d frames. Skipped %d frames. (%f processed)",
00510                     processed, 
00511                     skipped, 
00512                     ((float)processed) / (processed + skipped));
00513   }
00514   
00515   ROS_DEBUG_STREAM_NAMED("timer",
00516                          "We are behind by " 
00517                          << ros::Time::now() - rgb_img->header.stamp);
00518   // Avoid falling too far back
00519   if (rgb_img->header.stamp + ros::Duration(config.filter_lag_cutoff) < 
00520       ros::Time::now())
00521   {
00522     ROS_WARN_STREAM_NAMED("timer",
00523                           "Too old! (" << 
00524                           ros::Time::now() - rgb_img->header.stamp << ")");
00525     skipped++;
00526     return;
00527   }
00528   else
00529   {
00530     ROS_DEBUG_STREAM_NAMED(
00531         "filter", 
00532         "Processing filter input at " << rgb_img->header.stamp);
00533     processed++;
00534   }
00535   
00536   
00537   // Get the transform between the camera and canonical frames
00538   tf::StampedTransform trans;
00539   bool found = 
00540     listener->waitForTransform(config.canonical_frame, cloud->header.frame_id,
00541                                cloud->header.stamp, ros::Duration(1.0));
00542   try 
00543   {
00544     if (found)
00545       listener->lookupTransform(config.canonical_frame, cloud->header.frame_id,
00546                                 cloud->header.stamp, trans);
00547   }
00548   catch (tf::TransformException& e)
00549   {
00550     found = false;
00551   }
00552   if (!found)
00553   {
00554     ROS_WARN_STREAM("Couldn't find transform between " << 
00555                     cloud->header.frame_id << 
00556                     " and " << 
00557                     config.canonical_frame << 
00558                     " at " 
00559                     <<
00560                     cloud->header.stamp << "; skipping");
00561     return;
00562   }
00563   PointCloud::Ptr transformed(new PointCloud()); 
00564   pcl16_ros::transformPointCloud(*cloud, *transformed, trans);
00565   transformed->header.frame_id = config.canonical_frame;
00566 //  btVector3 origin = trans.getOrigin().asBt();
00567 //  btQuaternion rot = trans.getRotation().asBt();
00568   //ROS_INFO ("Transform is (%.4f, %.4f, %.4f), (%.4f, %.4f, %.4f, %.4f)",
00569   //         origin.x(), origin.y(), origin.z(), rot.x(), rot.y(), rot.z(), rot.w());
00570   
00571   // Step 0: estimate normals (for untransformed cloud)
00572   NormalCloud::Ptr normals(new NormalCloud());
00573   normal_estimator_.setInputCloud(cloud);
00574   normal_estimator_.compute(*normals);
00575   
00576   // Step 1: find horizontal planes 
00577   // Inputs are untransformed, outputs are transformed
00578   std::vector<PointCloud::Ptr> good_planes;
00579   std::vector<pcl16::ModelCoefficients> good_coeffs, bad_coeffs;
00580   float* distance_map = normal_estimator_.getDistanceMap();
00581   comp_->setDistanceMap(distance_map); // Needed by fit_planes
00582   fit_planes(cloud, normals, trans, good_planes, good_coeffs, bad_coeffs);
00583 
00584   // Step 3. Convex hulls. We need to keep the plane points, so we can't
00585   // clobber the planes variable.
00586   std::vector<PointCloud::Ptr> hulls;
00587   convex_hulls(good_planes, hulls); 
00588   if (!ros::ok())
00589     return;
00590 
00591   // Step 4. Send our planes to the tracker, and get back the new hulls.
00592   std::vector<Plane> returnedplanes;
00593   publish_planes(good_planes, hulls, good_coeffs, returnedplanes);
00594   BOOST_FOREACH (PointCloud::Ptr plane, good_planes) 
00595     latest_planes_.push_back(plane);
00596   if (short_circuit || !ros::ok())
00597     return;
00598 
00599   // Parse the newplanes out.
00600   hulls.clear();
00601   good_coeffs.clear();
00602   foreach(Plane& p, returnedplanes)
00603   {
00604     hulls.push_back(PointCloud::Ptr(new PointCloud()));
00605     pcl16::fromROSMsg(p.hull, *hulls.back());
00606     good_coeffs.push_back(pcl16::ModelCoefficients());
00607     good_coeffs.back().values.resize(4);
00608     good_coeffs.back().values[0] = p.a;
00609     good_coeffs.back().values[1] = p.b;
00610     good_coeffs.back().values[2] = p.c;
00611     good_coeffs.back().values[3] = p.d;
00612   }
00613 
00614   // Step 5: we go back to working in the dense plane, here.
00615   map_points_pub.publish(transformed);
00616   if (transformed->points.size() == 0 || !ros::ok())
00617   {
00618     ROS_ERROR("No transformed points in filter");
00619     return;
00620   }
00621 
00622   PointCloud::Ptr aboves(new PointCloud());
00623   get_aboves(hulls, good_coeffs, transformed, aboves);
00624   if (aboves->points.size() == 0 || !ros::ok())
00625   {
00626     ROS_DEBUG_NAMED("aboves", "No above points; skipping");
00627     return;
00628   }
00629 
00630   // Step 5.5. Given that we have points that are "above enough" the
00631   // horizontal planes in the scene, we now want to throw out those that are
00632   // too good of a fit to the "bad" (non-horizontal) planes. The idea here is
00633   // to get rid of points on walls.
00634   PointCloud::Ptr good_aboves(new PointCloud());
00635   lose_bads(aboves, good_aboves, bad_coeffs);
00636 
00637   aboves.reset();
00638 
00639   if (good_aboves->points.size() == 0 || !ros::ok())
00640   {
00641     ROS_WARN("No good above points; skipping.");
00642     return;
00643   }
00644 
00645   /*
00646    * NEW VERSION.
00647    * Doing the extra object processing is expensive. Rather than do all of it,
00648    * we punt: send the simple aboves over the wire, and have the object
00649    * tracker keep it all together. We'd do lose_bads too, but it requires the
00650    * bad_coeffs variable, which I don't want to (also) ship over the wire.
00651    */
00652   Blobs msg;
00653   msg.blobs.resize(1);  // No clustering on this end.
00654   msg.rgb_info = *rgb_info;
00655   msg.rgb_image = *rgb_img;
00656   msg.depth_info = *depth_info;
00657   msg.depth_image = *depth_img;
00658   pcl16::toROSMsg(*good_aboves, msg.blobs.at(0));
00659   object_pub.publish(msg);
00660 }
00661 
00662 void Segmenter::dynamic_reconfigure_callback(const FilterConfig& config, 
00663                                              uint32_t level)
00664 {
00665   // We ignore level, because everybody does.
00666   boost::mutex::scoped_lock lock(config_mutex);
00667   this->config = config;  // Well, that was easy.
00668 }
00669 
00670 // These next two functions just add stuff to sync's queue; it handles the
00671 // rest.
00672 void Segmenter::cloud_cb(const PointCloud::ConstPtr& cloud)
00673 {
00674   sync->add<4>(cloud); 
00675 }
00676 
00677 void Segmenter::camera_cb(const sensor_msgs::ImageConstPtr& img, 
00678                           const sensor_msgs::CameraInfoConstPtr& info)
00679 {
00680   sync->add<0>(img);
00681   sync->add<1>(info);
00682 }
00683 
00684 void Segmenter::depth_cb(const sensor_msgs::ImageConstPtr& img, 
00685                          const sensor_msgs::CameraInfoConstPtr& info)
00686 {
00687   sync->add<2>(img);
00688   sync->add<3>(info);
00689 }
00690 
00691 }  // namespace semanticmodel


semanticmodel
Author(s): Julian ("Mac") Mason
autogenerated on Thu Dec 12 2013 12:39:10