目录
一、概述
1.1原理
1.2实现步骤
1.3应用场景
二、代码实现
2.1关键函数
2.1.1 计算法向量
2.1.2 提取边界点
2.1.3 可视化边界点
2.2完整代码
三、实现效果
PCL点云算法汇总及实战案例汇总的目录地址链接:
PCL点云算法与项目实战案例汇总(长期更新)
一、概述
点云边界提取是计算点云中具有显著几何变化点的一种方法,常用于点云的形状分析和特征提取。在点云数据中,边界点通常位于物体轮廓处,它们的几何信息可以帮助理解点云的轮廓结构。通过提取这些边界点,可以实现物体的轮廓特征增强和更准确的表面重建。
1.1原理
点云边界提取的原理基于局部几何信息的变化,通过分析点云中某一点邻域内点的分布,可以判断该点是否位于边界。具体方法包括:
- 通过KD树找到每个点的局部邻域点。
- 基于这些邻域点计算法向量的变化或曲率变化。
- 若该点周围的法向量变化较大,则认为该点是边界点。
提取边界的公式如下:
1.2实现步骤
- 加载点云数据。
- 使用KD树查找点云的局部邻域。
- 基于邻域内法向量的变化提取边界点。
- 可视化原始点云和提取的边界点。
1.3应用场景
- 边界特征增强:在物体重建或识别过程中,提取边界点有助于增强物体的轮廓特征。
- 形状分析:边界点提取可以用于点云的轮廓形状分析,尤其是在复杂场景中。
- 物体识别:在3D物体识别中,边界点作为特征点可以帮助提高识别的准确性。
二、代码实现
2.1关键函数
2.1.1 计算法向量
该函数使用 pcl::NormalEstimation 来计算每个点的法向量,并将计算结果存储在 normals 点云中。
void computeNormals(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals)
{// 创建法向量估计对象pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;ne.setInputCloud(cloud); // 输入原始点云// 使用 KD-Tree 方法进行邻域搜索pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());ne.setSearchMethod(tree);// 设置 K 近邻搜索中的邻域点数量ne.setKSearch(50); // 计算法向量ne.compute(*normals);
}
2.1.2 提取边界点
该函数使用 pcl::BoundaryEstimation 来提取点云中的边界点。
void extractBoundaries(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals, pcl::PointCloud<pcl::Boundary>::Ptr boundaries)
{// 创建边界估计对象pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> be;// 设置输入的原始点云和法向量be.setInputCloud(cloud);be.setInputNormals(normals);// 使用 KD-Tree 方法进行邻域搜索pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());be.setSearchMethod(tree);// 设置邻域搜索的半径be.setRadiusSearch(0.02);// 设置法向量角度阈值,大于此角度则认为是边界点be.setAngleThreshold(M_PI / 2);// 执行边界点提取操作be.compute(*boundaries);
}
2.1.3 可视化边界点
该函数负责将原始点云和提取的边界点进行可视化展示,原始点云显示为红色,边界点显示为绿色。
void visualizeBoundaries(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Boundary>::Ptr boundaries)
{// 创建一个新的点云用于存储边界点pcl::PointCloud<pcl::PointXYZ>::Ptr boundary_points(new pcl::PointCloud<pcl::PointXYZ>);// 遍历边界估计结果,将边界点存储到新的点云中for (size_t i = 0; i < boundaries->points.size(); ++i){if (boundaries->points[i].boundary_point) // 如果是边界点,则存储该点{boundary_points->points.push_back(cloud->points[i]);}}// 创建可视化窗口boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("Boundary Viewer"));int vp1, vp2;// 创建视口 1,显示原始点云viewer->createViewPort(0.0, 0.0, 0.5, 1.0, vp1);viewer->setBackgroundColor(1.0, 1.0, 1.0, vp1); // 设置背景为白色viewer->addText("Original Point Cloud", 10, 10, "vp1_text", vp1);// 原始点云设置为红色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_color_handler(cloud, 255, 0, 0);viewer->addPointCloud(cloud, cloud_color_handler, "original_cloud", vp1);// 创建视口 2,显示边界点viewer->createViewPort(0.5, 0.0, 1.0, 1.0, vp2);viewer->setBackgroundColor(0.98, 0.98, 0.98, vp2); // 设置背景为灰色viewer->addText("Boundary Points", 10, 10, "vp2_text", vp2);// 边界点设置为绿色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> boundary_color_handler(boundary_points, 0, 255, 0);viewer->addPointCloud(boundary_points, boundary_color_handler, "boundary_cloud", vp2);// 开始可视化显示viewer->spin();
}
2.2完整代码
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
#include <pcl/features/boundary.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>// 计算法向量
void computeNormals(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals)
{// 创建法向量估计对象pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;ne.setInputCloud(cloud); // 输入原始点云// 使用 KD-Tree 方法进行邻域搜索pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());ne.setSearchMethod(tree);// 设置 K 近邻搜索中的邻域点数量ne.setKSearch(50);// 计算法向量ne.compute(*normals);
}// 提取边界点
void extractBoundaries(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals, pcl::PointCloud<pcl::Boundary>::Ptr boundaries)
{// 创建边界估计对象pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> be;// 设置输入的原始点云和法向量be.setInputCloud(cloud);be.setInputNormals(normals);// 使用 KD-Tree 方法进行邻域搜索pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());be.setSearchMethod(tree);// 设置邻域搜索的半径be.setRadiusSearch(0.01);// 设置法向量角度阈值,大于此角度则认为是边界点be.setAngleThreshold(M_PI / 15);// 执行边界点提取操作be.compute(*boundaries);
}// 可视化边界点
void visualizeBoundaries(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Boundary>::Ptr boundaries)
{// 创建一个新的点云用于存储边界点pcl::PointCloud<pcl::PointXYZ>::Ptr boundary_points(new pcl::PointCloud<pcl::PointXYZ>);// 遍历边界估计结果,将边界点存储到新的点云中for (size_t i = 0; i < boundaries->points.size(); ++i){if (boundaries->points[i].boundary_point) // 如果是边界点,则存储该点{boundary_points->points.push_back(cloud->points[i]);}}// 创建可视化窗口boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("Boundary Viewer"));int vp1, vp2;// 创建视口 1,显示原始点云viewer->createViewPort(0.0, 0.0, 0.5, 1.0, vp1);viewer->setBackgroundColor(1.0, 1.0, 1.0, vp1); // 设置背景为白色viewer->addText("Original Point Cloud", 10, 10, "vp1_text", vp1);// 原始点云设置为红色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_color_handler(cloud, 255, 0, 0);viewer->addPointCloud(cloud, cloud_color_handler, "original_cloud", vp1);// 创建视口 2,显示边界点viewer->createViewPort(0.5, 0.0, 1.0, 1.0, vp2);viewer->setBackgroundColor(0.98, 0.98, 0.98, vp2); // 设置背景为灰色viewer->addText("Boundary Points", 10, 10, "vp2_text", vp2);// 边界点设置为绿色pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> boundary_color_handler(boundary_points, 0, 255, 0);viewer->addPointCloud(boundary_points, boundary_color_handler, "boundary_cloud", vp2);// 开始可视化显示viewer->spin();
}// 主函数
int main(int argc, char** argv)
{// 读取点云数据pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());pcl::io::loadPCDFile("bunny.pcd", *cloud);// 计算法向量pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);computeNormals(cloud, normals);// 提取边界点pcl::PointCloud<pcl::Boundary>::Ptr boundaries(new pcl::PointCloud<pcl::Boundary>);extractBoundaries(cloud, normals, boundaries);// 可视化结果visualizeBoundaries(cloud, boundaries);return 0;
}