/*
* @Description: 法向量估计(运⾏耗时2min)。:https://www.cnblogs.com/liyao7758258/p/6479255.html
* @Author: HCQ
* @Company(School): UCAS
* @Date: 2020-10-19 16:33:43
* @LastEditors: Please set LastEditors
* @LastEditTime: 2020-10-19 14:34:00
* @FilePath: /pcl-learning/10features特征/1法向量估计/normal_estimation.cpp
*/
#include
#include
#include //法线估计类头⽂件
#include
#include
#include
int
main ()
{
//打开点云代码
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new
pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile ("../table_scene_lms400.pcd", *cloud);
//创建法线估计估计向量
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setInputCloud(cloud);
//创建⼀个空的KdTree对象,并把它传递给法线估计向量
//基于给出的输⼊数据集,KdTree将被建⽴
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new
pcl::search::KdTree<pcl::PointXYZ> ());
ne.setSearchMethod(tree);
//存储输出数据
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new
pcl::PointCloud<pcl::Normal>);
//使⽤半径在查询点周围3厘⽶范围内的所有临近元素
ne.setRadiusSearch(0.03);
//计算特征值
ne.compute(*cloud_normals);
// cloud_normals->points.size ()应该与input cloud_downsampled->points.size ()有
相同的尺⼨
// 存储特征值为点云
pcl::PCDWriter writer;
writer.write<pcl::Normal> ( "../table_cloud_normals.pcd" , *cloud_normals,
false); // 保存⽂件
//可视化
pcl::visualization::PCLVisualizer viewer("PCL Viewer");
viewer.setBackgroundColor (0.0, 0.0, 0.0);
viewer.addPointCloudNormals<pcl::PointXYZ,pcl::Normal>(cloud, cloud_normals);
// 将估计的点云表⾯法线添加到屏幕
while (!viewer.wasStopped ())
{
viewer.spinOnce ();
}
return 0;
}
表面法线是几何体表面一个非常重要的属性,例如:在进行光照渲染的时候产生符合可视习惯的效果时需要表面法线的信息才能正常进行,对于一个已经知道的几何体表面,根据垂直于点表面的矢量,因此推出表面某一点的法线方向比较容易,然而由于我们获取的点云的数据集在真实的物体的表面表现为一组定点的样本,这样就会有两种方法解决:
(1)使用曲面重建技术,从获取的点云数据中得到采样点对应的曲面,然后从曲面模型中计算出表面法线。
(2)直接从点云数据中近似推断表面法线。
在确定表面一点法线的问题近似于估计表面的一个相切面法线的问题,因此转换过来就是求一个最小二乘法平面拟合的问题。
三种法线估计方法:
(1)COVARIANCE_MATRIX模式:从具体某个点的局部领域的协方差矩阵创建9个积分,来计算这个点的法线;
(2)AVERAGE_3D_GRADIENT模式:创建6个积分图来计算水平方向和垂直方向的平滑后的三维梯度并使用两个梯度间的向量积计算法线;
AVERAGE_DEPTH——CHANGE模式:只创建了⼀个单⼀的积分图,从⽽平局深度变化计算法线;
/*
* @Description: 为输⼊数据集中的点的⼦集估计⼀组表⾯法线。
https://pcl.readthedocs.io/projects/tutorials/en/latest/how_features_work.htm
l#how-3d-features-work
* @Author: HCQ
* @Company(School): UCAS
* @Email: 1756260160@qq.com
* @Date: 2020-10-19 15:04:50
* @LastEditTime: 2020-10-19 18:34:17
* @FilePath: /pcl-learning/10features特征/2估计点云⼦集的表⾯法线
(error)/normal_estimation_subdset_points.cpp
*/
#include
#include
#include
// 报错已解决:下⾯报错 error: ‘shared_ptr’ is not a member of ‘pcl’
int main ()
{
//打开点云代码
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new
pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile ("../table_scene_lms400.pcd", *cloud);
// //创建⼀组要使⽤的索引。为简单起⻅,我们将使⽤云中前10%的点
std::vector<int> indices (std::floor (cloud->size () / 10));
for (std::size_t i = 0; i < indices.size (); ++i) indices[i] = i;
// //创建NormalEstimation类,并将输⼊数据集传递给它
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setInputCloud (cloud);
// 传递索引
// std::shared_ptr > indicesptr (new std::vector
(indices)); // 报错
pcl::shared_ptr<std::vector<int> > indicesptr (new std::vector<int>
(indices)); // 解决报错
ne.setIndices (indicesptr);
// 创建⼀个空的kdtree表示形式,并将其传递给normal estimation 对象
// 它的内容将根据给定的输⼊数据集填充到对象内部(因为未提供其他搜索表⾯)。
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>);
// 在半径3cm的范围内近邻
ne.setRadiusSearch (0.03);
// 计算特征
ne.compute (*cloud_normals);
// cloud_normals->size () should have the same size as the input
indicesptr->size ()
}
点特征直⽅图(PFH)在PCL中的实现是pcl_features模块的⼀部分。默认PFH的实现使⽤5个区间分类。(例如:四个特征值中的每个都使⽤5个区间来统计)
#include //点类型头⽂件
#include //pfh特征估计类头⽂件
...//其他相关操作
pcl::PointCloud<pcl::PointXYZ>::Ptrcloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptrnormals(new pcl::PointCloud<pcl::Normal>());
...//打开点云⽂件估计法线等
//创建PFH估计对象pfh,并将输⼊点云数据集cloud和法线normals传递给它
pcl::PFHEstimation<pcl::PointXYZ,pcl::Normal,pcl::PFHSignature125> pfh;
pfh.setInputCloud(cloud);
pfh.setInputNormals(normals);
//如果点云是类型为PointNormal,则执⾏pfh.setInputNormals (cloud);
//创建⼀个空的kd树表示法,并把它传递给PFH估计对象。
//基于已给的输⼊数据集,建⽴kdtree
pcl::KdTreeFLANN<pcl::PointXYZ>::Ptrtree(new pcl::KdTreeFLANN<pcl::PointXYZ>
());
pfh.setSearchMethod(tree);
//输出数据集
pcl::PointCloud<pcl::PFHSignature125>::Ptrpfhs(new
pcl::PointCloud<pcl::PFHSignature125>());
//使⽤半径在5厘⽶范围内的所有邻元素。
//注意:此处使⽤的半径必须要⼤于估计表⾯法线时使⽤的半径!!!
pfh.setRadiusSearch(0.05);
//计算pfh特征值
pfh.compute(*pfhs);
// pfhs->points.size ()应该与input cloud->points.size ()有相同的⼤⼩,即每个点都有⼀个pfh特征向量
PFH和FPFH计算⽅式之间的主要区别总结如下:
(1)FPFH没有对全互连 点的所有邻近点的计算参数进⾏统计,从图中可以看到,因此可能漏掉了⼀些重要的点对,⽽这些漏掉的对点可能对捕获查询点周围的⼏何特征有贡献。
(2)PFH特征模型是对查询点周围的⼀个精确的邻域半径内,⽽FPFH还包括半径r范围以外的额外点对(不过在2r内);
(3)因为重新权重计算的⽅式,所以FPFH结合SPFH值,重新捕获邻近重要点对的⼏何信息;
(4)由于⼤⼤地降低了FPFH的整体复杂性,因此FPFH有可能使⽤在实时应⽤中;
(5)通过分解三元组,简化了合成的直⽅图。也就是简单⽣成d分离特征直⽅图,对每个特征维度来单独绘制,并把它们连接在⼀起;
#include
#include //fpfh特征估计类头⽂件声明
...//其他相关操作
pcl::PointCloud<pcl::PointXYZ>::Ptrcloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptrnormals(new pcl::PointCloud<pcl::Normal>());
...//打开点云⽂件估计法线等
//创建FPFH估计对象fpfh,并把输⼊数据集cloud和法线normals传递给它。
pcl::FPFHEstimation<pcl::PointXYZ,pcl::Normal,pcl::FPFHSignature33> fpfh;
fpfh.setInputCloud(cloud);
fpfh.setInputNormals(normals);
//如果点云是类型为PointNormal,则执⾏fpfh.setInputNormals (cloud);
//创建⼀个空的kd树对象tree,并把它传递给FPFH估计对象。
//基于已知的输⼊数据集,建⽴kdtree
pcl::search::KdTree<PointXYZ>::Ptrtree(new pcl::search::KdTree<PointXYZ>);
fpfh.setSearchMethod(tree);
//输出数据集
pcl::PointCloud<pcl::FPFHSignature33>::Ptrfpfhs(new
pcl::PointCloud<pcl::FPFHSignature33>());
//使⽤所有半径在5厘⽶范围内的邻元素
//注意:此处使⽤的半径必须要⼤于估计表⾯法线时使⽤的半径!!!
fpfh.setRadiusSearch(0.05);
//计算获取特征向量
fpfh.compute(*fpfhs);
// fpfhs->points.size ()应该和input cloud->points.size ()有相同的⼤⼩,即每个点有⼀个特征向量
视点特征直⽅图(或VFH)是源于FPFH描述⼦。由于它的获取速度和识别⼒,我们决定利⽤FPFH强⼤的识别⼒,但是为了使构造的特征保持缩放不变性的性质同时,还要区分不同的位姿,计算时需要考虑加⼊视点变量。
我们做了以下两种计算来构造特征,以应⽤于⽬标识别问题和位姿估计:
(1)扩展FPFH,使其利⽤整个点云对象来进⾏计算估计(如2图所示),在计算FPFH时以物体中⼼点与物体表⾯其他所有点之间的点对作为计算单元。
(2)添加视点⽅向与每个点估计法线之间额外的统计信息,为了达到这个⽬的,我们的关键想法是在FPFH计算中将视点⽅向变量直接融⼊到相对法线⻆计算当中。
通过统计视点⽅向与每个法线之间⻆度的直⽅图来计算视点相关的特征分量。
注意:并不是每条法线的视⻆,因为法线的视⻆在尺度变换下具有可变性,我们指的是平移视点到查询点后的视点⽅向和每条法线间的⻆度。第⼆组特征分量就是前⾯PFH中讲述的三个⻆度,如PFH⼩节所述,只是现在测量的是在中⼼点的视点⽅向和每条表⾯法线之间的⻆度。
因此新组合的特征被称为视点特征直⽅图(VFH)。包含了两部分:
1.⼀个视点⽅向相关的分量
2.⼀个包含扩展FPFH的描述表⾯形状的分量。
#include
#include //VFH特征估计类头⽂件
...//其他相关操作
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new
pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptr normals (new pcl::PointCloud<pcl::Normal>
());
...//打开点云⽂件估计法线等
//创建VFH估计对象vfh,并把输⼊数据集cloud和法线normal传递给它
pcl::VFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::VFHSignature308> vfh;
vfh.setInputCloud (cloud);
vfh.setInputNormals (normals);
//如果点云是PointNormal类型,则执⾏vfh.setInputNormals (cloud);
//创建⼀个空的kd树对象,并把它传递给FPFH估计对象。
//基于已知的输⼊数据集,建⽴kdtree
pcl::KdTreeFLANN<pcl::PointXYZ>::Ptr tree (new
pcl::KdTreeFLANN<pcl::PointXYZ> ());
vfh.setSearchMethod (tree);
//输出数据集
pcl::PointCloud<pcl::VFHSignature308>::Ptr vfhs (new
pcl::PointCloud<pcl::VFHSignature308> ());
//计算特征值
vfh.compute (*vfhs);
// vfhs->points.size ()的⼤⼩应该是1,即vfh描述⼦是针对全局的特征描述