PCL估计点云的表面法向量

  • 估计点云表面法向量的方法
  • 理论基础
    • 法线确定方法
    • 法线方向确定
    • 选择合适的邻域尺度
  • PCL估计表面法线代码实现
  • 用OpenMP加速法线估计
  • 参考资料

估计点云表面法向量的方法

  1. 根据点云中点及其邻域信息,构建点云表面mesh网格,然后mesh网格计算曲面法线
  2. 使用近似值直接从点云数据集中推断出曲面法线
    PCL中点云表面法向量的实现是基于后者的,即在给定点云数据集的情况下,直接计算点云中每个点的表面法线。

理论基础

法线确定方法

确定表面上点的法线问题可以通过估计与表面相切的平面的法线问题来近似,从而又可以转化为最小二乘平面拟合估计问题。
因为点云中的点是三维的,要估计的平面是二维的,用三维数据估计二维数据,很容易想到的方法就是降维,而一提起降维,最容易想到的方法就是PCA,关于PCA的理论及代码实现,请参考我的文章PCA(主成分分析)降维原理及其在optdigits以及点云数据集上的python实现

因此,估计表面法线问题被简化为对由待估计点的最近邻生成的协方差矩阵的特征向量和特征值的分析。 更具体地说,对于每个点pi,我们如下构建它的协方差矩阵C:

其中k是点pi的k个相邻点,这些相邻点可以用最近邻方法确定,也可以指定半径确定,这在文章后面还会讨论到; p_bar表示临近点的3D质心; lamdaj表示协方差矩阵的第j个特征值,vector vj表示协方差矩阵的第j个特征向量。

可以很容易看出,pi和p_bar的shape都是(3,1),所以协方差矩阵C的shape是(3,3)
要从一组点中估计协方差矩阵,在PCL中实现方式为:

  // Placeholder for the 3x3 covariance matrix at each surface patchEigen::Matrix3f covariance_matrix;// 16-bytes aligned placeholder for the XYZ centroid of a surface patchEigen::Vector4f xyz_centroid;// Estimate the XYZ centroidcompute3DCentroid (cloud, xyz_centroid);// Compute the 3x3 covariance matrixcomputeCovarianceMatrix (cloud, xyz_centroid, covariance_matrix);

法线方向确定

PCA可以很好的实现降维,找到切平面,但是并不能确定切平面的法线方向。
解决这一问题的方法是引入一个视点约束,假设引入一个视点vp,为了使所有法线vector ni始终朝向视点的一面,它们需要满足如下不等式约束:

若要在PCL中手动重新定向给定法线,可以用如下代码:

flipNormalTowardsViewpoint (const PointT &point, float vp_x, float vp_y, float vp_z, Eigen::Vector4f &normal);

选择合适的邻域尺度

如前所述,某一点的法线估计离不开其邻域点的约束。但是,给定点云数据,要确定某一点的邻域点集,需要确定合理的k(通过pcl::Feature::setKSearch给出)或r(通过pcl::Feature:: setRadiusSearch)给出,从而使得法线估计取得理想的效果,这就是邻域尺度问题。

该问题极其重要,是默认参数点特征估计的限制因素。为了更好地说明这个问题,下图比较了合理的尺度与偏大尺度的效果。左图是合理尺度的表面法线估计结果,可以看到,算法估计的表面法线分别与两个平面大致垂直,并且在整个桌子上可以看到小边缘。右图是偏大尺度的表面法线估计结果,因为比例尺度偏大,邻近点集覆盖了更多来自相邻表面的点,估计的法线变得失真,在两个平坦表面的边缘处表面法线出现明显旋转,并且边缘出现模糊、很多细节被抑制。

尺度因子的选择需要根据应用程序所需的详细程度来确定。 简单地说,如果杯子的手柄和圆柱形部分之间的边缘处的法线很重要,则比例因子需要足够小以捕获那些细节,否则设置成较大比例因子比较合适。

PCL估计表面法线代码实现

估算输入数据集中所有点的曲面法线:

#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>{pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);... read, pass in or create a point cloud ...// Create the normal estimation class, and pass the input dataset to itpcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;ne.setInputCloud (cloud);// Create an empty kdtree representation, and pass it to the normal estimation object.// Its content will be filled inside the object, based on the given input dataset (as no other search surface is given).pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());ne.setSearchMethod (tree);// Output datasetspcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);// Use all neighbors in a sphere of radius 3cmne.setRadiusSearch (0.03);// Compute the featuresne.compute (*cloud_normals);// cloud_normals->points.size () should have the same size as the input cloud->points.size ()*
}

ne.compute()方法实际上完成了以下的工作:

for each point p in cloud P1. get the nearest neighbors of p2. compute the surface normal n of p3. check if n is consistently oriented towards the viewpoint and flip otherwise

默认的视点坐标是(0,0,0),可以通过如下代码指定:

setViewPoint (float vpx, float vpy, float vpz);

如果只需要计算某一个单点的法线,可以用下面的代码:

computePointNormal (const pcl::PointCloud<PointInT> &cloud, const std::vector<int> &indices, Eigen::Vector4f &plane_parameters, float &curvature);

其中cloud是包含点的输入点云,indices表示来自点云的k-最近邻集合,plane_parameters和curvature表示法线估计的输出,plane_parameters保存法线(nx,ny,nz)的前3个坐标,第四个坐标是D = nc = p_plane(这里是质心)+ p。 输出表面curvature被估计为协方差矩阵的特征值之间的关系,如下所示:

用OpenMP加速法线估计

PCL提供了表面法线估计的加速实现,基于OpenMP使用多核/多线程来加速计算。 该类的名称是pcl :: NormalEstimationOMP,其API与单线程pcl :: NormalEstimation 100%兼容。 在具有8个内核的系统上,一般计算时间可以加快6-8倍。
示例代码:

#include <pcl/point_types.h>
#include <pcl/features/normal_3d_omp.h>{pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);... read, pass in or create a point cloud ...// Create the normal estimation class, and pass the input dataset to itpcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> ne;ne.setNumberOfThreads(12);  // 手动设置线程数,否则提示错误ne.setInputCloud (cloud);// Create an empty kdtree representation, and pass it to the normal estimation object.// Its content will be filled inside the object, based on the given input dataset (as no other search surface is given).pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());ne.setSearchMethod (tree);// Output datasetspcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);// Use all neighbors in a sphere of radius 3cmne.setRadiusSearch (0.03);// Compute the featuresne.compute (*cloud_normals);// cloud_normals->points.size () should have the same size as the input cloud->points.size ()*
}

参考资料

Estimating Surface Normals in a PointCloud

PCL估计点云的表面法向量相关推荐

  1. 基于最小二乘法估计点云的曲面法向量

    转自:https://blog.csdn.net/lming_08/article/details/21171491 之前对PCL库计算三维点云数据的曲面法向量有过介绍,点云的曲面法向量估计,PCL库 ...

  2. 3D点云系列——pcl:点云平滑及法线估计

    通过重采样实现点云平滑 需要平滑的情况: 用RGB-D激光扫描仪等设备扫描物体,尤其是比较小的物体时,往往会有测量误差.这些误差所造成的不规则数据如果直接拿来曲面重建的话,会使得重建的曲面不光滑或者有 ...

  3. PCL之估计点云子集的表面法线

    代码展示: #include <iostream> #include <string> #include <pcl/point_types.h> #include ...

  4. PCL教程指南-估计点云法向量

    PCL教程指南-Estimating Surface Normals in a PointCloud(估计点云法向量) 官方原文档 点云法向指每个点的法向量,它是基于各点所在邻域范围内估计而出,常用方 ...

  5. PCL:点云数据基于法线的边界提取(从最初的法线估计理论推导到最终的边界提取)

    该边界提取采用PCL库里边的方法,基于法线估计来实现的边界检测与提取: 首先从原始点云上计算出法线,再由法线结合数据估计出边界.(这样解释还是特别抽像吧) ------------法线求解:(平面的法 ...

  6. matlab练习程序(点云表面法向量)

    思路还是很容易想到的: 1.首先使用KD树寻找当前点邻域的N个点,这里取了10个,直接调用了vlfeat. 2.用最小二乘估计当前邻域点组成的平面,得到法向量. 3.根据当前邻域点平均值确定邻域质心, ...

  7. PCL点云处理之法向量计算与显示(三十六)

    PCL点云处理之法向量计算与显示(三十六) 一.点云法向量? 二.实验步骤 1.代码 2.效果 总结 一.点云法向量? 法向量是点云的一个重要特征,在区域生长中可以作为生长条件,得到平面.建筑物表面和 ...

  8. PCL——从点云到网格(三)点云到Mesh

    参考: https://mp.weixin.qq.com/s/GFDWOudJ08In6jFyrZ7hhg https://mp.weixin.qq.com/s/FfHkVY-lmlOSf4jKoZq ...

  9. 3D点云系列———pcl:点云网格化

    参考:https://mp.weixin.qq.com/s/FfHkVY-lmlOSf4jKoZqjEA 什么是网格 网格主要用于计算机图形学中,有三角.四角网格等很多种. 计算机图形学中的网格处理绝 ...

最新文章

  1. IIS 配置Http重定向到Https
  2. p ython笔记第一天
  3. ecplise 使用 git
  4. 【hdu 1573 X问题】【 hdu3579 Hello Kiki 】【poj 2891】
  5. [NCTF2019]Reverse
  6. 这个微型机器人可以在人体内“游泳”
  7. HelloWorld程序的编译运行
  8. 计算机系统结构相关技术,计算机系统结构第1章技术总结.pptx
  9. no.2_用绳子计时15分钟
  10. php基本功之_get(),_set()的用法
  11. 为什么基于数字的技术公司进行机器人研究
  12. mysql怎么获取系统_MySQL获取系统性能和状态代码
  13. DuiLib(一)——窗口及消息
  14. 美的董事长:数字化每年投几十亿,看不见结果我也焦虑
  15. 常见面试算法题汇总(Android面试)
  16. 英文词根词典简化笔记
  17. (Android7.0) init.rc与AIL(Android Init Language)语句
  18. 致加西亚的信观后有感
  19. 【SpringBoot】tk.mybatis集成,帮你更加傻瓜式的写代码~
  20. 新站如何快速做SEO优化,获收录和排名

热门文章

  1. C++ RegSetValueEx返回值始终为5
  2. 1 实验9_7_设计函数int getVowel(char str[],char vowel[]); (100分)
  3. 快来直播:仓库是时候实施物联网了吗
  4. 20210117noteexpress云端数据库经验分享
  5. 详解TCP之listen
  6. 计算机信息技术对医院医疗服务工作的影响,计算机在医院信息管理工作中应用探究.doc...
  7. VC++获取不同Windows版本的方法
  8. Java 实现高并发秒杀
  9. 软件过程与项目管理-西安电子科技大学
  10. 如何读懂交易伙伴的EDI需求