Extracting indices from a PointCloud
In this tutorial we will learn how to use an ExtractIndices filter to extract a subset of points from a point cloud based on the indices output by a segmentation algorithm. In order to not complicate the tutorial, the segmentation algorithm is not explained here. Please check the Plane model segmentation tutorial for more information.
The code
First, download the dataset table_scene_lms400.pcd and save it somewhere to disk.
Then, create a file, let’s say, extract_indices.cpp
in your favorite
editor, and place the following inside it:
1#include <iostream>
2#include <pcl/ModelCoefficients.h>
3#include <pcl/io/pcd_io.h>
4#include <pcl/point_types.h>
5#include <pcl/sample_consensus/method_types.h>
6#include <pcl/sample_consensus/model_types.h>
7#include <pcl/segmentation/sac_segmentation.h>
8#include <pcl/filters/voxel_grid.h>
9#include <pcl/filters/extract_indices.h>
10
11int
12main ()
13{
14 pcl::PCLPointCloud2::Ptr cloud_blob (new pcl::PCLPointCloud2), cloud_filtered_blob (new pcl::PCLPointCloud2);
15 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>), cloud_p (new pcl::PointCloud<pcl::PointXYZ>), cloud_f (new pcl::PointCloud<pcl::PointXYZ>);
16
17 // Fill in the cloud data
18 pcl::PCDReader reader;
19 reader.read ("table_scene_lms400.pcd", *cloud_blob);
20
21 std::cerr << "PointCloud before filtering: " << cloud_blob->width * cloud_blob->height << " data points." << std::endl;
22
23 // Create the filtering object: downsample the dataset using a leaf size of 1cm
24 pcl::VoxelGrid<pcl::PCLPointCloud2> sor;
25 sor.setInputCloud (cloud_blob);
26 sor.setLeafSize (0.01f, 0.01f, 0.01f);
27 sor.filter (*cloud_filtered_blob);
28
29 // Convert to the templated PointCloud
30 pcl::fromPCLPointCloud2 (*cloud_filtered_blob, *cloud_filtered);
31
32 std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height << " data points." << std::endl;
33
34 // Write the downsampled version to disk
35 pcl::PCDWriter writer;
36 writer.write<pcl::PointXYZ> ("table_scene_lms400_downsampled.pcd", *cloud_filtered, false);
37
38 pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients ());
39 pcl::PointIndices::Ptr inliers (new pcl::PointIndices ());
40 // Create the segmentation object
41 pcl::SACSegmentation<pcl::PointXYZ> seg;
42 // Optional
43 seg.setOptimizeCoefficients (true);
44 // Mandatory
45 seg.setModelType (pcl::SACMODEL_PLANE);
46 seg.setMethodType (pcl::SAC_RANSAC);
47 seg.setMaxIterations (1000);
48 seg.setDistanceThreshold (0.01);
49
50 // Create the filtering object
51 pcl::ExtractIndices<pcl::PointXYZ> extract;
52
53 int i = 0, nr_points = (int) cloud_filtered->size ();
54 // While 30% of the original cloud is still there
55 while (cloud_filtered->size () > 0.3 * nr_points)
56 {
57 // Segment the largest planar component from the remaining cloud
58 seg.setInputCloud (cloud_filtered);
59 seg.segment (*inliers, *coefficients);
60 if (inliers->indices.size () == 0)
61 {
62 std::cerr << "Could not estimate a planar model for the given dataset." << std::endl;
63 break;
64 }
65
66 // Extract the inliers
67 extract.setInputCloud (cloud_filtered);
68 extract.setIndices (inliers);
69 extract.setNegative (false);
70 extract.filter (*cloud_p);
71 std::cerr << "PointCloud representing the planar component: " << cloud_p->width * cloud_p->height << " data points." << std::endl;
72
73 std::stringstream ss;
74 ss << "table_scene_lms400_plane_" << i << ".pcd";
75 writer.write<pcl::PointXYZ> (ss.str (), *cloud_p, false);
76
77 // Create the filtering object
78 extract.setNegative (true);
79 extract.filter (*cloud_f);
80 cloud_filtered.swap (cloud_f);
81 i++;
82 }
83
84 return (0);
85}
The explanation
Now, let’s break down the code piece by piece, skipping the obvious.
After the data has been loaded from the input .PCD file, we create a VoxelGrid filter, to downsample the data. The rationale behind data downsampling here is just to speed things up – less points means less time needed to spend within the segmentation loop.
pcl::VoxelGrid<pcl::PCLPointCloud2> sor;
sor.setInputCloud (cloud_blob);
sor.setLeafSize (0.01f, 0.01f, 0.01f);
sor.filter (*cloud_filtered_blob);
The next block of code deals with the parametric segmentation. To keep the tutorial simple, its explanation will be skipped for now. Please see the segmentation tutorials (in particular Plane model segmentation) for more information.
pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients ());
pcl::PointIndices::Ptr inliers (new pcl::PointIndices ());
// Create the segmentation object
pcl::SACSegmentation<pcl::PointXYZ> seg;
// Optional
seg.setOptimizeCoefficients (true);
// Mandatory
seg.setModelType (pcl::SACMODEL_PLANE);
seg.setMethodType (pcl::SAC_RANSAC);
seg.setMaxIterations (1000);
seg.setDistanceThreshold (0.01);
The line
pcl::ExtractIndices<pcl::PointXYZ> extract;
and
extract.setInputCloud (cloud_filtered);
extract.setIndices (inliers);
extract.setNegative (false);
extract.filter (*cloud_p);
represent the actual indices extraction filter. To process multiple models, we run the process in a loop, and after each model is extracted, we go back to obtain the remaining points, and iterate. The inliers are obtained from the segmentation process, as follows:
seg.setInputCloud (cloud_filtered);
seg.segment (*inliers, *coefficients);
Compiling and running the program
Add the following lines to your CMakeLists.txt file:
1cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
2
3project(extract_indices)
4
5find_package(PCL 1.2 REQUIRED)
6
7include_directories(${PCL_INCLUDE_DIRS})
8link_directories(${PCL_LIBRARY_DIRS})
9add_definitions(${PCL_DEFINITIONS})
10
11add_executable (extract_indices extract_indices.cpp)
12target_link_libraries (extract_indices ${PCL_LIBRARIES})
After you have made the executable, you can run it. Simply do:
$ ./extract_indices
You will see something similar to:
PointCloud before filtering: 460400 data points.
PointCloud after filtering: 41049 data points.
PointCloud representing the planar component: 20164 data points.
PointCloud representing the planar component: 12129 data points.