学习一下PCL中点到面的配准算法,笔记一下以免以后忘记,好记性不如烂笔头!嘻嘻!
一、理论基础
1、估计表面法向量问题 =对点集的K最近邻点的协方差矩阵求特征值和特征向量(PCA)问题
2、假设三维点集中的任意点Pi,则
求协方差矩阵C的公式:
这里补充一下:
协方差矩阵的阶=随机向量的维数,而与随机向量的个数无关。协方差矩阵是对称矩阵。其主对角线元素为点的各个分量的方差,可以反映点集在各个轴向的离散程度。其他元素反映了不同分量的相关性,当两个分量距各自均值发生同向偏离时,协方差(即相应的协方差矩阵元素)趋向正值,当两个分量距各自均值发生异向偏离时,协方差趋向负值。
通过计算协方差矩阵的特征值和特征向量,可以反映出点集离散的主要方向。若特征向量为最大特征向量,则特征值即为沿着该轴的顶点数据具有的最大方差值;若特征向量为最小特征向量,则特征值即为沿着该轴的顶点数据具有的最小方差值。
因此,估计表面法线问题可以表示为:,其中
其中,k是点Pi周围K近邻点的数量,在下文中会介绍;:K近邻点的三维质心;
:协方差矩阵C的第j个特征值;
:第j个特征向量
注意:
因为PCA来求解的法线方向模糊,并且不会朝向点云数据集的视点,右侧图 Extended Gaussian Image (EGI)描述了点云的所有法线方向,由于法线方向不一致,他们遍布整个球面
因此要想使数据集中所有法线始终朝向视点,需要加条件,可以看到加了条件处理后的效果更好(效果怎么看在参数的选择部分会解释)
二、选择正确的参数
选择K值的标准:
1°好的效果:估计得到的表面法线相较于两个平面垂直,并且在点云渲染图中可以看到细小的法线边缘
2°比例因子选择太大:估计得到的点特征失真,在两个平坦表面的边缘处的表面法向量发生旋转,并且,边缘模糊,丢失细节
选择K值的小技巧:
如果数据集中的圆形边缘处的曲率十分关键,那么取较小的K值会捕获更好的边缘细节,其他情况可以取大一点的K值。
三、估计法向量
算法思路
1°Create the normal estimation class, and pass the input dataset to it
2°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)
3°Output datasets
4°Use all neighbors in a sphere of radius 3cm
5°Compute the features
PCL中的实现代码是这样的(个人感觉matlab版本的实现更清爽)
#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 ()*
}
原文链接:http://pointclouds.org/documentation/tutorials/normal_estimation.php#normal-estimation