Removing outliers using a Conditional or RadiusOutlier removal
This document demonstrates how to remove outliers from a PointCloud using several different methods in the filter module. First we will look at how to use a ConditionalRemoval filter which removes all indices in the given input cloud that do not satisfy one or more given conditions. Then we will learn how to us a RadiusOutlierRemoval filter which removes all indices in its input cloud that don’t have at least some number of neighbors within a certain range.
The code
First, create a file, let’s say, remove_outliers.cpp
in your favorite
editor, and place the following inside it:
1#include <iostream>
2#include <pcl/point_types.h>
3#include <pcl/filters/radius_outlier_removal.h>
4#include <pcl/filters/conditional_removal.h>
5
6int
7 main (int argc, char** argv)
8{
9 if (argc != 2)
10 {
11 std::cerr << "please specify command line arg '-r' or '-c'" << std::endl;
12 exit(0);
13 }
14 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
15 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
16
17 // Fill in the cloud data
18 cloud->width = 5;
19 cloud->height = 1;
20 cloud->resize (cloud->width * cloud->height);
21
22 for (auto& point: *cloud)
23 {
24 point.x = 1024 * rand () / (RAND_MAX + 1.0f);
25 point.y = 1024 * rand () / (RAND_MAX + 1.0f);
26 point.z = 1024 * rand () / (RAND_MAX + 1.0f);
27 }
28
29 if (strcmp(argv[1], "-r") == 0){
30 pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
31 // build the filter
32 outrem.setInputCloud(cloud);
33 outrem.setRadiusSearch(0.8);
34 outrem.setMinNeighborsInRadius (2);
35 outrem.setKeepOrganized(true);
36 // apply filter
37 outrem.filter (*cloud_filtered);
38 }
39 else if (strcmp(argv[1], "-c") == 0){
40 // build the condition
41 pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond (new
42 pcl::ConditionAnd<pcl::PointXYZ> ());
43 range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new
44 pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::GT, 0.0)));
45 range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new
46 pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::LT, 0.8)));
47 // build the filter
48 pcl::ConditionalRemoval<pcl::PointXYZ> condrem;
49 condrem.setCondition (range_cond);
50 condrem.setInputCloud (cloud);
51 condrem.setKeepOrganized(true);
52 // apply filter
53 condrem.filter (*cloud_filtered);
54 }
55 else{
56 std::cerr << "please specify command line arg '-r' or '-c'" << std::endl;
57 exit(0);
58 }
59 std::cerr << "Cloud before filtering: " << std::endl;
60 for (const auto& point: *cloud)
61 std::cerr << " " << point.x << " "
62 << point.y << " "
63 << point.z << std::endl;
64 // display pointcloud after filtering
65 std::cerr << "Cloud after filtering: " << std::endl;
66 for (const auto& point: *cloud_filtered)
67 std::cerr << " " << point.x << " "
68 << point.y << " "
69 << point.z << std::endl;
70 return (0);
71}
RadiusOutlierRemoval Background
The picture below helps to visualize what the RadiusOutlierRemoval filter object does. The user specifies a number of neighbors which every index must have within a specified radius to remain in the PointCloud. For example if 1 neighbor is specified, only the yellow point will be removed from the PointCloud. If 2 neighbors are specified then both the yellow and green points will be removed from the PointCloud.
ConditionalRemoval Background
Not much to explain here, this filter object removes all points from the PointCloud that do not satisfy one or more conditions that are specified by the user.
The explanation
Let’s break down the code piece by piece.
Some of the code in all 3 of these files is the exact same, so I will only explain what it does once.
Initially the program ensures that the user has specified a command line argument:
if (argc != 2)
{
std::cerr << "please specify command line arg '-r' or '-c'" << std::endl;
exit(0);
}
In the following lines, we first define the PointCloud structures and fill one of them with random points:
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
// Fill in the cloud data
cloud->width = 5;
cloud->height = 1;
cloud->resize (cloud->width * cloud->height);
for (auto& point: *cloud)
{
point.x = 1024 * rand () / (RAND_MAX + 1.0f);
point.y = 1024 * rand () / (RAND_MAX + 1.0f);
point.z = 1024 * rand () / (RAND_MAX + 1.0f);
}
Here is where things are a little bit different depending on which filter class is being used – an if statement involving the command line options divides the program flow.
For the RadiusOutlierRemoval, the user must specify ‘-r’ as the command line argument so that this code is executed:
if (strcmp(argv[1], "-r") == 0){
pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
// build the filter
outrem.setInputCloud(cloud);
outrem.setRadiusSearch(0.8);
outrem.setMinNeighborsInRadius (2);
outrem.setKeepOrganized(true);
// apply filter
outrem.filter (*cloud_filtered);
}
Basically, we create the RadiusOutlierRemoval filter object, set its parameters and apply it to our input cloud. The radius of search is set to 0.8, and a point must have a minimum of 2 neighbors in that radius to be kept as part of the PointCloud.
For the ConditionalRemoval class, the user must specify ‘-c’ as the command line argument so that this code is executed:
else if (strcmp(argv[1], "-c") == 0){
// build the condition
pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond (new
pcl::ConditionAnd<pcl::PointXYZ> ());
range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new
pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::GT, 0.0)));
range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new
pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::LT, 0.8)));
// build the filter
pcl::ConditionalRemoval<pcl::PointXYZ> condrem;
condrem.setCondition (range_cond);
condrem.setInputCloud (cloud);
condrem.setKeepOrganized(true);
// apply filter
condrem.filter (*cloud_filtered);
}
Basically, we create the condition which a given point must satisfy for it to remain in our PointCloud. In this example, we use add two comparisons to the condition: greater than (GT) 0.0 and less than (LT) 0.8. This condition is then used to build the filter.
In both cases the code above creates the filter object that we are going to use and sets certain parameters that are necessary for the filtering to take place.
The following code just outputs PointCloud before filtering and then after applying whatever filter object is used:
std::cerr << "Cloud before filtering: " << std::endl;
for (const auto& point: *cloud)
std::cerr << " " << point.x << " "
<< point.y << " "
<< point.z << std::endl;
// display pointcloud after filtering
std::cerr << "Cloud after filtering: " << std::endl;
for (const auto& point: *cloud_filtered)
std::cerr << " " << point.x << " "
<< point.y << " "
<< point.z << std::endl;
Compiling and running remove_outliers.cpp
Add the following lines to your CMakeLists.txt file:
1cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
2
3project(remove_outliers)
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 (remove_outliers remove_outliers.cpp)
12target_link_libraries (remove_outliers ${PCL_LIBRARIES})
After you have made the executable, you can run it. If you would like to use ConditionalRemoval then simply do:
$ ./remove_outliers -c
Otherwise, if you would like to use RadiusOutlierRemoval, simply do:
$ ./remove_outliers -r
You will see something similar to (depending on which filter you are using):
Cloud before filtering:
0.0080142 0.694695 -0.26015
-0.342265 -0.446349 0.214207
0.173687 -0.84253 -0.400481
-0.874475 0.706127 -0.117635
0.908514 -0.598159 0.744714
Cloud after filtering:
nan nan nan
-0.342265 -0.446349 0.214207
nan nan nan
nan nan nan
0.908514 -0.598159 0.744714