PCL 计算点云的平均曲率
目录
一、概述
1.1原理
1.2实现步骤
1.3应用场景
二、代码实现
2.1关键函数
2.1.1 法向量计算
1.2 主曲率与平均曲率计算
2.1.3 可视化函数
2.2完整代码
三、实现效果
PCL点云算法汇总及实战案例汇总的目录地址链接:
PCL点云算法与项目实战案例汇总(长期更新)
一、概述
点云的平均曲率是表征点云表面局部几何特性的重要指标,反映了表面在某一点的平均弯曲程度。通过计算点云的法向量和主曲率,我们可以进一步得出平均曲率并进行可视化展示。本文介绍如何利用PCL库计算点云的平均曲率,并在可视化窗口中显示原始点云、法向量和曲率信息。
1.1原理
平均曲率 H 是通过点云的主曲率计算得到的,公式如下:
平均曲率反映了点云表面在某一点处的局部几何形态,是点云表面平滑度的重要度量。
1.2实现步骤
- 读取点云数据:加载待处理的点云数据。
- 计算法向量:通过 pcl::NormalEstimation 计算每个点的法向量。
- 计算主曲率和平均曲率:通过 pcl::PrincipalCurvaturesEstimation 计算主曲率,并通过公式计算平均曲率。
- 可视化:在一个可视化窗口中同时展示原始点云、法向量和主曲率信息。
1.3应用场景
- 表面检测与质量控制:检测物体表面不平整区域,识别表面异常。
- 3D表面建模:在3D模型重建过程中,平均曲率可帮助识别特征区域。
- 机器人操作规划:基于物体表面的平均曲率进行路径规划,避免机器人操作过程中与表面接触不平整区域。
二、代码实现
2.1关键函数
2.1.1 法向量计算
通过 pcl::NormalEstimation 计算每个点的法向量:
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setInputCloud(cloud); // 设置输入点云
ne.setSearchMethod(tree); // 设置搜索方法
ne.setRadiusSearch(0.03); // 设置搜索半径
ne.compute(*normals); // 计算法向量
1.2 主曲率与平均曲率计算
使用 pcl::PrincipalCurvaturesEstimation 计算主曲率,并根据公式计算平均曲率:
pcl::PrincipalCurvaturesEstimation<pcl::PointXYZ, pcl::Normal, pcl::PrincipalCurvatures> pce;
pce.setInputCloud(cloud); // 设置输入点云
pce.setInputNormals(normals); // 设置法向量
pce.setSearchMethod(tree); // 设置搜索方法
pce.setRadiusSearch(0.03); // 设置搜索半径
pce.compute(*curvatures); // 计算主曲率
// 计算平均曲率
for (size_t i = 0; i < curvatures->size(); ++i) {
float k1 = curvatures->points[i].pc1; // 最大曲率
float k2 = curvatures->points[i].pc2; // 最小曲率
float average_curvature = (k1 + k2) / 2; // 平均曲率
avg_curvature_cloud->points[i].curvature = average_curvature;
}
2.1.3 可视化函数
在可视化窗口中同时展示原始点云、法向量、主曲率以及平均曲率:
void visualizePointCloudAndAverageCurvature(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals, pcl::PointCloud<pcl::PrincipalCurvatures>::Ptr curvatures)
{
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("Average Curvature Viewer"));
int vp_1, vp_2;
// 创建第一个视口:显示原始点云
viewer->createViewPort(0.0, 0.0, 0.5, 1.0, vp_1);
viewer->setBackgroundColor(1.0, 1.0, 1.0, vp_1);
viewer->addText("Original PointCloud", 10, 10, "vp1_text", vp_1);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_color_handler(cloud, 255, 0, 0); // 红色
viewer->addPointCloud(cloud, cloud_color_handler, "original_cloud", vp_1);
// 创建第二个视口:显示法向量与曲率信息
viewer->createViewPort(0.5, 0.0, 1.0, 1.0, vp_2);
viewer->setBackgroundColor(0.98, 0.98, 0.98, vp_2);
viewer->addText("Normals and Average Curvatures", 10, 10, "vp2_text", vp_2);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_color_handler_2(cloud, 0, 255, 0); // 绿色
viewer->addPointCloud(cloud, cloud_color_handler_2, "cloud_with_curvature", vp_2);
// 添加法向量,控制显示间隔和长度
viewer->addPointCloudNormals<pcl::PointXYZ, pcl::Normal>(cloud, normals, 20, 2, "normals", vp_2);
// 添加主曲率
viewer->addPointCloudPrincipalCurvatures<pcl::PointXYZ, pcl::Normal>(cloud, normals, curvatures, 10, 10, "Curvatures", vp_2);
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
}
2.2完整代码
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
#include <pcl/features/principal_curvatures.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
using namespace std;
void visualizePointCloudAndAverageCurvature(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals, pcl::PointCloud<pcl::PrincipalCurvatures>::Ptr curvatures);
void visualizePointCloudAndAverageCurvature(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::Normal>::Ptr normals, pcl::PointCloud<pcl::PrincipalCurvatures>::Ptr curvatures)
{
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("Average Curvature Viewer"));
int vp_1, vp_2;
// 创建第一个视口:显示原始点云
viewer->createViewPort(0.0, 0.0, 0.5, 1.0, vp_1);
viewer->setBackgroundColor(0.0, 0.0, 0.0, vp_1);
viewer->addText("Original PointCloud", 10, 10, "vp1_text", vp_1);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_color_handler(cloud, 255, 0, 0); // 红色
viewer->addPointCloud(cloud, cloud_color_handler, "original_cloud", vp_1);
// 创建第二个视口:显示法向量与曲率信息
viewer->createViewPort(0.5, 0.0, 1.0, 1.0, vp_2);
viewer->setBackgroundColor(0.08, 0.08, 0.08, vp_2);
viewer->addText("Normals and Average Curvatures", 10, 10, "vp2_text", vp_2);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_color_handler_2(cloud, 0, 255, 0); // 绿色
viewer->addPointCloud(cloud, cloud_color_handler_2, "cloud_with_curvature", vp_2);
// 添加法向量,控制显示间隔和长度
viewer->addPointCloudNormals<pcl::PointXYZ, pcl::Normal>(cloud, normals, 20, 0.02, "normals", vp_2);
// 添加主曲率
viewer->addPointCloudPrincipalCurvatures<pcl::PointXYZ, pcl::Normal>(cloud, normals, curvatures, 10, 0.02, "Curvatures", vp_2);
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
}
int main(int argc, char** argv)
{
// 1. 读取点云数据
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile<pcl::PointXYZ>("bunny.pcd", *cloud);
// 2. 计算法向量
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
ne.setInputCloud(cloud); // 设置输入点云
ne.setSearchMethod(tree); // 设置搜索方法
ne.setRadiusSearch(0.03); // 设置搜索半径
ne.compute(*normals); // 计算法向量
// 3. 计算主曲率
pcl::PrincipalCurvaturesEstimation<pcl::PointXYZ, pcl::Normal, pcl::PrincipalCurvatures> pce;
pcl::PointCloud<pcl::PrincipalCurvatures>::Ptr curvatures(new pcl::PointCloud<pcl::PrincipalCurvatures>);
pce.setInputCloud(cloud); // 设置输入点云
pce.setInputNormals(normals); // 设置法向量
pce.setSearchMethod(tree); // 设置搜索方法
pce.setRadiusSearch(0.03); // 设置搜索半径
pce.compute(*curvatures); // 计算主曲率
// 4. 计算平均曲率
pcl::PointCloud<pcl::PointXYZ>::Ptr avg_curvature_cloud(new pcl::PointCloud<pcl::PointXYZ>);
for (size_t i = 0; i < curvatures->size(); ++i) {
float k1 = curvatures->points[i].pc1; // 最大曲率
float k2 = curvatures->points[i].pc2; // 最小曲率
float average_curvature = (k1 + k2) / 2; // 平均曲率
avg_curvature_cloud->push_back(cloud->points[i]);
avg_curvature_cloud->points[i].z = average_curvature; // 用z值来存储曲率信息
}
// 5. 可视化
visualizePointCloudAndAverageCurvature(cloud, normals, curvatures);
return 0;
}