注意:本方法速度虽然快,但只适用于有序点云!!!
1.原理
积分图法线估计,即将点云看做一个有深度的图像(包含坐标xy和深度z)。在从点云起始位置开始生成一个平滑窗口,然后按照点云顺序(该方法必须作用于有序点云),该窗口不停平滑后移,不停获取每个窗口内点云的所有相邻点,并以此进行法线计算。
(1)从有序点云起始点P开始,生成点云窗口(也就是取P周围一定范围内的点云)。
(2)获取该窗口中的所有点云点,按照一定规则进行积分图法线计算(规则在PCL中以参数确定,本文关键函数处会讲解,如果想了解具体原理,可以看下文)。
(3)窗口不停的平滑移动,遍历计算完所有点云点的法向量。
简易版理解如下图:
假设红线是点云的顺序,黄色框即是所谓窗口。
该黄色窗口从起始位置开始,一直移动计算每个窗口中的法向量,直到计算到最后。
2.使用场景
相较于normal_3d,积分图计算法向量
优点:速度快(使用窗口,不使用kd树进行邻近搜索)、可以进行实时计算。
缺点:需要使用有序点云。
使用场景囿于有序点云,实际上使用除了实时激光雷达扫下来的点云,用处较少。
3.注意事项
注意要使用有序点云。
4.关键函数
(1)积分图法线估计方法
COVARIANCE_MATRIX:邻域的协方差矩阵中创建9个积分图去计算一个点的法线。
AVERAGE_3D_GRADIENT:创建了6个积分图去计算3D梯度里面垂直和水平方向的光滑部分,同时利用两个梯度的又积来计算法线。
AVERAGE_DEPTH_CHANGE:创建一个单一的积分图,从平均深度的变化中来计算法线。。
SINGLE_NORMAL:不进行法线平滑,仅计算单个点的法线。
setNormalEstimationMethod()
(2)处理边界的策略
BORDER_POLICY_IGNORE:忽略边界处理。
BORDER_POLICY_MIRROR:镜像边界处理。
setBorderPolicy()
(3)设置法向量的视点
setViewPoint()
(4)计算对象边界的深度变化阈值
setMaxDepthChangeFactor()
(5)平滑法线的区域大小(窗口大小)
setNormalSmoothingSize()
5.代码
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/integral_image_normal.h>
#include <boost/thread/thread.hpp>
#include <pcl/visualization/pcl_visualizer.h>
int main()
{
/****************积分图法线估计********************/
// 原始点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("D:/code/csdn/data/milk_cartoon_all_small_clorox.pcd", *cloud); // 加载原始点云数据
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
// 积分图法线估计
pcl::IntegralImageNormalEstimation<pcl::PointXYZ, pcl::Normal> feature;
feature.setNormalEstimationMethod(feature.AVERAGE_3D_GRADIENT); // 估计方法
feature.setBorderPolicy(feature.BORDER_POLICY_MIRROR); // 处理边界的策略
feature.setViewPoint(0, 0, 0); // 设置法向量的视点
feature.setMaxDepthChangeFactor(0.02f); // 计算对象边界的深度变化阈值
feature.setNormalSmoothingSize(10.0f); // 窗口大小/邻域大小
feature.setInputCloud(cloud); // 只能处理有序点云
feature.compute(*normals);
/****************展示********************/
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer(""));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color(cloud, 0, 225, 0);
viewer->addPointCloud<pcl::PointXYZ>(cloud, color, "sample cloud"); // 添加点云
viewer->addPointCloudNormals<pcl::PointXYZ, pcl::Normal>(cloud, normals, 20, 0.02, "normals"); // 添加点云法向量
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud");
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}