00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 #include <boost/thread/thread.hpp>
00041 #include <pcl/apps/timer.h>
00042 #include <pcl/common/common.h>
00043 #include <pcl/common/angles.h>
00044 #include <pcl/io/openni_grabber.h>
00045 #include <pcl/io/pcd_io.h>
00046 #include <pcl/search/organized.h>
00047 #include <pcl/features/integral_image_normal.h>
00048 #include <pcl/filters/extract_indices.h>
00049 #include <pcl/segmentation/organized_multi_plane_segmentation.h>
00050 #include <pcl/segmentation/sac_segmentation.h>
00051 #include <pcl/segmentation/extract_polygonal_prism_data.h>
00052 #include <pcl/sample_consensus/sac_model_plane.h>
00053
00054 #include <pcl/surface/convex_hull.h>
00055 #include <pcl/visualization/point_cloud_handlers.h>
00056 #include <pcl/visualization/pcl_visualizer.h>
00057 #include <pcl/visualization/image_viewer.h>
00058 #include <pcl/console/print.h>
00059 #include <pcl/console/parse.h>
00060 #include <pcl/segmentation/euclidean_cluster_comparator.h>
00061 #include <pcl/segmentation/organized_connected_component_segmentation.h>
00062 #include <pcl/segmentation/edge_aware_plane_comparator.h>
00063 #include <pcl/geometry/polygon_operations.h>
00064
00065 using namespace pcl;
00066 using namespace std;
00067
00068 typedef PointXYZRGBA PointT;
00069
00070 #define SHOW_FPS 1
00071
00073 class NILinemod
00074 {
00075 public:
00076 typedef PointCloud<PointT> Cloud;
00077 typedef Cloud::Ptr CloudPtr;
00078 typedef Cloud::ConstPtr CloudConstPtr;
00079 bool added;
00080
00081 NILinemod (Grabber& grabber)
00082 : cloud_viewer_ ("PointCloud")
00083 , grabber_ (grabber)
00084 , image_viewer_ ("Image")
00085 , first_frame_ (true)
00086 {
00087 added = false;
00088
00089
00090 ne_.setNormalEstimationMethod (ne_.COVARIANCE_MATRIX);
00091 ne_.setMaxDepthChangeFactor (0.02f);
00092 ne_.setNormalSmoothingSize (20.0f);
00093
00094
00095 plane_comparator_.reset (new EdgeAwarePlaneComparator<PointT, Normal>);
00096 plane_comparator_->setDistanceThreshold (0.01f, false);
00097 mps_.setMinInliers (5000);
00098 mps_.setAngularThreshold (pcl::deg2rad (3.0));
00099 mps_.setDistanceThreshold (0.02);
00100 mps_.setMaximumCurvature (0.001);
00101 mps_.setProjectPoints (true);
00102 mps_.setComparator (plane_comparator_);
00103 }
00104
00106 void
00107 cloud_callback (const CloudConstPtr& cloud)
00108 {
00109 FPS_CALC ("cloud callback");
00110 boost::mutex::scoped_lock lock (cloud_mutex_);
00111 cloud_ = cloud;
00112 search_.setInputCloud (cloud);
00113
00114
00115
00116
00117
00118 if (!first_frame_)
00119 {
00120 if (!plane_indices_ || plane_indices_->indices.empty () || !search_.getInputCloud ())
00121 {
00122 PCL_ERROR ("Lost tracking. Select the object again to continue.\n");
00123 first_frame_ = true;
00124 return;
00125 }
00126 SACSegmentation<PointT> seg;
00127 seg.setOptimizeCoefficients (true);
00128 seg.setModelType (SACMODEL_PLANE);
00129 seg.setMethodType (SAC_RANSAC);
00130 seg.setMaxIterations (1000);
00131 seg.setDistanceThreshold (0.01);
00132 seg.setInputCloud (search_.getInputCloud ());
00133 seg.setIndices (plane_indices_);
00134 ModelCoefficients coefficients;
00135 PointIndices inliers;
00136 seg.segment (inliers, coefficients);
00137
00138 if (inliers.indices.empty ())
00139 {
00140 PCL_ERROR ("No planar model found. Select the object again to continue.\n");
00141 first_frame_ = true;
00142 return;
00143 }
00144
00145
00146 CloudPtr plane_inliers (new Cloud);
00147 pcl::copyPointCloud (*search_.getInputCloud (), inliers.indices, *plane_inliers);
00148 if (plane_inliers->points.empty ())
00149 {
00150 PCL_ERROR ("No planar model found. Select the object again to continue.\n");
00151 first_frame_ = true;
00152 return;
00153 }
00154 else
00155 {
00156 plane_.reset (new Cloud);
00157
00158
00159 ConvexHull<PointT> chull;
00160 chull.setDimension (2);
00161 chull.setInputCloud (plane_inliers);
00162 chull.reconstruct (*plane_);
00163 }
00164 }
00165 }
00166
00168 CloudConstPtr
00169 getLatestCloud ()
00170 {
00171
00172 boost::mutex::scoped_lock lock (cloud_mutex_);
00173 CloudConstPtr temp_cloud;
00174 temp_cloud.swap (cloud_);
00175 return (temp_cloud);
00176 }
00177
00179 void
00180 keyboard_callback (const visualization::KeyboardEvent&, void*)
00181 {
00182
00183
00184
00185
00186
00187
00188
00189
00190 }
00191
00193 void
00194 mouse_callback (const visualization::MouseEvent& mouse_event, void*)
00195 {
00196
00197
00198
00199
00200 }
00201
00203
00212 void
00213 segmentObject (int picked_idx,
00214 const CloudConstPtr &cloud,
00215 const PointIndices::Ptr &plane_indices,
00216 const PointIndices::Ptr &plane_boundary_indices,
00217 Cloud &object)
00218 {
00219 CloudPtr plane_hull (new Cloud);
00220
00221
00222 ConvexHull<PointT> chull;
00223 chull.setDimension (2);
00224 chull.setInputCloud (cloud);
00225 chull.setIndices (plane_boundary_indices);
00226 chull.reconstruct (*plane_hull);
00227
00228
00229 PointIndices::Ptr everything_but_the_plane (new PointIndices);
00230 if (indices_fullset_.size () != cloud->points.size ())
00231 {
00232 indices_fullset_.resize (cloud->points.size ());
00233 for (int p_it = 0; p_it < static_cast<int> (indices_fullset_.size ()); ++p_it)
00234 indices_fullset_[p_it] = p_it;
00235 }
00236 std::vector<int> indices_subset = plane_indices->indices;
00237 std::sort (indices_subset.begin (), indices_subset.end ());
00238 set_difference (indices_fullset_.begin (), indices_fullset_.end (),
00239 indices_subset.begin (), indices_subset.end (),
00240 inserter (everything_but_the_plane->indices, everything_but_the_plane->indices.begin ()));
00241
00242
00243 PointIndices::Ptr points_above_plane (new PointIndices);
00244 ExtractPolygonalPrismData<PointT> exppd;
00245 exppd.setInputCloud (cloud);
00246 exppd.setInputPlanarHull (plane_hull);
00247 exppd.setIndices (everything_but_the_plane);
00248 exppd.setHeightLimits (0.0, 0.5);
00249 exppd.segment (*points_above_plane);
00250
00251
00252 EuclideanClusterComparator<PointT, Normal, Label>::Ptr euclidean_cluster_comparator (new EuclideanClusterComparator<PointT, Normal, Label>);
00253 euclidean_cluster_comparator->setInputCloud (cloud);
00254 euclidean_cluster_comparator->setDistanceThreshold (0.03f, false);
00255
00256 Label l; l.label = 0;
00257 PointCloud<Label>::Ptr scene (new PointCloud<Label> (cloud->width, cloud->height, l));
00258
00259 for (int i = 0; i < static_cast<int> (points_above_plane->indices.size ()); ++i)
00260 scene->points[points_above_plane->indices[i]].label = 1;
00261 euclidean_cluster_comparator->setLabels (scene);
00262
00263 vector<bool> exclude_labels (2); exclude_labels[0] = true; exclude_labels[1] = false;
00264 euclidean_cluster_comparator->setExcludeLabels (exclude_labels);
00265
00266 OrganizedConnectedComponentSegmentation<PointT, Label> euclidean_segmentation (euclidean_cluster_comparator);
00267 euclidean_segmentation.setInputCloud (cloud);
00268
00269 PointCloud<Label> euclidean_labels;
00270 vector<PointIndices> euclidean_label_indices;
00271 euclidean_segmentation.segment (euclidean_labels, euclidean_label_indices);
00272
00273
00274 bool cluster_found = false;
00275 for (size_t i = 0; i < euclidean_label_indices.size (); i++)
00276 {
00277 if (cluster_found)
00278 break;
00279
00280 for (size_t j = 0; j < euclidean_label_indices[i].indices.size (); ++j)
00281 {
00282 if (picked_idx != euclidean_label_indices[i].indices[j])
00283 continue;
00284
00285 pcl::copyPointCloud (*cloud, euclidean_label_indices[i].indices, object);
00286 cluster_found = true;
00287 break;
00288
00289
00290 }
00291 }
00292 }
00293
00294
00296 void
00297 segment (const PointT &picked_point,
00298 int picked_idx,
00299 PlanarRegion<PointT> ®ion,
00300 PointIndices &indices,
00301 CloudPtr &object)
00302 {
00303
00304 if (!first_frame_)
00305 return;
00306
00307
00308 PointCloud<Normal>::Ptr normal_cloud (new PointCloud<Normal>);
00309 ne_.setInputCloud (search_.getInputCloud ());
00310 ne_.compute (*normal_cloud);
00311
00312 plane_comparator_->setDistanceMap (ne_.getDistanceMap ());
00313
00314
00315 mps_.setInputNormals (normal_cloud);
00316 mps_.setInputCloud (search_.getInputCloud ());
00317
00318
00319 vector<PlanarRegion<PointT>, Eigen::aligned_allocator<PlanarRegion<PointT> > > regions;
00320 vector<ModelCoefficients> model_coefficients;
00321 vector<PointIndices> inlier_indices;
00322 PointCloud<Label>::Ptr labels (new PointCloud<Label>);
00323 vector<PointIndices> label_indices;
00324 vector<PointIndices> boundary_indices;
00325 mps_.segmentAndRefine (regions, model_coefficients, inlier_indices, labels, label_indices, boundary_indices);
00326 PCL_DEBUG ("Number of planar regions detected: %zu for a cloud of %zu points and %zu normals.\n", regions.size (), search_.getInputCloud ()->points.size (), normal_cloud->points.size ());
00327
00328 double max_dist = numeric_limits<double>::max ();
00329
00330 int idx = -1;
00331 for (size_t i = 0; i < regions.size (); ++i)
00332 {
00333 double dist = pointToPlaneDistance (picked_point, regions[i].getCoefficients ());
00334 if (dist < max_dist)
00335 {
00336 max_dist = dist;
00337 idx = static_cast<int> (i);
00338 }
00339 }
00340
00341 PointIndices::Ptr plane_boundary_indices;
00342
00343 if (idx != -1)
00344 {
00345 region = regions[idx];
00346 plane_indices_.reset (new PointIndices (inlier_indices[idx]));
00347 plane_boundary_indices.reset (new PointIndices (boundary_indices[idx]));
00348 }
00349
00350
00351 if (plane_boundary_indices && !plane_boundary_indices->indices.empty ())
00352 {
00353 object.reset (new Cloud);
00354 segmentObject (picked_idx, search_.getInputCloud (), plane_indices_, plane_boundary_indices, *object);
00355
00356
00357
00358 }
00359 first_frame_ = false;
00360 }
00361
00363
00368 void
00369 pp_callback (const visualization::PointPickingEvent& event, void*)
00370 {
00371
00372 int idx = event.getPointIndex ();
00373 if (idx == -1)
00374 return;
00375
00376 vector<int> indices (1);
00377 vector<float> distances (1);
00378
00379
00380 boost::mutex::scoped_lock lock1 (cloud_mutex_);
00381
00382
00383 PointT picked_pt;
00384 event.getPoint (picked_pt.x, picked_pt.y, picked_pt.z);
00385
00386
00387 stringstream ss;
00388 ss << "sphere_" << idx;
00389 cloud_viewer_.addSphere (picked_pt, 0.01, 1.0, 0.0, 0.0, ss.str ());
00390
00391
00392 if (!search_.isValid ())
00393 return;
00394
00395
00396 search_.nearestKSearch (picked_pt, 1, indices, distances);
00397
00398
00399 uint32_t width = search_.getInputCloud ()->width,
00400 height = search_.getInputCloud ()->height;
00401 int v = height - indices[0] / width,
00402 u = indices[0] % width;
00403
00404
00405 image_viewer_.addCircle (u, v, 5, 1.0, 0.0, 0.0, "circles", 1.0);
00406 image_viewer_.addFilledRectangle (u-5, u+5, v-5, v+5, 0.0, 1.0, 0.0, "boxes", 0.5);
00407 image_viewer_.markPoint (u, v, visualization::red_color, visualization::blue_color, 10);
00408
00409
00410
00411
00412 PlanarRegion<PointT> region;
00413 CloudPtr object;
00414 PointIndices region_indices;
00415 segment (picked_pt, indices[0], region, region_indices, object);
00416
00417
00418 if (region.getContour ().empty ())
00419 {
00420 PCL_ERROR ("No planar region detected. Please select another point or relax the thresholds and continue.\n");
00421 return;
00422 }
00423
00424 else
00425 {
00426
00427
00428
00429 PlanarRegion<PointT> refined_region;
00430 pcl::approximatePolygon (region, refined_region, 0.01, false, true);
00431 PCL_INFO ("Planar region: %zu points initial, %zu points after refinement.\n", region.getContour ().size (), refined_region.getContour ().size ());
00432 cloud_viewer_.addPolygon (refined_region, 0.0, 0.0, 1.0, "refined_region");
00433 cloud_viewer_.setShapeRenderingProperties (visualization::PCL_VISUALIZER_LINE_WIDTH, 10, "refined_region");
00434
00435
00436 image_viewer_.addPlanarPolygon (search_.getInputCloud (), refined_region, 0.0, 0.0, 1.0, "refined_region", 1.0);
00437 }
00438
00439
00440 if (!object)
00441 {
00442 PCL_ERROR ("No object detected. Please select another point or relax the thresholds and continue.\n");
00443 return;
00444 }
00445 else
00446 {
00447
00448 visualization::PointCloudColorHandlerCustom<PointT> red (object, 255, 0, 0);
00449 if (!cloud_viewer_.updatePointCloud (object, red, "object"))
00450 cloud_viewer_.addPointCloud (object, red, "object");
00451
00452 image_viewer_.removeLayer ("object");
00453 image_viewer_.addMask (search_.getInputCloud (), *object, "object");
00454
00455
00456 PointT min_pt, max_pt;
00457 getMinMax3D (*object, min_pt, max_pt);
00458 stringstream ss;
00459 ss << "cube_" << idx;
00460
00461 cloud_viewer_.addCube (min_pt.x, max_pt.x, min_pt.y, max_pt.y, min_pt.z, max_pt.z, 0.0, 1.0, 0.0, ss.str ());
00462 cloud_viewer_.setShapeRenderingProperties (visualization::PCL_VISUALIZER_LINE_WIDTH, 10, ss.str ());
00463
00464
00465 image_viewer_.addRectangle (search_.getInputCloud (), *object);
00466 }
00467 }
00468
00470 void
00471 init ()
00472 {
00473 cloud_viewer_.registerMouseCallback (&NILinemod::mouse_callback, *this);
00474 cloud_viewer_.registerKeyboardCallback(&NILinemod::keyboard_callback, *this);
00475 cloud_viewer_.registerPointPickingCallback (&NILinemod::pp_callback, *this);
00476 boost::function<void (const CloudConstPtr&) > cloud_cb = boost::bind (&NILinemod::cloud_callback, this, _1);
00477 cloud_connection = grabber_.registerCallback (cloud_cb);
00478
00479 image_viewer_.registerMouseCallback (&NILinemod::mouse_callback, *this);
00480 image_viewer_.registerKeyboardCallback(&NILinemod::keyboard_callback, *this);
00481 }
00482
00484 void
00485 run ()
00486 {
00487 grabber_.start ();
00488
00489 bool image_init = false, cloud_init = false;
00490 unsigned char* rgb_data = 0;
00491 unsigned rgb_data_size = 0;
00492
00493 while (!cloud_viewer_.wasStopped () && !image_viewer_.wasStopped ())
00494 {
00495 if (cloud_)
00496 {
00497 CloudConstPtr cloud = getLatestCloud ();
00498 if (!cloud_init)
00499 {
00500 cloud_viewer_.setPosition (0, 0);
00501 cloud_viewer_.setSize (cloud->width, cloud->height);
00502 cloud_init = !cloud_init;
00503 }
00504
00505 if (!cloud_viewer_.updatePointCloud (cloud, "OpenNICloud"))
00506 {
00507 cloud_viewer_.addPointCloud (cloud, "OpenNICloud");
00508 cloud_viewer_.resetCameraViewpoint ("OpenNICloud");
00509 }
00510
00511 if (!image_init)
00512 {
00513 image_viewer_.setPosition (cloud->width, 0);
00514 image_viewer_.setSize (cloud->width, cloud->height);
00515 image_init = !image_init;
00516 }
00517
00518 image_viewer_.showRGBImage<PointT> (cloud);
00519 }
00520
00521
00522
00523 CloudPtr plane (new Cloud);
00524 if (plane_)
00525 *plane = *plane_;
00526 visualization::PointCloudColorHandlerCustom<PointT> blue (plane, 0, 255, 0);
00527 if (!cloud_viewer_.updatePointCloud (plane, blue, "plane"))
00528 cloud_viewer_.addPointCloud (plane, "plane");
00529 cloud_viewer_.spinOnce ();
00530
00531 image_viewer_.spinOnce ();
00532 boost::this_thread::sleep (boost::posix_time::microseconds (100));
00533 }
00534
00535 grabber_.stop ();
00536
00537 cloud_connection.disconnect ();
00538 }
00539
00540 visualization::PCLVisualizer cloud_viewer_;
00541 Grabber& grabber_;
00542 boost::mutex cloud_mutex_;
00543 CloudConstPtr cloud_;
00544
00545 visualization::ImageViewer image_viewer_;
00546
00547 search::OrganizedNeighbor<PointT> search_;
00548 private:
00549 boost::signals2::connection cloud_connection, image_connection;
00550 bool first_frame_;
00551
00552
00553 std::vector<int> indices_fullset_;
00554 PointIndices::Ptr plane_indices_;
00555 CloudPtr plane_;
00556 IntegralImageNormalEstimation<PointT, Normal> ne_;
00557 OrganizedMultiPlaneSegmentation<PointT, Normal, Label> mps_;
00558 EdgeAwarePlaneComparator<PointT, Normal>::Ptr plane_comparator_;
00559 };
00560
00561
00562 int
00563 main (int, char**)
00564 {
00565 string device_id ("#1");
00566 OpenNIGrabber grabber (device_id);
00567 NILinemod openni_viewer (grabber);
00568
00569 openni_viewer.init ();
00570 openni_viewer.run ();
00571
00572 return (0);
00573 }
00574