背景:
有一料仓点云数据,需祛除料仓壁,提取料仓面。
思路:
1、点云法线估计
typedef pcl::PointXYZ PointT;
pcl::NormalEstimation<PointT, pcl::Normal> ne; // 法线估算对象
pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>());
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
ne.setSearchMethod(tree); //设置法线估计的方式为kdtree
ne.setInputCloud(cloud_filtered_vg); //输入点云用于法线估计
ne.setKSearch(50); //设置k近邻点的个数为5
ne.compute(*cloud_normals);
2、构建圆柱形分割模型
#include <pcl/segmentation/sac_segmentation.h>
pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg; // 分割器
pcl::ModelCoefficients::Ptr coefficients_cylinder(new pcl::ModelCoefficients);
pcl::PointIndices::Ptr inliers_cylinder(new pcl::PointIndices);
//创建用于圆柱体分割的分割对象,并设置所有参数
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_CYLINDER); // 设置分割模型为圆柱体
seg.setMethodType(pcl::SAC_RANSAC); // 设置采用 RANSAC 算法进行参数估计
seg.setNormalDistanceWeight(0.1); // 设置表面法线权重系数
seg.setMaxIterations(1000); // 设置最大迭代次数 10000
seg.setDistanceThreshold(0.03); // 设置内点到模型的最大距离 0.05m
seg.setRadiusLimits(0, 8.0); // 设置圆柱体的半径范围 0 ~ 0.1m
seg.setInputCloud(cloud_filtered_vg); //输入点云数据
seg.setInputNormals(cloud_normals);
seg.segment(*inliers_cylinder, *coefficients_cylinder);
std::cerr << "Cylinder coefficients: " << *coefficients_cylinder << std::endl;
3、提取数据
pcl::ExtractIndices<PointT> extract; // 点提取对象
pcl::PCDWriter writer;
// 将圆柱体外点写入磁盘
extract.setInputCloud(cloud_filtered_vg);
extract.setIndices(inliers_cylinder);
extract.setNegative(true); //ture:提取圆柱体外数据,false:提取圆柱体内数据
pcl::PointCloud<PointT>::Ptr cloud_cylinder(new pcl::PointCloud<PointT>());
extract.filter(*cloud_cylinder);
writer.write("..\\testdata\\result\\data\\output_cloud.pcd", *cloud_cylinder, false);
完整示例代码:
#include <pcl/io/pcd_io.h>
#include <pcl/ModelCoefficients.h>
#include <pcl/point_types.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/passthrough.h>
#include <pcl/features/normal_3d.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/filters/voxel_grid.h>
typedef pcl::PointXYZ PointT;
int main(int argc, char *argv[]) {
pcl::PCDReader reader; // PCD 文件读取对象
pcl::NormalEstimation<PointT, pcl::Normal> ne; // 法线估算对象
pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg; // 分割器
pcl::PCDWriter writer; // PCD 文件写入对象
pcl::ExtractIndices<PointT> extract; // 点提取对象
pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>());
pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
pcl::ModelCoefficients::Ptr coefficients_cylinder(new pcl::ModelCoefficients);
pcl::PointIndices::Ptr inliers_cylinder(new pcl::PointIndices);
// 读取点云数据
reader.read("..\\testdata\\result\\data\\tong.pcd", *cloud);
std::cerr << "PointCloud has: " << cloud->points.size() << " data points." << std::endl;
//---------------------------------------体素滤波-------------------------------
pcl::PointCloud<PointT>::Ptr cloud_filtered_vg(new pcl::PointCloud<PointT>);
//pcl::ApproximateVoxelGrid<pcl::PointXYZ> sor;
pcl::VoxelGrid<PointT> vg;
vg.setInputCloud(cloud);
vg.setLeafSize(0.2f, 0.2f, 0.2f);//单位为m
vg.filter(*cloud_filtered_vg);
pcl::io::savePCDFileASCII("..\\testdata\\result\\data\\vg.pcd", *cloud_filtered_vg);
ne.setSearchMethod(tree); //设置法线估计的方式为kdtree
ne.setInputCloud(cloud_filtered_vg); //输入过滤后的点云用于法线估计
ne.setKSearch(50); //设置k近邻点的个数为50
ne.compute(*cloud_normals);
//创建用于圆柱体分割的分割对象,并设置所有参数
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_CYLINDER); // 设置分割模型为圆柱体
seg.setMethodType(pcl::SAC_RANSAC); // 设置采用 RANSAC 算法进行参数估计
seg.setNormalDistanceWeight(0.1); // 设置表面法线权重系数
seg.setMaxIterations(1000); // 设置最大迭代次数 10000
seg.setDistanceThreshold(0.03); // 设置内点到模型的最大距离 0.05m
seg.setRadiusLimits(0, 8.0); // 设置圆柱体的半径范围 0 ~ 0.1m
seg.setInputCloud(cloud_filtered_vg);
seg.setInputNormals(cloud_normals);
seg.segment(*inliers_cylinder, *coefficients_cylinder);
std::cerr << "Cylinder coefficients: " << *coefficients_cylinder << std::endl;
// 将圆柱体外点写入磁盘
extract.setInputCloud(cloud_filtered_vg);
extract.setIndices(inliers_cylinder);
extract.setNegative(true);
pcl::PointCloud<PointT>::Ptr cloud_cylinder(new pcl::PointCloud<PointT>());
extract.filter(*cloud_cylinder);
if (cloud_cylinder->points.empty()) {
std::cerr << "Can't find the cylindrical component." << std::endl;
}
else {
std::cerr << "PointCloud representing the cylindrical component: " << cloud_cylinder->points.size()
<< " data points." << std::endl;
writer.write("..\\testdata\\result\\data\\output_cloud.pcd", *cloud_cylinder, false);
}
return 0;
}
运行结果: