估计PointCloud中的曲面法线(Estimating Surface Normals in a PointCloud)

表面法线是几何表面的重要属性,并在许多领域(如计算机图形应用)中大量使用,以应用生成阴影和其他视觉效果的正确光源。

给定一个几何曲面,通常很简单的方法是将曲面上某一点的法线方向作为垂直于该曲面的矢量。但是,由于我们获取的点云数据集代表了一组实际表面上的点样本,因此有两种可能性:

1、从获取的点云数据集中获取底层曲面,使用曲面网格划分技术,然后从网格中计算曲面法线;
2、使用近似值直接从点云数据集中推断出曲面法线。

本教程将介绍后者,即给定点云数据集,直接计算云中每个点的表面法线。

#理论引入
虽然存在许多不同的正常估算方法,但我们将专注于本教程的方法是最简单的方法之一,具体如下。确定表面上的点的法线的问题近似于估计与表面相切的平面的法线的问题,这又成为最小平方平面拟合估计问题。

    注意:
    欲了解更多信息,包括最小二乘问题的数学方程,请参阅[RusuDissertation](http://files.rbrusu.com/publications/RusuPhDThesis.pdf)。

因此,用于估计表面法线的解决方案被简化为分析从查询点的最近邻居创建的协方差矩阵的特征向量和特征值(或PCA-主分量分析)。更具体地说,对于每个点\ boldsymbol {P} _i,协方差矩阵\ mathcal {C}如下:

\大数符号{} {} {} {} {} {\大写符号\ \ frac {1} {k} \ sum_ {i = 1} ^ cdot大胆的符号{p} _i- \ overline {\ boldsymbol {p}})^ {T}},〜\ mathcal {C} \ cdot \ vec {{\ mathsf v} _j} = \ lambda_j \ cdot \ vec {{\ mathsf v} _j},〜j \ in \ {0,1,2 }

ķ为考虑点邻居的数目在 \ boldsymbol {P} _i附近,\划线{\ boldsymbol {P}} 表示最近邻居的3D质心,\ lambda_j是协方差矩阵的第Ĵ特征值,并且\ vec {{\ mathsf v} _j} 表示第Ĵ个特征向量。

要从PCL中的一组点中估计协方差矩阵,可以使用:


// Placeholder for the 3x3 covariance matrix at each surface patch

  Eigen::Matrix3f covariance_matrix;

  // 16-bytes aligned placeholder for the XYZ centroid of a surface patch

  Eigen::Vector4f xyz_centroid;



  // Estimate the XYZ centroid

  compute3DCentroid (cloud, xyz_centroid);



  // Compute the 3x3 covariance matrix

  computeCovarianceMatrix (cloud, xyz_centroid, covariance_matrix);

一般来说,由于没有数学方法来解决法线的符号,所以通过如上所示的主分量分析(PCA)计算的方位是不明确的,并且不一致地针对整个点云数据集。下图显示了代表厨房环境一部分的较大数据集的两个部分的这些影响。 该图的右边部分显示了扩展高斯图像(EGI),也被称为正常球体,它描述了所有法线从点云的方向。 由于数据集是2.5D,因此从一个单一的观点获得,法线应该只出现在EGI球体的一半。 但是,由于方向不一致,它们遍布整个球体。

_images/unflipped_scene1.jpg_images/unflipped_scene2.jpg_images/unflipped_sphere.jpg

如果事实上已经知道{\ mathsf v} _p这个viewpoint,那么解决这个问题是微不足道的。为了使所有法线\ VEC {\ boldsymbol {N}} _我 始终朝向这个viewpoint,他们需要满足以下等式:

\ vec {\ boldsymbol {n}} _ i \ cdot({\ mathsf v} _p - \ boldsymbol {p} _i)> 0

下图显示了上述数据集中的所有法线一直朝向这个viewpoint的结果。

_images / flipped_scene1.jpg_images / flipped_scene2.jpg_images / flipped_sphere.jpg

要在PCL中手动重新定位给定点,可以使用:

flipNormalTowardsViewpoint (const PointT &point, float vp_x, float vp_y, float vp_z, Eigen::Vector4f &normal);
   注意:
   如果数据集具有多个采集视点,则上述正常的重新定位方法不成立,需要实施更复杂的算法。请参阅[Rusu论文](http://files.rbrusu.com/publications/RusuPhDThesis.pdf)了解更多信息。

#选择正确的比例
如前所述,需要根据该点(也称为k邻域)的周围点附近支持来估计一个点处的表面法线 。

最近邻估计问题的具体细节提出了正确比例因子的问题:给定一个采样点云数据集,什么是正确的k(通过pcl::Feature::setKSearch给出)或r(通过pcl::Feature::setRadiusSearch给出)值应该用于确定一个点的最近邻居的集合?

这个问题是非常重要的并且构成点特征表示的自动估计(即,没有用户给定的阈值)的限制因素。为了更好地说明这个问题,下图给出了选择较小尺度(即较小的r或k)与较大尺度(即较大的r或k))。图的左边部分描绘了合理的精选比例因子,估计的表面法线近似垂直于两个平面表面,并且在整个桌子上可见小边缘。然而,如果比例因子太大(右部分),并且因此邻居集合相邻的表面的覆盖点较大,则估计的点特征表示被扭曲,在两个平坦表面的边缘处旋转表面法线,并且模糊边缘和压制细节。

_images / normals_different_radii.jpg_images / curvature_different_radii.jpg

没有太多的细节,只要假定现在确定一个点的邻域的尺度必须根据应用所要求的细节水平来选择。简而言之,如果杯子的手柄和圆柱形部分之间的边缘处的曲率很重要,则比例因子需要足够小以捕获这些细节,否则就需要较大的比例。

#估计法线
虽然在特征中已经给出了一个正常估计的例子 ,但我们会在这里修改其中的一个,目的是为了更好地解释在幕后发生的事情。

以下代码片段将估计输入数据集中所有点的一组曲面法线。

#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 it
  pcl::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 datasets
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);

  // Compute the features
  ne.compute (*cloud_normals);

  // cloud_normals->points.size () should have the same size as the input cloud->points.size ()*
}

NormalEstimation类的实际计算调用在内部不做任何事情,但是:

for each point p in cloud P

  1. get the nearest neighbors of p

  2. compute the surface normal n of p

  3. check if n is consistently oriented towards the viewpoint and flip otherwise

Viewpoint 默认为(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 表示来自cloud的k个最近邻居的集合,并且plane_parameters和curvature(曲率)表示正常估计的输出,其中plane_parameters保持正常(nx,ny,nz)在前3个坐标,第四个坐标是D = nc。p_plane(质心在这里)+ p。输出表面曲率被估计为协方差矩阵(如上所述)的特征值之间的关系,如下所示:

\ sigma = \ frac {\ lambda_0} {\ lambda_0 + \ lambda_1 + \ lambda_2}

#使用OpenMP加速正常估计
对于精通速度的用户来说,PCL提供了额外的曲面法线估计实现,使用OpenMP的多核/多线程范例来加速计算。该类的名称是pcl::NormalEstimationOMP,它的API与单线程pcl::NormalEstimation完全兼容,这使得它适合作为一个插入式替换。在一个有8个内核的系统上,你应该得到6-8倍的计算时间。

   注意:
   如果您的数据集是有组织的(例如,使用TOF相机,立体相机等获取 - 即它具有宽度和高度),为了更快的结果,请参阅[使用积分图像的法向量估计](http://pointclouds.org/documentation/tutorials/normal_estimation_using_integral_images.php#normal-estimation-using-integral-images)。

Estimating Surface Normals in a PointCloud

猜你喜欢

转载自blog.csdn.net/RuoQiQingCheDi/article/details/83959253