一,点的法向量
点云法线
法向量的概念是很小的时候我们就已经说的,法向量是我们点云中一个非常重要的属性,诸如饿哦们常说的三维重建、点云分割,点云去噪 以及特种描述算法等。
特性:
点云中每一点的法向量夹角和曲率值均不随着物体的运动而改变,具有刚体运动不变性
点云法向量得的应用:
1、点云渲染:
法向量可以用于光照渲染,漫反射光照符合Lambert余弦定律,即漫反射光强与N * L成正比,N为法线方向,L为点到光源的向量。所以,在模型边缘处,N与L近似垂直,着色会比较暗。
点云渲染可以检查法向量的正确性。
2、点云重建:
对与一个封闭的曲面,我们可以在空间中定义一个函数形状Indicator := 曲面内部为1,曲面外部为0。则这个形状函数的梯度只有在曲面上不为0,这个梯度方向就是曲面的法线方向。
3、区分薄板正反面:
4、 法线贴图
下面两个图是一个人脸网格的UV展开,属于平面网格。它们的区别在于法线信息,右图的法线继承了原始网格的法线,它使得平面网格也可以渲染出凹凸感。这个技巧常用于游戏场景的渲染,用低面片数的网格加上高质量的法线贴图信息,来增强模型的几何凹凸感。
二、法向量估计方法
一般可用低阶多项式曲面进行局部拟合。如果点云均匀分布,希望计算速度快,也可以用平面进行局部拟合,平面法线即为点云法线。
1.基于Delaunay三角分割法
基于Delaunay三角分割法不适合有噪声的点云,无法很好的用于现场采集数据集中。
2.基于鲁棒统计学方法
基于鲁棒统计学的方法从原理到计算都过于复杂,因此无法直接用在大规模的点云场景中。
3.基于局部表面拟合法
该方法使用范围最为广泛,且适用于大规模的点云场景中,计算原理较为简单了,效率快。
原理介绍:
1、 对点云中的每个点找半径A规定领域范围或者直接选择最近的K个点组合成一个平面,法然后使用最小二乘法拟合一个局部的平面。平面方程ax+by+cz+d=0;
Matlab 最小二乘法 拟合平面_Σίσυφος1900的博客-CSDN博客_matlab最小二乘法拟合平面
因此我们对点云中的某个点拟合的过程表示为:
平面局部拟合我们在pcl 中采的是PCA( 主要成分分析):
2、PCA分析:
因为曲面局部是平坦的,法线所在的方向是主成分最低的方向,也就是PCA里面最小特征值对应的特征方向
寻求一个方向n使得所有邻域点在方向n上的投影点的分布最为集中,也是就意味着点在该方向上的投影的方差最小
具体操作如下:
1)给每个点计算K邻近邻域(也可以用半径邻域);
2)计算PCA的协方差矩阵 Cov = ∑ (Ni – C) X (Ni – C),其中Ni为邻域点,C为中心点。这个协方差矩阵的最小特征值对应的特征向量即为这个点的法线方向。
在特征比较尖锐的地方,如左图所示,法线计算容易被光滑掉。右图直线是物体真实的几何,可以比较红色法线方向的差别。特征点的法线,可以用迭代权重的方法来修正,如先用平面局部拟合,然后给局部的点计算权重,离平面越远的点权重越小,然后再用带权重的点局部拟合平面,如此迭代即可。
pca : 主成分分析(PCA)原理详解 - 知乎
点云法向量_o180o的博客-CSDN博客_点云法向量
法向量的定向:
PCL 计算点云法向量并显示_点云侠的博客-CSDN博客_pcl 法向量
若两个相邻的连个点p1 p2(假设这连个平面上的点平滑的切共面的),那么我们从上一步中计算得到的两个向量你n1 n2 也几乎是平行的,也就是说n1 n2 之间的夹角可能就是0,,若夹角为180度,那么这两个点种的器重一个向量需要翻转一下。
表面的曲率:
曲率的意义:
曲率越小表示这个有K个点组成的平面越平坦,也就是说k-1 个领域的起伏情况越小,反之,曲率越大,则表示领域的起伏越大
计算:
我们在用PCA的时候用协方差矩阵M对齐进行特征值的分解,计算得出M的每个特征值r1<r2<r3<...<r(k-2),那么这K个点平面的曲率就是:
曲率S=r1/(r1+r2+r3+....+r(k-2))
三、PCL中的法向量
1、pcl 中法向量的格式:
/*brief A point structure representing normal coordinates and the surface curvature estimate. (SSE friendly)ingroup common*/
struct Normal : public _Normal
{
inline Normal (const _Normal &p)
{
normal_x = p.normal_x;
normal_y = p.normal_y;
normal_z = p.normal_z;
data_n[3] = 0.0f;
curvature = p.curvature;
}
inline Normal ()
{
normal_x = normal_y = normal_z = data_n[3] = 0.0f;
curvature = 0;
}
inline Normal (float n_x, float n_y, float n_z)
{
normal_x = n_x; normal_y = n_y; normal_z = n_z;
curvature = 0;
data_n[3] = 0.0f;
}
friend std::ostream& operator << (std::ostream& os, const Normal& p);
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud(m_currentCloud->makeShared());
n.setInputCloud(m_currentCloud->makeShared());
n.setSearchMethod(tree);
n.setKSearch(p1);
// 输出结果是 法向量的x y z坐标和表面的曲率,
n.compute(*normals);