- 包含相关头文件
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/pcl_visualizer.h>
- 定义了两个类型别名
PointT
和PointNT
,用于表示输入点云和输出点云中各个点的类型。
输出点云的类型也可以是pcl::Normal,但无法用PCLVisualizer显示。
typedef pcl::PointXYZ PointT;
typedef pcl::PointNormal PointNT;
- 读取点云,如果点云数据不存在,程序结束
pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
if (pcl::io::loadPCDFile<PointT>("../input_cloud/rabbit.pcd", *cloud) == -1)
{
PCL_ERROR("找不到文件...\n");
return -1;
}
- 创建一个
pcl::NormalEstimation
类型的对象nest
,该对象用于进行法线估计运算。在创建这个对象时,需要指定输入点云和输出点云的数据类型。 - 设置了法线估计中的一个参数
KSearch
,表示在计算每个点的法向量时要考虑周围的最近邻点数,这里设置为 50。 - 输入点云赋值给
nest
对象,并调用compute()
函数来计算法向量。计算结果存储在一个pcl::PointCloud<PointNT>::Ptr
类型的指针变量normals
中。
pcl::NormalEstimation<PointT, PointNT> nest;
//nest.setRadiusSearch(0.01); // 设置拟合时邻域搜索半径,最好用模型分辨率的倍数
nest.setKSearch(50);
nest.setInputCloud(cloud);
pcl::PointCloud<PointNT>::Ptr normals(new pcl::PointCloud<PointNT>);
nest.compute(*normals);
- 将原始点云数据中的每个点的坐标信息(x、y、z)复制到法向量点云数据中去。
生成时只生成了法向量,没有将原始点云信息拷贝,为了显示需要复制原信息
也可用其他方法进行连接,如:pcl::concatenateFieldsfor (size_t i = 0; i < cloud->points.size(); ++i) { normals->points[i].x = cloud->points[i].x; normals->points[i].y = cloud->points[i].y; normals->points[i].z = cloud->points[i].z; }
- 将法向量计算结果和原始点云信息结合起来,并使用 PCL 库中的可视化工具进行展示。
我们创建了一个 PCL 可视化对象viewer
,并使用addPointCloud()
函数将原始点云数据添加到可视化对象中,命名为 "cloud"。
设置了两个参数level
和scale
,用于控制法向量集合的显示效果。其中level
表示多少条法向量集合显示成一条,scale
表示法向量长度的缩放比例。
调用viewer.spin()
语句来启动可视化窗口,等待用户交互操作。
pcl::visualization::PCLVisualizer viewer;
viewer.setBackgroundColor(1, 0.6, 0.8,0.2); // R=255/255, G=153/255, B=204/255
pcl::visualization::PointCloudColorHandlerCustom<PointT> color_handler(cloud, 0, 255, 0);
viewer.addPointCloud<PointT>(cloud, color_handler, "cloud");
int level = 100;
float scale = 1;
viewer.addPointCloudNormals<PointNT>(normals, level, scale, "normals");
viewer.spin();
完整代码:
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
// 包含相关头文件
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/pcl_visualizer.h>
typedef pcl::PointXYZ PointT;
typedef pcl::PointNormal PointNT; // 也可以pcl::Normal,但无法用PCLVisualizer显示。
int main(int argc, char** argv)
{
// 读取点云
pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
if (pcl::io::loadPCDFile<PointT>("../input_cloud/rabbit.pcd", *cloud) == -1)
{
PCL_ERROR("找不到文件...\n");
return -1;
}
// 计算法向量
pcl::NormalEstimation<PointT, PointNT> nest;
//nest.setRadiusSearch(0.01); // 设置拟合时邻域搜索半径,最好用模型分辨率的倍数
nest.setKSearch(50); // 设置拟合时采用的点数
nest.setInputCloud(cloud);
pcl::PointCloud<PointNT>::Ptr normals(new pcl::PointCloud<PointNT>);
nest.compute(*normals);
for (size_t i = 0; i < cloud->points.size(); ++i)
{ // 生成时只生成了法向量,没有将原始点云信息拷贝,为了显示需要复制原信息
// 也可用其他方法进行连接,如:pcl::concatenateFields
normals->points[i].x = cloud->points[i].x;
normals->points[i].y = cloud->points[i].y;
normals->points[i].z = cloud->points[i].z;
}
// 显示
pcl::visualization::PCLVisualizer viewer;
viewer.setBackgroundColor(1, 0.6, 0.8,0.2); // R=255/255, G=153/255, B=204/255
pcl::visualization::PointCloudColorHandlerCustom<PointT> color_handler(cloud, 0, 255, 0);
viewer.addPointCloud<PointT>(cloud, color_handler, "cloud");
int level = 100; // 多少条法向量集合显示成一条
float scale = 1; // 法向量长度
viewer.addPointCloudNormals<PointNT>(normals, level, scale, "normals");
viewer.spin();
system("pause");
return 0;
}
运行效果: