pcl里面的法线估计
法线估计是一个很重要的特征,常常在被使用在很多计算机视觉的应用里面,比如可以用来推出光源的位置,通过阴影与其他视觉影响。
给一个几何表面,去推断给定点的法线方向,即垂直向量的方向往往是不容易的。然而,在我们获取物体表面的点云数据后,有两大选择:
1.从已获取的点云数据集中得到潜在表面,并用表面网格化技术,来计算网格的表面法线。
2.使用近似值来推断点云数据集的表面法线。
尽管有很多法线估计的方法存在,但是我们这次将会讲的是最简单的方法。表面法线的问题可以近似化解为切面的问题,这个切面的问题又会变成最小二乘法拟合平面的问题。
解决表面法线估计的问题可以最终化简为对一个协方差矩阵的特征向量和特征值的分析(或者也叫PCA-Principal Component Analysis 主成分分析),这个协方差矩阵是由查询点的最近邻产生的。对于每个点Pi,我们假设协方差矩阵C如下:
这里K指的是离点的最近的K个点,是最近邻的中心,是第j个特征值,是第j个特征向量。
下面是一段用来估计协方差矩阵的代码
// Placeholder for the 3x3 covariance matrix at each surface patchEigen::Matrix3f covariance_matrix;// 16-bytes aligned placeholder for the XYZ centroid of a surface patchEigen::Vector4f xyz_centroid;// Estimate the XYZ centroidcompute3DCentroid (cloud, xyz_centroid);// Compute the 3x3 covariance matrixcomputeCovarianceMatrix (cloud, xyz_centroid, covariance_matrix);
总的来说,因为数学上没有方法解决法线的符号,比如一个球面,法线可以指向球心,也可以背向球心。下面的两幅图像是描述厨房的点云图,右边的图像是通过高斯扩展图(EGI Extended Gaussian Image),也常常叫做法线球。法线球是一个描述点云里面所有法线方向的一种方式。因为数据集是2.5D的,何为2.5D,你可以把上下,左右,前后看成一个D,然后现实生活里面往往不可能每个方向都兼顾,比如摄像机只能拍到前面的,所有是1(上下)+1(左右)+0.5(前)叫2.5D,当然这是建立在摄像机为单一视角的情况下,即摄像机不会动,一直是固定的。所以理论上,EGI图,即高斯球也应该是2.5D的,因为你摄像机是向前拍摄的,所以物体的法线也是向前的,然而因为这个算法的原因。主成分分析这个算法,不能解决法线的符号,所以导致了法线指向可能往前,也可能往后,导致整个球里面各个方向都可能存在着法线。
解决上面的法线方向不定的问题,我们得知道视角的向量,这就很简单了,只要法线和 视角与点的连线,这两条线的夹角是锐角,即这两个向量的点积大于0即可。
我们经过上述的处理后,图片就变成了这样
可以看到左边的那副图,法线的指向全都变成一个方向了,同时高斯扩展图只有前半个球面是有法线的,即我们的方法是有效的。
我们可以使用下面的方法去改变法线的方向
flipNormalTowardsViewpoint (const PointT &point, float vp_x, float vp_y, float vp_z, Eigen::Vector4f &normal);
上面的这个方法就像我们刚才说的,只适用于单一视角的情况下。
选择正确的比例
就像前面说的,预测一个表面法线需要最近邻的方法,那么如何设置最近邻所需要的半径与点的个数呢?
这个问题是非常重要的,是对点的特征自动化评估的重要因素。为了更好的阐述这个问题,下面的图将显示选择一个小比例和大比例的影响。左边的图是比例(r和k)比较小的情况,我们发现它的法线是另人满意的,而右边就不尽人意了,你看那个桌角的位置,有一个法线出轨了,是一个小三,不属于上一个表面也不属于侧面,这就是比例选择太大的弊端,所以小比例往往更注重细节,更适合描述比较复杂的物体。
我们得根据不同的细节来选取比例,简单的说,如果边缘的曲率在杯子的把柄和圆柱体之间的时候,比例需要比较小来获取足够的细节。
下面是官方的一段代码段:
#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 itpcl::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 datasetspcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);// Use all neighbors in a sphere of radius 3cmne.setRadiusSearch (0.03);// Compute the featuresne.compute (*cloud_normals);// cloud_normals->points.size () should have the same size as the input cloud->points.size ()* }
NormalEstimation这个类
做了以下3件事
1.得到p的最近邻
2.计算p的表面法线n
3.检查法线的朝向,然后拨乱反正。
默认的视角是(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);
前面两个参数很好理解,plane_parameters包含了4个参数,前面三个是法线的(nx,ny,nz)坐标,加上一个 nc . p_plane (centroid here) + p的坐标,然后最后一个参数是曲率。
接下去是我写的一个代码,先通过从磁盘里面加载一个PCD文件,然后进行降低采样和滤波等操作,最后通过PCLVisulizer显示出来.
#include <iostream>
#include <pcl/visualization/cloud_viewer.h>
#include<pcl/io/pcd_io.h>
#include<pcl/point_types.h>
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/features/normal_3d.h>
#include <boost/thread/thread.hpp>
#include <pcl/common/common_headers.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/console/parse.h>
#include <pcl/filters/statistical_outlier_removal.h>int main ()
{pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_old (new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_downsampled (new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);pcl::io::loadPCDFile ("test_pcd.pcd", *cloud_old);//Use a voxelSampler to downsamplepcl::VoxelGrid<pcl::PointXYZ> voxelSampler;voxelSampler.setInputCloud(cloud_old);voxelSampler.setLeafSize(0.01f, 0.01f, 0.01f);voxelSampler.filter(*cloud_downsampled);//Use a filter to reduce noisepcl::StatisticalOutlierRemoval<pcl::PointXYZ> statFilter;statFilter.setInputCloud(cloud_downsampled);statFilter.setMeanK(10);statFilter.setStddevMulThresh(0.2);statFilter.filter(*cloud);// Create the normal estimation class, and pass the input dataset to itpcl::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 datasetspcl::PointCloud<pcl::Normal>::Ptr normals (new pcl::PointCloud<pcl::Normal>);// Use all neighbors in a sphere of radius 1cmne.setRadiusSearch(0.01);// Compute the featuresne.compute (*normals);boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));viewer->setBackgroundColor (0, 0, 0);viewer->addPointCloud<pcl::PointXYZ> (cloud, "sample cloud");viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");viewer->addPointCloudNormals<pcl::PointXYZ, pcl::Normal> (cloud, normals, 10, 0.2, "normals");viewer->addCoordinateSystem (1.0);viewer->initCameraParameters ();while (!viewer->wasStopped()){viewer->spinOnce (100);boost::this_thread::sleep (boost::posix_time::microseconds (100000));}return 0;
}
因为我的PCD的点云文件里面的点是PointXYZ类型,所以显示得时候,都是白色的,下面上一张效果图。
这是一张桌子,可以看到上面充满了密密麻麻的法线。如果你要下载这个PCD文件,点击下面这个链接。
点击打开链接
pcl里面的法线估计相关推荐
- PCL:点云数据基于法线的边界提取(从最初的法线估计理论推导到最终的边界提取)
该边界提取采用PCL库里边的方法,基于法线估计来实现的边界检测与提取: 首先从原始点云上计算出法线,再由法线结合数据估计出边界.(这样解释还是特别抽像吧) ------------法线求解:(平面的法 ...
- PCL之积分图法线估计
积分图像是对有序点云的发现的估计的一种方法.该算法把点云作为一个深度图像,并创建一定的矩形区域来计算法线,考虑到相邻像素关系,而无需建立树形查询结构.因此,它是非常有效的. 代码展示: #includ ...
- pcl点云特征提取 法线估计 PFH FPFH NARF 惯量偏心矩 RoPs特征 视点特征直方图VFH GASD特征
pcl点云特征提取 法线估计 PFH FPFH NARF 惯量偏心矩 RoPs特征 视点特征直方图VFH GASD特征 博文末尾支持二维码赞赏哦 _ 如果要对一个三维点云进行描述,光有点云的位置是 ...
- PCL学习笔记(30)——法线估计normal_estimation
源码 #include <pcl/io/io.h> #include <pcl/io/pcd_io.h> #include <pcl/features/integral_ ...
- PCL入门系列 —— NormalEstimation、NormalEstimationOMP 基于邻域的点云法线估计
PCL入门系列 -- NormalEstimation.NormalEstimationOMP 基于邻域的点云法线估计 前言 程序说明 输出结果 代码示例 总结 前言 随着工业自动化.智能化的不断推进 ...
- 3D点云系列——pcl:点云平滑及法线估计
通过重采样实现点云平滑 需要平滑的情况: 用RGB-D激光扫描仪等设备扫描物体,尤其是比较小的物体时,往往会有测量误差.这些误差所造成的不规则数据如果直接拿来曲面重建的话,会使得重建的曲面不光滑或者有 ...
- 【点云重采样Resampling】Python-pcl 基于多项式平滑点云及法线估计的曲面重建
1. 点云重采样 基于多项式平滑点云及法线估计的曲面重建以实现重采样,可以使得点云数据更规整一些,没之前那么杂乱. set_Compute_Normals(True) 可以通过在最小二乘法中进行法线估 ...
- PCL:从法线计算到曲率计算并可视化
----------------法线求解原理-------------- 表面法线是几何体表面的重要属性,在很多领域都有大量应用,例如:在进行光照渲染时产生符合可视习惯的效果时需要表面法线信息才能正常 ...
- 点云法线估计:C++实现
点云法线估计原理 一.理论基础 1.为什么要求点云的法线 2.如何估计法向量 二.程序实现 三.运行结果 四.法线估计存在的问题 一.理论基础 1.为什么要求点云的法线 表面法线是几何体表面的重要属性 ...
最新文章
- Linux内存管理之高端内存映射
- js中String的常用扩展
- 干货 | 机器学习入门方法和资料合集
- css文字下滑,CSS3 文本下落渐变动效
- Windows 令人“社死”的新功能,你都知道吗?
- 【opencv学习】【轮廓检测】
- UI加载动效模板|优秀作品给UI设计师做个示范
- 共轭函数Fenchel不等式
- 本地 hosts 文件找不到怎么办
- java 读写 ini 配置文件【IDEA】
- kodi android 目录,Kodi使用豆瓣刮削器建立媒体库,以及把资料库导出到片源目录...
- 基于深度学习的回声消除系统与Pytorch实现
- linux源码编译ipk,openwrt SDK, 利用SDK生成自己的ipk安装包
- php 简繁体,使用php实现简体转繁体的方法
- 对角安装的双舵轮AGV运动学核心算法
- 天津大学关于博士、硕士学位论文格式
- python中format使用
- 充分利用 cpu_如何充分利用云
- 使用vue+golang+mysql写一个即时聊天、多人视频的项目
- 【网络安全】溯源NAT之前的IP地址
热门文章
- springMVC,aop管理log4j,把当前session信息和错误信息打印到日志
- java访问同一个变量_java – 从另一个类访问变量
- fastjson为什么默认是无序的
- 分页插件PageHelper的使用方法
- mysql命令实践_MySQL:常用命令行
- python元组赋值给变量,Python的赋值
- Linux 目录所属组设置,Linux系统用户与组管理命令及配置文件总结
- linux内核计算list的长度,Linux内核通用链表 linux/list.h阅读
- 计算机桌面壁纸怎样拉伸,win10桌面壁纸怎么拉伸?手把手教你拉伸win10桌面壁纸的方法...
- 32f407tim4时钟源频率_慎重选择时钟发生器,别让这俩指标影响你的ADC 「图片」...