PCL -- preprocessing (filtering)

Point Cloud Filtering

wave filtering

When acquiring point cloud data, due to the impact of equipment accuracy, operator experience, environmental factors and other factors, some noise points (that is, points we can't use) are inevitable in point cloud data. We need to remove these points when processing data. This process is called filtering.
1, Point cloud filtering scheme in pcl
(1) Irregular density of point cloud data needs smoothing
(2) Outliers need to be removed due to occlusion and other problems
(3) A large amount of data needs to be downsampled.
(4) Noise data needs to be removed.
The corresponding method is as follows:
(1) Filter the removal points according to the specific given rules.
(2) Some attributes of points are modified by common filtering algorithms.
(3) Down sampling the data.

Pass through filter

Pass through filter: for point cloud data with certain spatial characteristics in spatial distribution, for example, point cloud is collected by line structured light scanning, which is widely distributed along the z direction, but the x and y directions are in a limited range. At this time, the pass through filter can be used to determine the range of point cloud in x or y direction, which can cut off outliers quickly and achieve the purpose of the first step of rough processing.

#include<iostream>
#include<pcl/point_types.h>
#include<pcl/filters/passthrough.h>  //Pass through filter header file
using namespace std;
  
int main()
{
  ///////****************************************************////////////////////
  /*Create a point cloud dataset.*/
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
  cloud->width = 500;
  cloud->height = 1;
  cloud->points.resize(cloud->width*cloud->height);
  std::cout << "Create original point cloud data" << std::endl;
  for (size_t i = 0; i < cloud->points.size(); ++i)
  {
    cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
  }
  
  for (size_t i = 0; i < cloud->points.size(); i++)
  {
    std::cerr << "   " << cloud->points[i].x << " "
      << cloud->points[i].y << " "
      << cloud->points[i].z << std::endl;
  }
  std::cout << "Original point cloud data points:" << cloud->points.size()<< std::endl << std::endl;
 
  ///////****************************************************////////////////////
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_PassThrough(new pcl::PointCloud<pcl::PointXYZ>);
 
  pcl::PassThrough<pcl::PointXYZ> passthrough;
  passthrough.setInputCloud(cloud);//Input point cloud
  passthrough.setFilterFieldName("z");//Operate on z-axis
  passthrough.setFilterLimits(0.0, 400.0);//Set operation range of pass through filter
  //passthrough.setFilterLimitsNegative(true);//true means within the reserved range, false means outside the reserved range
  passthrough.filter(*cloud_after_PassThrough);//Perform filtering and save the filtering results in cloud_after_PassThrough
 
  std::cout << "Point cloud data points after pass through filtering:" << cloud_after_PassThrough->points.size() << std::endl;
 
 std::cerr<<"cloud afer filtering:"<<std::endl;
 for(size_i=0;i<cloud_after_PassThrough->points.size();++i)
	 {
	 	std::cerr<<""<<cloud_after_PassThrough->points[i].x<<" " 
	 	<<cloud_after_PassThrough->points[i].y<<" " 
	 	<<cloud_after_PassThrough->points[i].z<<" " <<std::endl;
	}
}

Voxel filter

The concept of voxels is similar to pixels. The AABB bounding box is used to voxel the point cloud data. Generally, the more dense the voxels are, the more information there is. Noise points and outliers can be removed by voxel grid. On the other hand, if high-resolution cameras and other devices are used to collect point clouds, they tend to be more dense. Too many point clouds will bring difficulties to the subsequent segmentation. Voxel filter can achieve the function of sampling down without destroying the geometry of the point cloud itself.

#include<iostream>
#include<pcl/point_types.h>
#include<pcl/filters/voxel_grid.h>  //Voxel filter header file
using namespace std;
  
int main()
{
  ///////****************************************************////////////////////
  /*Create a point cloud dataset.*/
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
  cloud->width = 500;
  cloud->height = 1;
  cloud->points.resize(cloud->width*cloud->height);
  std::cout << "Create original point cloud data" << std::endl;
  for (size_t i = 0; i < cloud->points.size(); ++i)
  {
    cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
  }
  
  for (size_t i = 0; i < cloud->points.size(); i++)
  {
    std::cerr << "   " << cloud->points[i].x << " "
      << cloud->points[i].y << " "
      << cloud->points[i].z << std::endl;
  }
  std::cout << "Original point cloud data points:" << cloud->points.size()<< std::endl << std::endl;
 
 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_voxelgrid(new pcl::PointCloud<pcl::PointXYZ>);//
 
  pcl::VoxelGrid<pcl::PointXYZ> voxelgrid; 
  voxelgrid.setInputCloud(cloud);//Input point cloud data
  voxelgrid.setLeafSize(10.0f, 10.0f, 10.0f);//AABB length width height
  voxelgrid.filter(*cloud_after_voxelgrid);
 
  std::cout << "Point cloud data points after voxeled grid method:" << cloud_after_voxelgrid->points.size() << std::endl;
 
 std::cerr<<"cloud afer filtering:"<<std::endl;
 for(size_i=0;i<ccloud_after_voxelgrid->points.size();++i)
	 {
	 	std::cerr<<""<<cloud_after_voxelgrid->points[i].x<<" " 
	 	<<cloud_after_voxelgrid->points[i].y<<" " 
	 	<<cloud_after_voxelgrid->points[i].z<<" " <<std::endl;
	}
}

Statistical outlier removal

Using statistical analysis technology, the measurement noise points (i.e. outliers) are removed from a point cloud data set. For example, laser scanning usually produces a point cloud data set with uneven density. In addition, the measurement error also produces sparse outliers, which makes the effect bad. The operation of estimating local point cloud features (such as normal vector or curvature change rate at sampling point) is complex. This will lead to In turn, it will lead to the failure of point cloud registration and other later processing

Solution: make a statistical analysis of the neighborhood of each point, and prune some points that do not meet certain standards. The sparse outlier removal method is based on the calculation of the distance distribution from the point to the adjacent point in the input data. For each point, calculate the average distance from it to all the adjacent points. Suppose that the result is a Gaussian distribution, whose shape is composed of mean value and scale The standard deviation determines that the point whose average distance is beyond the standard range can be defined as outlier and can be removed from the data.
Supplement: Gaussian distribution is also called normal distribution

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.h>
 
int main(int argc, char** argv)
{
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
 
	// Define read object
	pcl::PCDReader reader;
	// Read point cloud file
	reader.read<pcl::PointXYZ>("bunny.pcd", *cloud);//Read required files
 
	std::cout << "Cloud before filtering: " << std::endl;
	std::cout << *cloud << std::endl;
 
	// Create a filter, set the number of adjacent points analyzed for each point to 50, and set the multiple of the standard deviation to 1, which means that if one
	 //If the distance of points exceeds the average distance by more than one standard deviation, the point is marked as outlier, removed and stored
	pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;   //Create filter object
	sor.setInputCloud(cloud);                           //Set point cloud to be filtered
	sor.setMeanK(50);                               //Set the number of query point proximity points to be considered in Statistics
	//sor.setStddevMulThresh(1.0); / / set the threshold to determine whether it is an outlier
	sor.setStddevMulThresh(0.00034);                      //Set threshold to judge whether it is outlier
	sor.filter(*cloud_filtered);                    //storage
 
	std::cerr << "Cloud after filtering: " << std::endl;
	std::cerr << *cloud_filtered << std::endl;
 
	pcl::PCDWriter writer;
	writer.write<pcl::PointXYZ>("test1_inliers.pcd", *cloud_filtered, false);
 
	sor.setNegative(true);
	sor.filter(*cloud_filtered);
	writer.write<pcl::PointXYZ>("test1_outliers.pcd", *cloud_filtered, false);
 
	return (0);
}

Conditional filtering

Conditional filter filters by setting filter conditions, a bit of piecewise function taste, when the point cloud is in a certain range, leave, not abandon.

#include<iostream>
#include<pcl/point_types.h>
#include <pcl/filters/conditional_removal.h>    //Conditional filter header file
using namespace std;
  
int main()
{
  ///////****************************************************////////////////////
  /*Create a point cloud dataset.*/
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
  cloud->width = 500;
  cloud->height = 1;
  cloud->points.resize(cloud->width*cloud->height);
  std::cout << "Create original point cloud data" << std::endl;
  for (size_t i = 0; i < cloud->points.size(); ++i)
  {
    cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
  }
  
  for (size_t i = 0; i < cloud->points.size(); i++)
  {
    std::cerr << "   " << cloud->points[i].x << " "
      << cloud->points[i].y << " "
      << cloud->points[i].z << std::endl;
  }
  std::cout << "Original point cloud data points:" << cloud->points.size()<< std::endl << std::endl;
 
/*Conditional filter*/
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_Condition(new pcl::PointCloud<pcl::PointXYZ>);
 
  pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_condition(new pcl::ConditionAnd<pcl::PointXYZ>());
  range_condition->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
    pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::GT, 0.0)));  //GT means greater than or equal to
  range_condition->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
    pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::LT, 0.8)));  //LT means less than or equal to
 
  pcl::ConditionalRemoval<pcl::PointXYZ> condition;
  condition.setCondition(range_condition);
  condition.setInputCloud(cloud);                   //Input point cloud
  condition.setKeepOrganized(true);
 
  condition.filter(*cloud_after_Condition);
  std::cout << "Point cloud data points after conditional filtering:" << cloud_after_Condition->points.size() << std::endl;
 
 std::cerr<<"cloud afer filtering:"<<std::endl;
 for(size_i=0;i<cloud_after_Condition->points.size();++i)
	 {
	 	std::cerr<<""<<cloud_after_Condition->points[i].x<<" " 
	 	<<cloud_after_Condition->points[i].y<<" " 
	 	<<cloud_after_Condition->points[i].z<<" " <<std::endl;
	}
}

Radius filtering

The radius filter is simpler and rougher than the statistical filter. Draw a circle with a point as the center to calculate the number of points falling in the middle of the circle. When the number is greater than the given value, the point is retained, and if the number is less than the given value, the point is eliminated. This algorithm runs fast, and the points left by sequential iteration must be the most dense, but the radius of the circle and the number of points in the circle need to be specified manually.

#include<iostream>
#include<pcl/point_types.h>
#include <pcl/filters/conditional_removal.h>    //Conditional filter header file
using namespace std;
  
int main()
{
  ///////****************************************************////////////////////
  /*Create a point cloud dataset.*/
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
  cloud->width = 500;
  cloud->height = 1;
  cloud->points.resize(cloud->width*cloud->height);
  std::cout << "Create original point cloud data" << std::endl;
  for (size_t i = 0; i < cloud->points.size(); ++i)
  {
    cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
    cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
  }
  
  for (size_t i = 0; i < cloud->points.size(); i++)
  {
    std::cerr << "   " << cloud->points[i].x << " "
      << cloud->points[i].y << " "
      << cloud->points[i].z << std::endl;
  }
  std::cout << "Original point cloud data points:" << cloud->points.size()<< std::endl << std::endl;
 
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_Radius(new pcl::PointCloud<pcl::PointXYZ>);
 
  pcl::RadiusOutlierRemoval<pcl::PointXYZ> radiusoutlier;  //Create filter
 
  radiusoutlier.setInputCloud(cloud);    //Set input point cloud
  radiusoutlier.setRadiusSearch(100);     //Set the radius of 100 to find the nearest point
  radiusoutlier.setMinNeighborsInRadius(2); //Set the deletion when the number of neighbor point sets of query points is less than 2
                                 
  radiusoutlier.filter(*cloud_after_Radius);
  std::cout << "Point cloud data points after radius filtering:" << cloud_after_Radius->points.size() << std::endl;
 std::cerr<<"cloud afer filtering:"<<std::endl;
 for(size_i=0;i<cloud_after_Radius->points.size();++i)
	 {
	 	std::cerr<<""<<cloud_after_Radius->points[i].x<<" " 
	 	<<cloud_after_Radius->points[i].y<<" " 
	 	<<cloud_after_Radius->points[i].z<<" " <<std::endl;
	}
}

The above five methods are the most basic and commonly used five filtering methods, which will be supplemented later when encountering better methods.

Tags: PCL less

Posted on Sat, 13 Jun 2020 04:14:43 -0400 by jeremuck