簡述
點(diǎn)云法向量估計(jì)這個(gè)問題躏碳,相信很多人在點(diǎn)云處理累奈,曲面重建的過程中遇到過局蚀。表面法線是幾何體面的重要屬性。而點(diǎn)云數(shù)據(jù)集在真實(shí)物體的表面表現(xiàn)為一組定點(diǎn)樣本碎紊。對(duì)點(diǎn)云數(shù)據(jù)集的每個(gè)點(diǎn)的法線估計(jì)佑附,可以看作是對(duì)表面法線的近似推斷。在開源庫提供我們調(diào)用便利的同時(shí)矮慕,了解其實(shí)現(xiàn)原理也有利于我們對(duì)問題的深刻認(rèn)識(shí)帮匾!格物要致知:)
原理
確定表面一點(diǎn)法線的問題近似于估計(jì)表面的一個(gè)相切面法線的問題,因此轉(zhuǎn)換過來以后就變成一個(gè)最小二乘法平面擬合估計(jì)問題痴鳄。
-
平面方程
為平面上點(diǎn)
處法向量的方向余弦瘟斜,
為原點(diǎn)到平面的距離。
求平面方程即轉(zhuǎn)化為求四個(gè)參數(shù)痪寻。
求解過程
1. 待擬合平面點(diǎn)集
待擬合的平面方程:
任意點(diǎn)到平面的距離:
2. 要獲得最佳擬合平面螺句,則需要滿足:
因此,轉(zhuǎn)化為求解極值的問題橡类,
3. 分別對(duì)求偏導(dǎo)
將帶入任意點(diǎn)到平面的距離公式:
繼續(xù)求偏導(dǎo)
令
則:
同理:
將上述三式統(tǒng)一:
易得:
即轉(zhuǎn)化到了求解矩陣A的特征值與特征向量的問題蛇尚,矩陣即為n個(gè)點(diǎn)的協(xié)方差矩陣。
即為該矩陣的一個(gè)特征向量顾画。
4. 求最小特征向量
如上所示取劫,求得的特征向量可能不止一個(gè),那么如何來選取特征向量研侣,使得求得法向量為最佳擬合平面的法向量呢谱邪?
由(內(nèi)積形式),
,
由
因此庶诡,最小特征值對(duì)應(yīng)的特征向量即為法向量
程序應(yīng)用
- PCL中的NormalEstimation
#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 ()*
}
-
OpenMP加速法線估計(jì)
PCL提供了表面法線估計(jì)的加速實(shí)現(xiàn)惦银,基于OpenMP使用多核/多線程來加速計(jì)算。 該類的名稱是pcl :: NormalEstimationOMP末誓,其API與單線程pcl :: NormalEstimation 100%兼容扯俱。 在具有8個(gè)內(nèi)核的系統(tǒng)上,一般計(jì)算時(shí)間可以加快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 it
pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> ne;
ne.setNumberOfThreads(12); // 手動(dòng)設(shè)置線程數(shù)迅栅,否則提示錯(cuò)誤
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 ()*
}