PCL点云处理:计算点云法向量并可视化

Posted 没事就要敲代码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PCL点云处理:计算点云法向量并可视化相关的知识,希望对你有一定的参考价值。

1 点云法向量(法线)估计

PCL法线估计是通过寻找待估计点与其邻域点共同构成的局部平面的法向量实现的。通过主成分分析,最小特征向量就是该点法向量,通常法向量具有二义性,需要指定视点 V p V_p Vp 进行法向量定向。

对所有法线 n i n_i ni 定向,只需要使它们一致指向视点方向,满足下面的方程:
n i ⋅ ( V p − P i ) > 0 n_i·(V_p-P_i)>0 ni(VpPi)>0

邻域点的选择方式有两种,一种是K近邻,指定邻域点的数量;一种是R近邻,指定邻域半径。

K近邻

pcl::Feature::setKSearch

R近邻

pcl::Feature::setRadiusSearch

2 法线估计与可视化

代码:

#include <iostream>
#include <pcl\\io\\pcd_io.h>
#include <pcl\\features\\normal_3d.h>
#include <pcl\\visualization\\cloud_viewer.h>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);

using namespace std;

typedef pcl::PointXYZ PointT;

int main()
{
	//--------------------------- 加载点云 ---------------------------
	pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
	if (pcl::io::loadPCDFile("table.pcd", *cloud) < 0)
	{
		PCL_ERROR("->点云文件不存在!\\a\\n");
		system("pause");
		return -1;
	}
	//===============================================================
	
	
	//--------------------------- 法线估计 ---------------------------
	pcl::NormalEstimation<PointT, pcl::Normal> ne;		//创建法线估计对象
	ne.setInputCloud(cloud);							//设置法线估计输入点云
	pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>());	//创建一个空的kdtree
	ne.setSearchMethod(tree);													//将空kdtree传递给法线估计对象 ne
	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);//法向量计算结果
	ne.setKSearch(10);			//设置K近邻的个数
	//ne.setRadiusSearch(0.05);	//设置半径邻域的大小,两种方式二选一
	ne.setViewPoint(0, 0, 1);	//设置视点向量,默认0向量(0,0,0),没有方向
	ne.compute(*normals);		//执行法线估计,并将结果保存到normals中
	//===============================================================


	//-------------------------- 法线可视化 --------------------------
	boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer")); //创建视窗对象,定义标题栏名称“3D Viewer”
	viewer->addPointCloud<PointT>(cloud, "original_cloud");	//将点云添加到视窗对象中,并定义一个唯一的ID“original_cloud”
	viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0.5, "original_cloud"); //点云附色,三个字段,每个字段范围0-1
	viewer->addPointCloudNormals<PointT, pcl::Normal>(cloud, normals, 10, 0.05, "normals");	//每十个点显示一个法线,长度为0.05

	while (!viewer->wasStopped())
	{
		viewer->spinOnce(100);
		boost::this_thread::sleep(boost::posix_time::microseconds(100000));
	}
	//===============================================================
	
	return 0;
}

结果展示:

要计算一个点的法线,可使用 computePointNormal 函数:

  • 输入参数:原始点云cloud、要估计法线的点的索引 indices
  • 输出参数:法线plane_parameters(nx, ny, nz, nc)、曲率curvature
computePointNormal (const pcl::PointCloud<PointInT> &cloud, const std::vector<int> &indices, Eigen::Vector4f &plane_parameters, float &curvature);

3 OpenMP加速法线估计

法线估计是一个比较耗时的操作,PCL提供了OpenMP 加速法线估计的方法,在8核处理器下,可提高6~8倍。

代码:

#include <iostream>
#include <pcl\\io\\pcd_io.h>
#include <pcl\\features\\normal_3d.h>
#include <pcl\\features\\normal_3d_omp.h>
#include <pcl\\console\\time.h>

using namespace std;

typedef pcl::PointXYZ PointT;

int main()
{
	//--------------------------- 加载点云 ---------------------------
	pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
	if (pcl::io::loadPCDFile("table.pcd", *cloud) < 0)
	{
		PCL_ERROR("->点云文件不存在!\\a\\n");
		system("pause");
		return -1;
	}
	cout << "->加载点云个数:" << cloud->points.size() << endl;
	//===============================================================
	
	pcl::console::TicToc time;
	time.tic();
	//--------------------------- 法线估计 ---------------------------
	pcl::NormalEstimation<PointT, pcl::Normal> ne;		//创建法线估计对象
	ne.setInputCloud(cloud);							//设置法线估计输入点云
	pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>());	//创建一个空的kdtree
	ne.setSearchMethod(tree);													//将空kdtree传递给法线估计对象 ne
	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);//法向量计算结果
	ne.setKSearch(10);			//设置K近邻的个数
	//ne.setRadiusSearch(0.05);	//设置半径邻域的大小,两种方式二选一
	ne.setViewPoint(0, 0, 1);	//设置视点向量,默认0向量(0,0,0),没有方向
	ne.compute(*normals);		//执行法线估计,并将结果保存到normals中
	//===============================================================
	double t1 = time.toc() / 1000;
	cout << "->法线估计用时:" << t1 << " s" << endl;


	pcl::console::TicToc time_omp;
	time_omp.tic();
	//---------------------- OpenMP加速法线估计 ----------------------
	pcl::NormalEstimationOMP<PointT, pcl::Normal> omp;
	omp.setInputCloud(cloud);
	pcl::search::KdTree<PointT>::Ptr tree_omp(new pcl::search::KdTree<PointT>());
	omp.setSearchMethod(tree_omp);
	omp.setNumberOfThreads(8);		//设置线程数
	omp.setKSearch(10);
	ne.setViewPoint(0, 0, 1);
	ne.compute(*normals);
	//===============================================================
	double t_omp = time_omp.toc() / 1000;
	cout << "->OpenMP加速法线估计用时:" << t_omp << " s" << endl;

	cout << "->OpenMP效率是原来的 " << t1 / t_omp << " 倍" << endl;

	return 0;
}

输出结果:

->加载点云个数:41049
->法线估计用时:3.888 s
->OMP加速法线估计用时:3.569 s
->OMP效率是原来的 1.08938

实际加速效果并不明显!!!


相关链接:

PCL可视化点云法向量异常Errer:no override found for “VtkActor“

PCL:点云可视化(简单可视化、单视口、多视口可视化)

以上是关于PCL点云处理:计算点云法向量并可视化的主要内容,如果未能解决你的问题,请参考以下文章

求出点云法向量有啥作用

点云特征——法向量估计

PCL基于法线的差异来分割点云

PCL点云处理:getAngle3D 计算两个向量的夹角

为啥循环和可视化点云滞后于 PCL 可视化器?

PCL点云数据处理基础❤️❤️❤️目录