PCL 【点云分割】

编程入门 行业动态 更新时间:2024-10-23 11:19:14

<a href=https://www.elefans.com/category/jswz/34/1763827.html style=PCL 【点云分割】"/>

PCL 【点云分割】

一、平面模型分割

1、分割代码

//平面分割代码
#include <pcl/ModelCoefficients.h>//模型系数相关头文件
#include <pcl/sample_consensus/method_types.h>//模型定义头文件
#include <pcl/sample_consensus/model_types.h>//随机参数估计方法头文件
#include <pcl/segmentation/sac_segmentation.h>//基于采样一致性分割的类的头文件
pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);//创建分割时所需要的模型系数对象coefficients以及存储内点的点索引集合对象
pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
//创建分割对象pcl::SACSegmentation<pcl::PointXYZ> seg;//可选设置,设置模型系数需要优化seg.setOptimizeCoefficients (true);
//必须设置,设置分割的模型类型、所用的随机参数估计方法、距离阈值、输入点云seg.setModelType (pcl::SACMODEL_PLANE);seg.setMethodType (pcl::SAC_RANSAC);seg.setDistanceThreshold (0.01);//距离阈值,包括0.01seg.setInputCloud (cloud.makeShared ());seg.segment (*inliers, *coefficients);//分割,存储分割结果到点集合inliers及存储平面模型的系数coefficients

2、完整代码

#include <iostream>
#include <pcl/ModelCoefficients.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/sample_consensus/method_types.h>//模型定义头文件
#include <pcl/sample_consensus/model_types.h>//随机参数估计方法头文件
#include <pcl/segmentation/sac_segmentation.h>//基于采样一致性分割的类的头文件#include <pcl/visualization/pcl_visualizer.h>//可视化头文件
int
main (int argc, char** argv)
{pcl::PointCloud<pcl::PointXYZ> cloud;
//填充点云数据
cloud.width  = 15;cloud.height = 1;cloud.points.resize (cloud.width * cloud.height);
//生成数据
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 = 1.0;}
//设置几个局外点,使其偏离z=1的平面
cloud.points[0].z = 2.0;
cloud.points[3].z = -2.0;
cloud.points[6].z = 4.0;
cloud.points[7].z = 1.01;
cloud.points[8].z = 1.005;std::cerr << "Point cloud data: " << cloud.points.size () <<" points" << std::endl;
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;
pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);//创建分割时所需要的模型系数对象coefficients以及存储内点的点索引集合对象
pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
//创建分割对象pcl::SACSegmentation<pcl::PointXYZ> seg;//可选设置,设置模型系数需要优化seg.setOptimizeCoefficients (true);
//必须设置,设置分割的模型类型、所用的随机参数估计方法、距离阈值、输入点云seg.setModelType (pcl::SACMODEL_PLANE);seg.setMethodType (pcl::SAC_RANSAC);seg.setDistanceThreshold (0.01);//距离阈值,包括0.01seg.setInputCloud (cloud.makeShared ());seg.segment (*inliers, *coefficients);//分割,存储分割结果到点集合inliers及存储平面模型的系数coefficients
if (inliers->indices.size () == 0){PCL_ERROR ("Could not estimate a planar model for the given dataset.");
return (-1);}
//输出平面模型的系数 a,b,c,dstd::cerr << "Model coefficients: " << coefficients->values[0] << " " 
<<coefficients->values[1] << " "
<<coefficients->values[2] << " " 
<<coefficients->values[3] <<std::endl;std::cerr << "Model inliers: " << inliers->indices.size () << std::endl;//输出点集合的坐标
for (size_t i = 0; i < inliers->indices.size (); ++i)std::cerr << inliers->indices[i] << "    " <<cloud.points[inliers->indices[i]].x << " "
<<cloud.points[inliers->indices[i]].y << " "
<<cloud.points[inliers->indices[i]].z << std::endl;//可视化
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3d viewer"));
//左边窗口显示输入的点云(待拟合)
int v1(0);
viewer->createViewPort(0,0,0.5,1,v1);
viewer->setBackgroundColor(0,0,0,v1);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color_in(cloud.makeShared(),255,0,0);
viewer->addPointCloud<pcl::PointXYZ>(cloud.makeShared(),color_in,"cloud_in",v1);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,9,"cloud_in",v1);//右边的窗口显示拟合之后的点云
int v2(0);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out(new pcl::PointCloud<pcl::PointXYZ>);//存放最后拟合好的平面
cloud_out->width = inliers->indices.size();
cloud_out->height = 1;
cloud_out->is_dense = false;
cloud_out->resize(cloud_out->height * cloud_out->width);
for(size_t i=0;i<inliers->indices.size();i++)
{cloud_out->points[i].x = cloud.points[inliers->indices[i]].x;cloud_out->points[i].y = cloud.points[inliers->indices[i]].y;cloud_out->points[i].z = cloud.points[inliers->indices[i]].z;
}
std::cout<<"convert succeed!"<<std::endl;
viewer->createViewPort(0.5,0,1,1,v2);
viewer->setBackgroundColor(255,255,255,v2);
viewer->addPointCloud<pcl::PointXYZ>(cloud_out,"cloud_out",v2);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,0,255,0,"cloud_out",v2);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,9,"cloud_out",v2);while (!viewer->wasStopped())
{viewer->spinOnce();
}
return (0);
}

3、可视化 

 

 

二、圆柱体模型分割提取

1、完整代码(不太懂,和之前的从点云中提取一个子集流程基本相同)

#include <pcl/ModelCoefficients.h>
#include <pcl/io/pcd_io.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/visualization/pcl_visualizer.h>//可视化相关头文件typedef pcl::PointXYZ PointT;int
main (int argc, char** argv)
{// All the objects neededpcl::PCDReader reader;//点云读入对象pcl::PassThrough<PointT> pass;//直通滤波器对象pcl::NormalEstimation<PointT, pcl::Normal> ne;//法线估计对象pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg; //分割对象pcl::PCDWriter writer;//写对象pcl::ExtractIndices<PointT> extract;//点提取对象pcl::ExtractIndices<pcl::Normal> extract_normals;//法线提取对象pcl::search::KdTree<PointT>::Ptr tree (new pcl::search::KdTree<PointT> ());//搜索方式对象// Datasetspcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);pcl::PointCloud<PointT>::Ptr cloud_filtered (new pcl::PointCloud<PointT>);pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);pcl::PointCloud<PointT>::Ptr cloud_filtered2 (new pcl::PointCloud<PointT>);pcl::PointCloud<pcl::Normal>::Ptr cloud_normals2 (new pcl::PointCloud<pcl::Normal>);pcl::ModelCoefficients::Ptr coefficients_plane (new pcl::ModelCoefficients), coefficients_cylinder (new pcl::ModelCoefficients);pcl::PointIndices::Ptr inliers_plane (new pcl::PointIndices), inliers_cylinder (new pcl::PointIndices);//一个指向索引的指针// Read in the cloud datareader.read ("table_scene_mug_stereo_textured.pcd", *cloud);std::cerr << "PointCloud has: " << cloud->points.size () << " data points." << std::endl;// Build a passthrough filter to remove spurious NaNs//对cloud的z fields进行滤波,剔除0~1.5之外的点pass.setInputCloud (cloud);//设置输入点云pass.setFilterFieldName ("z");//设置滤波的fieldpass.setFilterLimits (0, 1.5);//滤波范围pass.filter (*cloud_filtered);//滤波结果存放std::cerr << "PointCloud after filtering has: " << cloud_filtered->points.size () << " data points." << std::endl;// Estimate point normalsne.setSearchMethod (tree);//搜索方式ne.setInputCloud (cloud_filtered);//输入点云为直通滤波器的结果ne.setKSearch (50);//选择最近邻的50个点进行法线估计nepute (*cloud_normals);//法线的计算结果存放至cloud_normals// Create the segmentation object for the planar model and set all the parametersseg.setOptimizeCoefficients (true);//选择是否优化系数seg.setModelType (pcl::SACMODEL_NORMAL_PLANE);seg.setNormalDistanceWeight (0.1);seg.setMethodType (pcl::SAC_RANSAC);seg.setMaxIterations (100);seg.setDistanceThreshold (0.03);seg.setInputCloud (cloud_filtered);//输入的点云seg.setInputNormals (cloud_normals);//输入的法线// Obtain the plane inliers and coefficientsseg.segment (*inliers_plane, *coefficients_plane);//得到平面内点和平面的4个系数std::cerr << "Plane coefficients: " << *coefficients_plane << std::endl;// Extract the planar inliers from the input cloud//提取出平面内点extract.setInputCloud (cloud_filtered);extract.setIndices (inliers_plane);extract.setNegative (false);// Write the planar inliers to diskpcl::PointCloud<PointT>::Ptr cloud_plane (new pcl::PointCloud<PointT> ());extract.filter (*cloud_plane);//将平面点提取至clodu_planestd::cerr << "PointCloud representing the planar component: " << cloud_plane->points.size () << " data points." << std::endl;writer.write ("table_scene_mug_stereo_textured_plane.pcd", *cloud_plane, false);// Remove the planar inliers, extract the restextract.setNegative (true);//提取平面点之外的点,继续处理extract.filter (*cloud_filtered2);//平面点extract_normals.setNegative (true);extract_normals.setInputCloud (cloud_normals);extract_normals.setIndices (inliers_plane);extract_normals.filter (*cloud_normals2);// Create the segmentation object for cylinder segmentation and set all the parametersseg.setOptimizeCoefficients (true);seg.setModelType (pcl::SACMODEL_CYLINDER);seg.setMethodType (pcl::SAC_RANSAC);seg.setNormalDistanceWeight (0.1);seg.setMaxIterations (10000);seg.setDistanceThreshold (0.05);seg.setRadiusLimits (0, 0.1);seg.setInputCloud (cloud_filtered2);//提取平面之后剩余的点云seg.setInputNormals (cloud_normals2);//剩余点云的法向量// Obtain the cylinder inliers and coefficientsseg.segment (*inliers_cylinder, *coefficients_cylinder);//得到点的索引和圆柱体系数std::cerr << "Cylinder coefficients: " << *coefficients_cylinder << std::endl;// Write the cylinder inliers to diskextract.setInputCloud (cloud_filtered2);extract.setIndices (inliers_cylinder);extract.setNegative (false);pcl::PointCloud<PointT>::Ptr cloud_cylinder (new pcl::PointCloud<PointT> ());extract.filter (*cloud_cylinder);//按照圆柱体的索引将圆柱体的点提取至cloud_cylinderif (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 ("table_scene_mug_stereo_textured_cylinder.pcd", *cloud_cylinder, false);}//将原点云、平面cloud_plane、圆柱体cloud_cylinder可视化pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3d viewer"));//第一个窗口显示输入点云int v1(0);viewer->createViewPort(0,0,0.33,1,v1);viewer->setBackgroundColor(0,0,0,v1);pcl::visualization::PointCloudColorHandlerCustom<PointT> color(cloud,255,255,255);viewer->addPointCloud(cloud,color,"cloud",v1);//第二个窗口显示分割的平面int v2(0);viewer->createViewPort(0.33,0,0.66,1,v2);viewer->setBackgroundColor(0,0,0,v2);viewer->addPointCloud(cloud_plane,color,"cloud_plane",v2);//第三个窗口显示分割的圆柱int v3(0);viewer->createViewPort(0.66,0,1,1,v3);viewer->setBackgroundColor(0,0,0,v3);viewer->addPointCloud(cloud_cylinder,color,"cloud_cylinder",v3);viewer->addCoordinateSystem();//显示坐标系while (!viewer->wasStopped()){viewer->spinOnce();}return (0);
}

2、可视化

提取桌子(平面分割)、提取杯子(圆柱体分割提取)

 

 

三、欧式聚类分割 

1、API思路

对输入点云进行欧式聚类提取,将聚类结果保存至一个vector<pcl::PointIndices>容器中,该容器中的每一个元素是一类点的索引,即为一个独立的类。迭代访问该容器,对聚类后的每一类点云,将里面的点提取到相应的文件中。

2、欧式聚类提取代码

  // Creating the KdTree object for the search method of the extractionpcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);tree->setInputCloud (cloud_filtered);//将无法提取平面的点云作为cloud_filteredstd::vector<pcl::PointIndices> cluster_indices;//保存每一种聚类,每一种聚类下还有具体的聚类的点pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;//实例化一个欧式聚类提取对象ec.setClusterTolerance (0.02); // 近邻搜索的搜索半径为2cm,重要参数ec.setMinClusterSize (100);//设置一个聚类需要的最少点数目为100ec.setMaxClusterSize (25000);//一个聚类最大点数目为25000ec.setSearchMethod (tree);//设置点云的搜索机制ec.setInputCloud (cloud_filtered);//设置输入点云ec.extract (cluster_indices);//将聚类结果保存至cluster_indices中//迭代访问点云索引cluster_indices,直到分割出所有聚类,一个循环提取出一类点云int j = 0;for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin (); it != cluster_indices.end (); ++it){pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZ>);//创建新的点云数据集cloud_cluster,直到分割出所有聚类for (std::vector<int>::const_iterator pit = it->indices.begin (); pit != it->indices.end (); pit++)cloud_cluster->points.push_back (cloud_filtered->points[*pit]); //*cloud_cluster->width = cloud_cluster->points.size ();cloud_cluster->height = 1;cloud_cluster->is_dense = true;std::cout << "PointCloud representing the Cluster: " << cloud_cluster->points.size () << " data points." << std::endl;std::stringstream ss;ss << "cloud_cluster_" << j << ".pcd";writer.write<pcl::PointXYZ> (ss.str (), *cloud_cluster, false); //*j++;}

3、完整代码

#include <pcl/ModelCoefficients.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/features/normal_3d.h>
#include <pcl/kdtree/kdtree.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/visualization/pcl_visualizer.h>//可视化相关头文件int 
main (int argc, char** argv)
{// Read in the cloud datapcl::PCDReader reader;pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>), cloud_f (new pcl::PointCloud<pcl::PointXYZ>);reader.read ("table_scene_lms400.pcd", *cloud);std::cout << "PointCloud before voxel filtering has: " << cloud->points.size () << " data points." << std::endl; //*// Create the filtering object: downsample the dataset using a leaf size of 1cmpcl::VoxelGrid<pcl::PointXYZ> vg;pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);vg.setInputCloud (cloud);vg.setLeafSize (0.01f, 0.01f, 0.01f);vg.filter (*cloud_filtered);std::cout << "PointCloud after voxel filtering has: " << cloud_filtered->points.size ()  << " data points." << std::endl; //*// Create the segmentation object for the planar model and set all the parameterspcl::SACSegmentation<pcl::PointXYZ> seg;//实例化一个分割对象pcl::PointIndices::Ptr inliers (new pcl::PointIndices);//实例化一个索引pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);//实例化模型参数pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane (new pcl::PointCloud<pcl::PointXYZ> ());//提取到的平面保存至cloud_planepcl::PCDWriter writer;seg.setOptimizeCoefficients (true);//参数优化seg.setModelType (pcl::SACMODEL_PLANE);//模型类型:平面seg.setMethodType (pcl::SAC_RANSAC);//参数估计方法seg.setMaxIterations (100);//最大迭代次数seg.setDistanceThreshold (0.02);//设置内点到模型的距离允许最大值int i=0, nr_points = (int) cloud_filtered->points.size ();//计数变量i,记下提取的平面的个数while (cloud_filtered->points.size () > 0.3 * nr_points){// Segment the largest planar component from the remaining cloudseg.setInputCloud (cloud_filtered);//设置要分割的点云seg.segment (*inliers, *coefficients);//输出平面点的索引和参数if (inliers->indices.size () == 0)//如果平面点索引的数量为0{std::cout << "Could not estimate a planar model for the given dataset." << std::endl;break;}// Extract the planar inliers from the input cloudpcl::ExtractIndices<pcl::PointXYZ> extract;//实例化一个提取对象extract.setInputCloud (cloud_filtered);//设置要提取的点云extract.setIndices (inliers);//根据分割得到的平面的索引提取平面extract.setNegative (false);//提取内点// Write the planar inliers to diskextract.filter (*cloud_plane);//保存提取到的平面std::cout << "PointCloud representing the planar component: " << cloud_plane->points.size () << " data points." << std::endl;//存写指针的参数cloud_plane->width = cloud_plane->points.size();cloud_plane->height = 1;cloud_plane->resize(cloud_plane->width);cloud_plane->is_dense = false;//依次将该指针(cloud_plane)保存至一个专门存放平面的文件(未设置点云格式)、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、std::stringstream s_plane;s_plane << "cloud_plane_"<< i <<".pcd";pcl::io::savePCDFileASCII(s_plane.str(),*cloud_plane);std::cout<<s_plane.str()<<" save succeed \n\n"<<std::endl;//计数变量加1i++;// Remove the planar inliers, extract the restextract.setNegative (true);//提取外点(除第一个平面之外的点)extract.filter (*cloud_f);//保存除平面之外的剩余点cloud_filtered = cloud_f;//将剩余点作为下一次分割、提取的平面的输入点云}// Creating the KdTree object for the search method of the extractionpcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);tree->setInputCloud (cloud_filtered);//将无法提取平面的点云作为cloud_filteredstd::vector<pcl::PointIndices> cluster_indices;//保存每一种聚类,每一种聚类下还有具体的聚类的点pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;//实例化一个欧式聚类提取对象ec.setClusterTolerance (0.02); // 近邻搜索的搜索半径为2cm,重要参数ec.setMinClusterSize (100);//设置一个聚类需要的最少点数目为100ec.setMaxClusterSize (25000);//一个聚类最大点数目为25000ec.setSearchMethod (tree);//设置点云的搜索机制ec.setInputCloud (cloud_filtered);//设置输入点云ec.extract (cluster_indices);//将聚类结果保存至cluster_indices中//迭代访问点云索引cluster_indices,直到分割出所有聚类,一个循环提取出一类点云int j = 0;for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin (); it != cluster_indices.end (); ++it){pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZ>);//创建新的点云数据集cloud_cluster,直到分割出所有聚类for (std::vector<int>::const_iterator pit = it->indices.begin (); pit != it->indices.end (); pit++)cloud_cluster->points.push_back (cloud_filtered->points[*pit]); //*cloud_cluster->width = cloud_cluster->points.size ();cloud_cluster->height = 1;cloud_cluster->is_dense = true;std::cout << "PointCloud representing the Cluster: " << cloud_cluster->points.size () << " data points." << std::endl;std::stringstream ss;ss << "cloud_cluster_" << j << ".pcd";writer.write<pcl::PointXYZ> (ss.str (), *cloud_cluster, false); //*j++;}pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3d viewer"));int v1(0);viewer->createViewPort(0,0,0.5,1,v1);viewer->setBackgroundColor(0,0,0,v1);viewer->addPointCloud(cloud,"cloud",v1);viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,255,0,0,"cloud",v1);//第二个视口,显示分割聚类后的点云//读入每一个点云pcl::PointCloud<pcl::PointXYZ>::Ptr view0(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr view1(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr view2(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr view3(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr view4(new pcl::PointCloud<pcl::PointXYZ>);//读取两个平面的指针pcl::PointCloud<pcl::PointXYZ>::Ptr view_plane1(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr view_plane2(new pcl::PointCloud<pcl::PointXYZ>);pcl::io::loadPCDFile("cloud_cluster_0.pcd",*view0);pcl::io::loadPCDFile("cloud_cluster_1.pcd",*view1);pcl::io::loadPCDFile("cloud_cluster_2.pcd",*view2);pcl::io::loadPCDFile("cloud_cluster_3.pcd",*view3);pcl::io::loadPCDFile("cloud_cluster_4.pcd",*view4);//读取两个平面pcl::io::loadPCDFile("cloud_plane_0.pcd",*view_plane1);pcl::io::loadPCDFile("cloud_plane_1.pcd",*view_plane2);std::cerr<<"cloud_plane read"<<std::endl;int v2(0);viewer->createViewPort(0.5,0,1,1,v2);viewer->setBackgroundColor(1,1,1,v2);viewer->addPointCloud(view0,"view0",v2);viewer->addPointCloud(view1,"view1",v2);viewer->addPointCloud(view2,"view2",v2);viewer->addPointCloud(view3,"view3",v2);viewer->addPointCloud(view4,"view4",v2);//显示两个平面viewer->addPointCloud<pcl::PointXYZ>(view_plane1,"view_plane1",v2);viewer->addPointCloud(view_plane2,"view_plane2",v2);std::cout<<"added!"<<std::endl;viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,255,0,0,"view0",v2);viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,0,255,0,"view1",v2);viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,0,0,255,"view2",v2);viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,126,123,0,"view3",v2);viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,0,0,0,"view4",v2);//设置两个平面的颜色viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,100,0,60,"view_plane1",v2);viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,0,255,60,"view_plane2",v2);while (!viewer->wasStopped()){viewer->spinOnce();}return (0);
}

注:点云保存,读取,可视化部分没有利用循环,有点蠢,跑完例程之后回头解决这个问题 

4、可视化

注:两个平面是使用平面分割提取出来的,其余部分是使用欧式聚类提取得到的结果

 

四、基于区域生长的分割 

1、概述

本例程学习如何用pcl::RegionGrowing类实现区域生长分割。算法的输出是一个聚类的集合,每个聚类集合被认为是同一光滑表面的一部分。

该算法思想:首先依据点的曲率值对点进行排序,之所以排序,是因为区域生长算法是从曲率最小的点开始生长的,这个点就是初始种子点,初始种子点所在的区域即为最平滑的区域,一般场景中平面区域较大,这样从最平滑的区域开始生长即可减少分割区域的总数,提高效率等。

算法的流程:设置一空的聚类区域Ç和空的种子点序列Q,选好初始种子点,将其加入种子点序列,并搜索该种子点的领域点,计算每一个领域点法线与种子点法线之间的夹角,小于设定的平滑阀值时,将领域点加入到ç中,同时判断该领域点的曲率值是否小于曲率阀值,将小于曲率阔值的领域点加入种子点序列Q中,在Q中重新选择新的种子点重复上述步骤,直到Q中序列为空,算法结束。

2、基于区域生长的分割部分代码

基于法线

#include <pcl/segmentation/region_growing.h>// 区域生长算法的5个参数pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;//创建区域生长分割对象reg.setMinClusterSize (50);//设置一个聚类需要的最小点数reg.setMaxClusterSize (1000000);//设置一个聚类需要的最大点数reg.setSearchMethod (tree);//设置搜索方法reg.setNumberOfNeighbours (30);//设置搜索的临近点数目reg.setInputCloud (cloud);//设置输入点云if(Bool_Cuting)reg.setIndices (indices);//通过输入参数设置,确定是否输入点云索引.如果需要直通滤波,就设置输入点云的索引reg.setInputNormals (normals);//设置输入点云的法向量reg.setSmoothnessThreshold (SmoothnessThreshold / 180.0 * M_PI);//设置平滑阈值reg.setCurvatureThreshold (CurvatureThreshold);//设置曲率阈值std::vector <pcl::PointIndices> clusters;//保存每一种聚类,每一种聚类下面还有具体的点reg.extract (clusters);//获取聚类的结果,分割结果保存在点云索引的向量中。end = time(0); diff[2] = difftime (end, start)-diff[0]-diff[1]; //区域生长分割算法花费的时间PCL_INFO ("\tRegion growing takes(seconds): %d\n", diff[2]);std::cout << "Number of clusters is equal to " << clusters.size () << std::endl;//输出聚类的数量,有几类std::cout << "First cluster has " << clusters[0].indices.size () << " points." << endl;//输出第一个聚类中点的数量/***std::cout << "These are the indices of the points of the initial" <<std::endl << "cloud that belong to the first cluster:" << std::endl;int counter = 0;while (counter < clusters[0].indices.size ()){std::cout << clusters[0].indices[counter] << ", ";counter++;if (counter % 10 == 0)std::cout << std::endl;}std::cout << std::endl;***///保存第一个分割//vector和迭代器 理解不到位pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_seg1(new pcl::PointCloud<pcl::PointXYZ>);for (std::vector<int>::const_iterator pit = clusters[0].indices.begin();pit !=clusters[0].indices.end();pit++)//创建一个迭代器pit以访问第一个聚类的每一个点{cloud_seg1->points.push_back(cloud->points[*pit]);//迭代器pit类似于一个指针,将第一个聚类分割中的每一个点进行强制类型转换,并放置在points中}cloud_seg1->width = cloud_seg1->points.size();cloud_seg1->height = 1;cloud_seg1->is_dense = false;pcl::io::savePCDFileASCII("cloud_seg1.pcd",*cloud_seg1);std::cerr<<"cloud_seg1 saved!!!  poins: "<<cloud_seg1->size()<<std::endl;

3、完整代码

//该算法(区域生长分割)同欧式聚类分割类似,也是最后将聚类分割的结果存放至一个点云索引的向量vector中
#include <iostream>
#include <vector>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/search/search.h>
#include <pcl/search/kdtree.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/region_growing.h>
#include <pcl/console/print.h>
#include <pcl/console/parse.h>
#include <pcl/console/time.h>
//#include <windows.h>
#include <stdio.h>
//#include <psapi.h>
/***
void PrintMemoryInfo( )
{HANDLE hProcess;PROCESS_MEMORY_COUNTERS pmc;hProcess=GetCurrentProcess();printf( "\nProcess ID: %u\n", hProcess );// Print information about the memory usage of the process.//输出进程使用的内存信息if (NULL == hProcess)return;if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) ){printf( "\tPageFaultCount: 0x%08X\n", pmc.PageFaultCount );printf( "\tPeakWorkingSetSize: 0x%08X\n", pmc.PeakWorkingSetSize );printf( "\tWorkingSetSize: 0x%08X\n", pmc.WorkingSetSize );printf( "\tQuotaPeakPagedPoolUsage: 0x%08X\n", pmc.QuotaPeakPagedPoolUsage );printf( "\tQuotaPagedPoolUsage: 0x%08X\n", pmc.QuotaPagedPoolUsage );printf( "\tQuotaPeakNonPagedPoolUsage: 0x%08X\n", pmc.QuotaPeakNonPagedPoolUsage );printf( "\tQuotaNonPagedPoolUsage: 0x%08X\n", pmc.QuotaNonPagedPoolUsage );printf( "\tPagefileUsage: 0x%08X\n", pmc.PagefileUsage ); printf( "\tPeakPagefileUsage: 0x%08X\n", pmc.PeakPagefileUsage );}CloseHandle( hProcess );
}
****/
using namespace pcl::console;
int
main (int argc, char** argv)
{if(argc<2){std::cout<<".exe xx.pcd -kn 50 -bc 0 -fc 10.0 -nc 0 -st 30 -ct 0.05"<<endl;return 0;}//如果输入参数小于1个,输出提示time_t start,end,diff[5],option;//定义几个时刻,便于后面作差输出每一步花费的时间start = time(0); int KN_normal=50; //设置默认输入参数bool Bool_Cuting=false;//设置默认输入参数float far_cuting=10,near_cuting=0,SmoothnessThreshold=30.0,CurvatureThreshold=0.05;//设置默认输入参数parse_argument (argc, argv, "-kn", KN_normal);//设置用于法向量估计的k近邻数目parse_argument (argc, argv, "-bc", Bool_Cuting);//设置是否需要直通滤波parse_argument (argc, argv, "-fc", far_cuting);//直通滤波的最大值parse_argument (argc, argv, "-nc", near_cuting);//直通滤波的最小值parse_argument (argc, argv, "-st", SmoothnessThreshold);//平滑阈值parse_argument (argc, argv, "-ct", CurvatureThreshold);//曲率阈值pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);if ( pcl::io::loadPCDFile <pcl::PointXYZ> (argv[1], *cloud) == -1){std::cout << "Cloud reading failed." << std::endl;return (-1);}// 加载输入点云数据end = time(0); diff[0] = difftime (end, start); PCL_INFO ("\tLoading pcd file takes(seconds): %d\n", diff[0]);//Noraml estimation step(1 parameter)//创建一个tree对象pcl::search::Search<pcl::PointXYZ>::Ptr tree = boost::shared_ptr<pcl::search::Search<pcl::PointXYZ> > (new pcl::search::KdTree<pcl::PointXYZ>);//创建一个指向kd树搜索对象的共享指针pcl::PointCloud <pcl::Normal>::Ptr normals (new pcl::PointCloud <pcl::Normal>);pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimator;//创建法线估计对象normal_estimator.setSearchMethod (tree);//设置搜索方法normal_estimator.setInputCloud (cloud);//设置法线估计对象输入点集normal_estimator.setKSearch (KN_normal);// 设置用于法向量估计的k近邻数目normal_estimatorpute (*normals);//计算并输出法向量end = time(0); diff[1] = difftime (end, start)-diff[0]; PCL_INFO ("\tEstimating normal takes(seconds): %d\n", diff[1]);//输出法线估计这一步用了多久时间//optional step: cutting the part are far from the orignal point in Z direction.2 parameterspcl::IndicesPtr indices (new std::vector <int>);//创建一组索引if(Bool_Cuting)//判断是否需要直通滤波{pcl::PassThrough<pcl::PointXYZ> pass;//设置直通滤波器对象pass.setInputCloud (cloud);//设置输入点云pass.setFilterFieldName ("z");//设置指定过滤的维度pass.setFilterLimits (near_cuting, far_cuting);//设置指定纬度过滤的范围pass.filter (*indices);//执行滤波,保存滤波结果在上述索引中}// 区域生长算法的5个参数pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;//创建区域生长分割对象reg.setMinClusterSize (50);//设置一个聚类需要的最小点数reg.setMaxClusterSize (1000000);//设置一个聚类需要的最大点数reg.setSearchMethod (tree);//设置搜索方法reg.setNumberOfNeighbours (30);//设置搜索的临近点数目reg.setInputCloud (cloud);//设置输入点云if(Bool_Cuting)reg.setIndices (indices);//通过输入参数设置,确定是否输入点云索引.如果需要直通滤波,就设置输入点云的索引reg.setInputNormals (normals);//设置输入点云的法向量reg.setSmoothnessThreshold (SmoothnessThreshold / 180.0 * M_PI);//设置平滑阈值reg.setCurvatureThreshold (CurvatureThreshold);//设置曲率阈值std::vector <pcl::PointIndices> clusters;//保存每一种聚类,每一种聚类下面还有具体的点reg.extract (clusters);//获取聚类的结果,分割结果保存在点云索引的向量中。end = time(0); diff[2] = difftime (end, start)-diff[0]-diff[1]; //区域生长分割算法花费的时间PCL_INFO ("\tRegion growing takes(seconds): %d\n", diff[2]);std::cout << "Number of clusters is equal to " << clusters.size () << std::endl;//输出聚类的数量,有几类std::cout << "First cluster has " << clusters[0].indices.size () << " points." << endl;//输出第一个聚类中点的数量/***std::cout << "These are the indices of the points of the initial" <<std::endl << "cloud that belong to the first cluster:" << std::endl;int counter = 0;while (counter < clusters[0].indices.size ()){std::cout << clusters[0].indices[counter] << ", ";counter++;if (counter % 10 == 0)std::cout << std::endl;}std::cout << std::endl;***///保存第一个分割//vector和迭代器 理解不到位pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_seg1(new pcl::PointCloud<pcl::PointXYZ>);for (std::vector<int>::const_iterator pit = clusters[0].indices.begin();pit !=clusters[0].indices.end();pit++)//创建一个迭代器pit以访问第一个聚类的每一个点{cloud_seg1->points.push_back(cloud->points[*pit]);//迭代器pit类似于一个指针,将第一个聚类分割中的每一个点进行强制类型转换,并放置在points中}cloud_seg1->width = cloud_seg1->points.size();cloud_seg1->height = 1;cloud_seg1->is_dense = false;pcl::io::savePCDFileASCII("cloud_seg1.pcd",*cloud_seg1);std::cerr<<"cloud_seg1 saved!!!  poins: "<<cloud_seg1->size()<<std::endl;/**//保存分割结果----------------------------------------------------------------------------------------------------------------------------------int j = 0;for (std::vector<pcl::PointIndices>::const_iterator it = clusters.begin (); it != clusters.end (); ++it){pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster(new pcl::PointCloud<pcl::PointXYZ>);//创建新的点云数据集cloud_cluster,直到分割出所有聚类for (std::vector<int>::const_iterator pit = it->indices.begin(); pit != it->indices.end(); pit++)cloud_cluster->points.push_back(cloud_seg1->points[*pit]);cloud_cluster->width = cloud_cluster->points.size();cloud_cluster->height = 1;cloud_cluster->is_dense = true;std::cout << "PointCloud representing the Cluster: " << cloud_cluster->points.size() << " data points."<< std::endl;std::stringstream ss;ss << "cloud_cluster_" << j << ".pcd";pcl::io::savePCDFileASCII(ss.str(),*cloud_cluster);j++;}
**///PrintMemoryInfo();pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = reg.getColoredCloud ();//如果点云分割成功,该函数返回有色点云,相同的分割有相同的颜色,但是该函数不保证不同的分割一定会有不同的颜色,即不同的分割也可能会有相同的颜色pcl::visualization::CloudViewer viewer ("点云库PCL学习教程第二版-区域增长分割方法");viewer.showCloud(colored_cloud);while (!viewer.wasStopped ()){}//进行可视化return (0);
}

4、可视化

 

五、基于颜色的区域生长分割

1、 概述

该算法与区域生长算法一样,是基于同一策略之上的,其基本思想可参考区域生长算法相关章节。与区域生长算法相比,该算法主要有两个不同之处。第一,该算法用颜色测试代替了法线测试。第二,利用合并算法来控制过分割和欠分割。分割过程中,若两个相邻聚类的平均颜色相差较少,则将这两个聚类合并。然后进行第二步合并,在此步骤中,检查每一个聚类所包含的点的数量,如果这个数量小于用户定义的值,则当前这个聚类与其最近聚类合并在一起。

注意:输入的点云数据必须带有颜色

2、基于颜色的区域生长分割代码

	// Region growing RGB pcl::RegionGrowingRGB<pcl::PointXYZRGB> reg;//实例化一个区域生长对象reg.setInputCloud (cloud);//设置输入点云if(Bool_Cuting)reg.setIndices (indices);//如果刚才启动了直通滤波就设置点的索引reg.setSearchMethod (tree);//设置搜索方式reg.setDistanceThreshold (DistanceThreshold);//设置距离阈值,用于判断点云中的两点是否是相邻点,若小于此阈值则认为两点是相邻的reg.setPointColorThreshold (ColorThreshold);//设置点之间颜色阈值,若颜色差距不超过这个阈值则认为两点是同一区聚类reg.setRegionColorThreshold (RegionColorThreshold);//设置聚类间的颜色阈值,若小于此阈值则将两个聚类合并reg.setMinClusterSize (MinClusterSize);//设置聚类最少点的数目if(b_n)//控制是否同时使用颜色测试和法线测试两种测试手段{reg.setSmoothModeFlag(true);//设置平滑??????reg.setCurvatureTestFlag(true);//设置曲率????reg.setInputNormals (normals);//输入点云法向量reg.setSmoothnessThreshold (SmoothnessThreshold / 180.0 * M_PI);//平滑阈值reg.setCurvatureThreshold (CurvatureThreshold);//曲率阈值}std::vector <pcl::PointIndices> clusters;//保存每一种聚类,每种聚类下还有点reg.extract (clusters);//保存聚类结果至clustersend = time(0); diff[2] = difftime (end, start)-diff[0]-diff[1];PCL_INFO ("\tRGB region growing takes(seconds): %d\n", diff[2]);//输出基于颜色的区域生长分割花费的时间//输出聚类的数目std::cout<<"number of cluster : "<<clusters.size()<<std::endl;

3、完整代码

#include <iostream>
#include <vector>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/search/search.h>
#include <pcl/search/kdtree.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/region_growing_rgb.h>//基于颜色的区域生长分割
#include <pcl/console/print.h>
#include <pcl/console/parse.h>
#include <pcl/console/time.h>
#include <pcl/features/normal_3d.h>
using namespace pcl::console;
int
main (int argc, char** argv)
{if(argc<2){std::cout<<".exe xx.pcd -b_n 0 -kn 50 -st_n 30 -ct_n 0.05 -bc 0 -fc 10 -nc 0 -dt 10 -ct 6 -rt 5 -mt 600" <<endl;return 0;}time_t start,end,diff[5],option;//计时相关start = time(0); bool Bool_Cuting=false,b_n=false;//设置是否需要直通滤波int MinClusterSize=600,KN_normal=50;//设置最小的聚类数目,如果小于这个数目则当前这个聚类与其最近邻聚类合并在一起;设置法线估计的K近邻数目//设置直通滤波器的滤波范围,一个不知道干啥的距离阈值,颜色阈值,区域颜色阈值,平滑阈值,曲率阈值float far_cuting=10,near_cuting=0,DistanceThreshold=10.0,ColorThreshold=6,RegionColorThreshold=5,SmoothnessThreshold=30.0,CurvatureThreshold=0.05;parse_argument (argc, argv, "-b_n", b_n);parse_argument (argc, argv, "-kn", KN_normal);parse_argument (argc, argv, "-st_n", SmoothnessThreshold);parse_argument (argc, argv, "-ct_n", CurvatureThreshold);parse_argument (argc, argv, "-bc", Bool_Cuting);parse_argument (argc, argv, "-fc", far_cuting);parse_argument (argc, argv, "-nc", near_cuting);parse_argument (argc, argv, "-dt", DistanceThreshold);parse_argument (argc, argv, "-ct", ColorThreshold);parse_argument (argc, argv, "-rt", RegionColorThreshold);parse_argument (argc, argv, "-mt", MinClusterSize);//frist step load the point cloudpcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>);//读入点云,注意点云数据必须带颜色if ( pcl::io::loadPCDFile <pcl::PointXYZRGB> (argv[1], *cloud) == -1){std::cout << "Cloud reading failed." << std::endl;return (-1);}end = time(0); diff[0] = difftime (end, start); PCL_INFO ("\tLoading pcd file takes(seconds): %d\n", diff[0]);//读入点云所需时间pcl::search::Search <pcl::PointXYZRGB>::Ptr tree = boost::shared_ptr<pcl::search::Search<pcl::PointXYZRGB> > (new pcl::search::KdTree<pcl::PointXYZRGB>);//实例化一个KdTree对象//Noraml estimation step(1 parameter)pcl::search::Search<pcl::PointXYZRGB>::Ptr tree1 = boost::shared_ptr<pcl::search::Search<pcl::PointXYZRGB> > (new pcl::search::KdTree<pcl::PointXYZRGB>);pcl::PointCloud <pcl::Normal>::Ptr normals (new pcl::PointCloud <pcl::Normal>);pcl::NormalEstimation<pcl::PointXYZRGB, pcl::Normal> normal_estimator;//实例化一个法线估计对象normal_estimator.setSearchMethod (tree);//设置搜索方式normal_estimator.setInputCloud (cloud);//输入点云normal_estimator.setKSearch (KN_normal);//法线估计的K近邻数目normal_estimatorpute (*normals);//法线估计结果保存至normalsend = time(0); diff[1] = difftime (end, start)-diff[0]; PCL_INFO ("\tEstimating normal takes(seconds): %d\n", diff[1]);//输出法线估计花费时间//Optional step: cutting away the clutter scene too far away from camerapcl::IndicesPtr indices (new std::vector <int>);if(Bool_Cuting)//是否通过z轴范围对待处理数据进行裁剪{pcl::PassThrough<pcl::PointXYZRGB> pass;pass.setInputCloud (cloud);pass.setFilterFieldName ("z");pass.setFilterLimits (near_cuting, far_cuting);pass.filter (*indices);//直通滤波结果保存}// Region growing RGB pcl::RegionGrowingRGB<pcl::PointXYZRGB> reg;//实例化一个区域生长对象reg.setInputCloud (cloud);//设置输入点云if(Bool_Cuting)reg.setIndices (indices);//如果刚才启动了直通滤波就设置点的索引reg.setSearchMethod (tree);//设置搜索方式reg.setDistanceThreshold (DistanceThreshold);//设置距离阈值,用于判断点云中的两点是否是相邻点,若小于此阈值则认为两点是相邻的reg.setPointColorThreshold (ColorThreshold);//设置点之间颜色阈值,若颜色差距不超过这个阈值则认为两点是同一区聚类reg.setRegionColorThreshold (RegionColorThreshold);//设置聚类间的颜色阈值,若小于此阈值则将两个聚类合并reg.setMinClusterSize (MinClusterSize);//设置聚类最少点的数目if(b_n)//控制是否同时使用颜色测试和法线测试两种测试手段{reg.setSmoothModeFlag(true);//设置平滑??????reg.setCurvatureTestFlag(true);//设置曲率????reg.setInputNormals (normals);//输入点云法向量reg.setSmoothnessThreshold (SmoothnessThreshold / 180.0 * M_PI);//平滑阈值reg.setCurvatureThreshold (CurvatureThreshold);//曲率阈值}std::vector <pcl::PointIndices> clusters;//保存每一种聚类,每种聚类下还有点reg.extract (clusters);//保存聚类结果至clustersend = time(0); diff[2] = difftime (end, start)-diff[0]-diff[1];PCL_INFO ("\tRGB region growing takes(seconds): %d\n", diff[2]);//输出基于颜色的区域生长分割花费的时间//输出聚类的数目std::cout<<"number of cluster : "<<clusters.size()<<std::endl;pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = reg.getColoredCloud ();//如果分割成功则返回有色点云pcl::visualization::CloudViewer viewer ("点云库PCL学习教程第二版实例-基于颜色的区域生长算法实现点云分割");viewer.showCloud (colored_cloud);while (!viewer.wasStopped ()){boost::this_thread::sleep (boost::posix_time::microseconds (100));}return (0);
}

4、可视化

下面的均为输入点云,上面的是基于颜色的区域生长分割的结果

 

更多推荐

PCL 【点云分割】

本文发布于:2024-02-25 02:57:21,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1697553.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:PCL

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!