一、概述
PCL点云边界特征检测 (附完整代码 C++)_pcl计算点云特征值_McQueen_LT的博客-CSDN博客
在点云的边界特征检测(网格模型的边界特征检测已经是一个确定性问题了,见 网格模型边界检测)方面,PCL中有一个针对点云边界的可以称作为是s t a t e − o f − t h e − a r t state-of-the-artstate−of−the−art的方法,这个方法出自 D e t e c t i n g H o l e s i n P o i n t S e t S u r f a c e s Detecting\space Holes\space in\space Point\space Set\space SurfacesDetecting Holes in Point Set Surfaces这篇论文,叫做 A n g l e C r i t e r i o n Angle\ CriterionAngle Criterion,简称 A C ACAC。这篇论文中还提出了另一种检测边界的方法是H a l f d i s c C r i t e r i o n Halfdisc\ CriterionHalfdisc Criterion,H d C HdCHdC。目前PCL中应该只集成了A C ACAC,因为这个方法确实比H d C HdCHdC好,已经够用了。这两种方法的思路都非常简单,但是却非常有效,而往往流传下来的经典方法都是这种简单有效的方法。
二、AC方法步骤讲解
三、AC方法代码
原始点云:
计算步骤
1、计算点云的法向量
2、计算点云的边界
3、显示
代码
int PointCloudBoundary2(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
// 1 计算法向量
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normalEstimation;
normalEstimation.setInputCloud(cloud);
normalEstimation.setSearchMethod(tree);
normalEstimation.setRadiusSearch(0.02); // 法向量的半径
normalEstimation.compute(*normals);
/*pcl计算边界*/
pcl::PointCloud<pcl::Boundary>::Ptr boundaries(new pcl::PointCloud<pcl::Boundary>); //声明一个boundary类指针,作为返回值
boundaries->resize(cloud->size()); //初始化大小
pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> boundary_estimation; //声明一个BoundaryEstimation类
boundary_estimation.setInputCloud(cloud); //设置输入点云
boundary_estimation.setInputNormals(normals); //设置输入法线
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree_ptr(new pcl::search::KdTree<pcl::PointXYZ>);
boundary_estimation.setSearchMethod(kdtree_ptr); //设置搜寻k近邻的方式
boundary_estimation.setKSearch(30); //设置k近邻数量
boundary_estimation.setAngleThreshold(M_PI * 0.6); //设置角度阈值,大于阈值为边界
boundary_estimation.compute(*boundaries); //计算点云边界,结果保存在boundaries中
cout << "边界点云的点数 : "<< boundaries->size()<< endl;
/*可视化*/
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_visual(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_boundary(new pcl::PointCloud<pcl::PointXYZRGB>);
cloud_visual->resize(cloud->size());
for (size_t i = 0; i < cloud->size(); i++)
{
cloud_visual->points[i].x = cloud->points[i].x;
cloud_visual->points[i].y = cloud->points[i].y;
cloud_visual->points[i].z = cloud->points[i].z;
if (boundaries->points[i].boundary_point != 0)
{
cloud_visual->points[i].r = 255;
cloud_visual->points[i].g = 0;
cloud_visual->points[i].b = 0;
cloud_boundary->push_back(cloud_visual->points[i]);
}
else
{
cloud_visual->points[i].r = 255;
cloud_visual->points[i].g = 255;
cloud_visual->points[i].b = 255;
}
}
pcl::io::savePCDFileBinaryCompressed("D:\\work\\Pointclouds\\clouds\\all.pcd", *cloud_visual);
pcl::io::savePCDFileBinaryCompressed("D:\\work\\Pointclouds\\clouds\\boundaries.pcd", *cloud_boundary);
return 0;
}
显示
boundary_estimation.setKSearch(30); //设置k近邻数量
boundary_estimation.setAngleThreshold(M_PI * 0.6); //设置角度阈值,大于阈值为边界
boundary_estimation.compute(*boundaries); //计算点云边界,结果保存在boundaries中
M_PI*0.6
M_PI*0.8
四、平面轮廓
原始点云:
步骤:
1、采用RANSAC提取平面
2、提取平面
代码:
int PointCloudBoundary2(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
// 1 计算法向量
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normalEstimation;
normalEstimation.setInputCloud(cloud);
normalEstimation.setSearchMethod(tree);
normalEstimation.setRadiusSearch(0.02); // 法向量的半径
normalEstimation.compute(*normals);
/*pcl计算边界*/
pcl::PointCloud<pcl::Boundary>::Ptr boundaries(new pcl::PointCloud<pcl::Boundary>); //声明一个boundary类指针,作为返回值
boundaries->resize(cloud->size()); //初始化大小
pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> boundary_estimation; //声明一个BoundaryEstimation类
boundary_estimation.setInputCloud(cloud); //设置输入点云
boundary_estimation.setInputNormals(normals); //设置输入法线
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree_ptr(new pcl::search::KdTree<pcl::PointXYZ>);
boundary_estimation.setSearchMethod(kdtree_ptr); //设置搜寻k近邻的方式
boundary_estimation.setKSearch(30); //设置k近邻数量
boundary_estimation.setAngleThreshold(M_PI * 0.8); //设置角度阈值,大于阈值为边界
boundary_estimation.compute(*boundaries); //计算点云边界,结果保存在boundaries中
cout << "边界点云的点数 : "<< boundaries->size()<< endl;
/*可视化*/
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_visual(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_boundary(new pcl::PointCloud<pcl::PointXYZRGB>);
cloud_visual->resize(cloud->size());
for (size_t i = 0; i < cloud->size(); i++)
{
cloud_visual->points[i].x = cloud->points[i].x;
cloud_visual->points[i].y = cloud->points[i].y;
cloud_visual->points[i].z = cloud->points[i].z;
if (boundaries->points[i].boundary_point != 0)
{
cloud_visual->points[i].r = 255;
cloud_visual->points[i].g = 0;
cloud_visual->points[i].b = 0;
cloud_boundary->push_back(cloud_visual->points[i]);
}
else
{
cloud_visual->points[i].r = 255;
cloud_visual->points[i].g = 255;
cloud_visual->points[i].b = 255;
}
}
pcl::io::savePCDFileBinaryCompressed("D:\\work\\Pointclouds\\clouds\\all22.pcd", *cloud_visual);
pcl::io::savePCDFileBinaryCompressed("D:\\work\\Pointclouds\\clouds\\boundaries222.pcd", *cloud_boundary);
return 0;
}
/// <summary>
/// 先提取点云的平面然后在进行边界林廓
/// </summary>
/// <param name="cloud"></param>
/// <returns></returns>
int PlaneCloudContour(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
// 0 计算法向量
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normalEstimation;
normalEstimation.setInputCloud(cloud);
normalEstimation.setSearchMethod(tree);
normalEstimation.setRadiusSearch(0.02); // 法向量的半径
normalEstimation.compute(*normals);
// 1 提出出平面
// 提取平面点云的索引
pcl::PointIndices::Ptr index_plane(new pcl::PointIndices);
pcl::SACSegmentationFromNormals<pcl::PointXYZ, pcl::Normal> sacSegmentationFromNormals;
pcl::ModelCoefficients::Ptr mdelCoefficients_plane(new pcl::ModelCoefficients);
sacSegmentationFromNormals.setInputCloud(cloud);
sacSegmentationFromNormals.setOptimizeCoefficients(true);//设置对估计的模型系数需要进行优化
sacSegmentationFromNormals.setModelType(pcl::SACMODEL_NORMAL_PLANE); //设置分割模型
sacSegmentationFromNormals.setNormalDistanceWeight(0.1);//设置表面法线权重系数
sacSegmentationFromNormals.setMethodType(pcl::SAC_RANSAC);//设置采用RANSAC作为算法的参数估计方法
sacSegmentationFromNormals.setMaxIterations(500); //设置迭代的最大次数
sacSegmentationFromNormals.setDistanceThreshold(0.05); //设置内点到模型的距离允许最大值
sacSegmentationFromNormals.setInputCloud(cloud);
sacSegmentationFromNormals.setInputNormals(normals);
sacSegmentationFromNormals.segment(*index_plane, *mdelCoefficients_plane);
std::cerr << "Plane coefficients: " << *mdelCoefficients_plane << std::endl;
// 点云提取
pcl::ExtractIndices<pcl::PointXYZ> extractIndices;
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_p(new pcl::PointCloud<pcl::PointXYZ>);
extractIndices.setInputCloud(cloud);
extractIndices.setIndices(index_plane);
extractIndices.setNegative(false);
extractIndices.filter(*cloud_p);
pcl::io::savePCDFileBinaryCompressed("D:\\work\\Pointclouds\\clouds\\planeCloud.pcd",*cloud_p);
// 2
PointCloudBoundary2(cloud_p);
return 0;
}
结果:
平面点云的轮廓线计算-alpha shapes算法原理和实现_α-shape算法-CSDN博客
双阈值Alpha Shapes算法提取点云建筑物轮廓研究 - 豆丁网