$search
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