PCL点云边界特征检测 (附完整代码 C++)

一、概述

点云特征在定义上(以我个人理解)大致可以分为两大类:

一类是类似于深度学习的featrue map意义,通过计算一些算子来描述点云局部,这种描述只是一种标识符,并没有实际的几何意义,比如 PFH或者 FPFH 之类的,它们只是通过对每个点的局部邻域计算一个 125 125 125 维或者 33 33 33 维的向量来描述当前点,这跟机器学习中的 f e a t u r e feature feature 是一样的,通过这类特征可以用来做点云的配准(其中某些特征可以进一步处理【模式识别】得到几何特征,因为某一类的几何特征在这种 f e a t u r e feature feature的表现形式上通常都会有某种固定的模式);

而另一类则是有实际的几何意义的,那么这类特征就是叫做几何特征了,通常几何特征包括 { 边界特征,折痕特征,尖锐点 } 等,其中 折痕特征 又包含了 { 凹特征【谷线】和 凸特征【脊线】}。对于一个模型,如果有了几何特征,那么我们就可以得到这个模型的轮廓或者框架,这对模型的压缩表达是非常有意义的。而这类几何特征只要通过合理的处理当然也可以用在特征匹配上。

二、边界特征检测

目前,在点云的边界特征检测(网格模型的边界特征检测已经是一个确定性问题了,见 网格模型边界检测)方面,PCL中有一个针对点云边界的可以称作为是 s t a t e − o f − t h e − a r t state-of-the-art stateoftheart的方法,这个方法出自 D e t e c t i n g   H o l e s   i n   P o i n t   S e t   S u r f a c e s Detecting\space Holes\space in\space Point\space Set\space Surfaces Detecting Holes in Point Set Surfaces这篇论文,叫做 A n g l e   C r i t e r i o n Angle\ Criterion Angle Criterion,简称 A C AC AC。这篇论文中还提出了另一种检测边界的方法是 H a l f d i s c   C r i t e r i o n Halfdisc\ Criterion Halfdisc Criterion H d C HdC HdC。目前PCL中应该只集成了 A C AC AC,因为这个方法确实比 H d C HdC HdC好,已经够用了。这两种方法的思路都非常简单,但是却非常有效,而往往流传下来的经典方法都是这种简单有效的方法。本文只介绍 A C AC AC方法。

1. AC方法思路

  • 假设当前点为 p p p,对其邻域 N p N_p Np向其切平面投影,注意经过投影后 p p p的位置并没有改变;
  • 然后将投影后的近邻点以 p p p为角点,按照逆时针顺序(当然顺时针也可),两两与 p p p连接形成一系列夹角 Θ = { θ 1 , θ 2 , … , θ n } , 其 中 n = ∥ N p ∥ − 1 \Theta=\{\theta_1,\theta_2, \dots,\theta_n\},其中n = \|N_p\| - 1 Θ={θ1,θ2,,θn}n=Np1(因为 p ∈ N p p\in N_p pNp);
  • 找到 Θ \Theta Θ中最大的夹角 θ m a x = m a x ( Θ ) \theta_{max}=max(\Theta) θmax=max(Θ)
  • θ m a x \theta_{max} θmax越大,那么 p p p就越可能为边界点。可以通过设置一个角度阈值 ξ \xi ξ,当 θ m a x > ξ \theta_{max}>\xi θmax>ξ 时,判定 p p p为边界点。
    在这里插入图片描述
    如图所示,第一行为非边界点邻域经过投影后,近邻点与当前点 p p p形成一系列夹角的示意图,其中红色点为 p p p。第二行为边界点邻域经投影后形成夹角的示意图。可以看到当 p p p为边界点时,其近邻与之形成的最大夹角比 p p p为非边界点时形成的最大夹角要大很多,所以可以通过这个性质来对 p p p进行判定。

2. 代码

	#include <pcl/features/boundary.h>
	#include <pcl/visualization/cloud_viewer.h>
	#include <pcl/io/ply_io.h>
	
	int main()
	{
		/*输入点云和法线*/
		pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
		pcl::PointCloud<pcl::Normal>::Ptr normal(new pcl::PointCloud<pcl::Normal>);
		pcl::PLYReader reader;
		reader.read("*.ply", *cloud);
		reader.read("*.ply", *normal);
		
		/*pcl计算边界*/
		pcl::PointCloud<pcl::Boundary>::Ptr boundaries(new pcl::PointCloud<pcl::Boundary>); //声明一个boundary类指针,作为返回值
		boundaries->resize(cloud->size()); //初始化大小
		pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> boundary_estimation; //声明一个BoundaryEstimation类
		boundary_estimation.setInputCloud(cloud); //设置输入点云
		boundary_estimation.setInputNormals(normal); //设置输入法线
		pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree_ptr(new pcl::search::KdTree<pcl::PointXYZ>); 
		boundary_estimation.setSearchMethod(kdtree_ptr); //设置搜寻k近邻的方式
		boundary_estimation.setKSearch(30); //设置k近邻数量
		boundary_estimation.setAngleThreshold(M_PI * 0.6); //设置角度阈值,大于阈值为边界
		boundary_estimation.compute(*boundaries); //计算点云边界,结果保存在boundaries中

		/*可视化*/
		pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_visual(new pcl::PointCloud<pcl::PointXYZRGB>);
		cloud_visual->resize(cloud->size());
		for(size_t i = 0; i < cloud->size(); i++)
		{
			cloud_visual->points[i].x = cloud->points[i].x;
			cloud_visual->points[i].y = cloud->points[i].y;
			cloud_visual->points[i].z = cloud->points[i].z;
			if(boundaries->points[i].boundary_point != 0)
			{
				cloud_visual->points[i].r = 255;
				cloud_visual->points[i].g = 0;
				cloud_visual->points[i].b = 0;
			}
			else
			{
				cloud_visual->points[i].r = 255;
				cloud_visual->points[i].g = 255;
				cloud_visual->points[i].b = 255;
			}
		}
		pcl::visualization::CloudViewer viewer("view");
		viewer.showCloud(cloud_visual);
		system("PAUSE");
		
		return 0;
	}

3. AC边界检测结果

在这里插入图片描述

  • 27
    点赞
  • 207
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
以下是一个简单的 PCL 点云变化检测C++ 代码示例: ```cpp #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/registration/icp.h> #include <pcl/filters/voxel_grid.h> #include <pcl/visualization/pcl_visualizer.h> int main(int argc, char** argv) { // 读取第一个点云 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>); if (pcl::io::loadPCDFile<pcl::PointXYZ>("cloud_in.pcd", *cloud_in) == -1) { PCL_ERROR("Couldn't read file cloud_in.pcd! \n"); return (-1); } std::cout << "Loaded " << cloud_in->size() << " data points from cloud_in.pcd" << std::endl; // 读取第二个点云 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out(new pcl::PointCloud<pcl::PointXYZ>); if (pcl::io::loadPCDFile<pcl::PointXYZ>("cloud_out.pcd", *cloud_out) == -1) { PCL_ERROR("Couldn't read file cloud_out.pcd! \n"); return (-1); } std::cout << "Loaded " << cloud_out->size() << " data points from cloud_out.pcd" << std::endl; // 下采样 pcl::VoxelGrid<pcl::PointXYZ> sor; sor.setInputCloud(cloud_in); sor.setLeafSize(0.01f, 0.01f, 0.01f); sor.filter(*cloud_in); std::cout << "cloud_in after filtering: " << cloud_in->size() << std::endl; sor.setInputCloud(cloud_out); sor.filter(*cloud_out); std::cout << "cloud_out after filtering: " << cloud_out->size() << std::endl; // ICP 配准 pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp; icp.setInputSource(cloud_in); icp.setInputTarget(cloud_out); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_icp(new pcl::PointCloud<pcl::PointXYZ>); icp.align(*cloud_icp); std::cout << "ICP has converged with score: " << icp.getFitnessScore() << std::endl; // 可视化 pcl::visualization::PCLVisualizer viewer("ICP demo"); int v1(0), v2(0); viewer.createViewPort(0.0, 0.0, 0.5, 1.0, v1); viewer.createViewPort(0.5, 0.0, 1.0, 1.0, v2); viewer.setBackgroundColor(0, 0, 0, v1); viewer.setBackgroundColor(0.05, 0.05, 0.05, v2); viewer.addPointCloud(cloud_in, "cloud_in", v1); viewer.addPointCloud(cloud_out, "cloud_out", v2); viewer.addPointCloud(cloud_icp, "cloud_icp", v2); viewer.spin(); return 0; } ``` 该代码使用 PCL 库实现点云的下采样和 ICP 配准,并使用 PCL 可视化工具可视化结果。请注意,此示例仅针对简单的场景,实际应用中需要根据具体情况进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值