PCL 提取点云边界轮廓-AC方法、平面轮廓

一、概述

PCL点云边界特征检测 (附完整代码 C++)_pcl计算点云特征值_McQueen_LT的博客-CSDN博客

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

二、AC方法步骤讲解

 

三、AC方法代码

原始点云:

计算步骤

1、计算点云的法向量

2、计算点云的边界

3、显示

代码


int PointCloudBoundary2(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{

	// 1 计算法向量
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
	pcl::PointCloud<pcl::Normal>::Ptr  normals(new  pcl::PointCloud<pcl::Normal>);
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normalEstimation;
	normalEstimation.setInputCloud(cloud);
	normalEstimation.setSearchMethod(tree);
	normalEstimation.setRadiusSearch(0.02);  // 法向量的半径
	normalEstimation.compute(*normals);

	/*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(normals); //设置输入法线
	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中
	
	cout << "边界点云的点数   :  "<< boundaries->size()<< endl;


	/*可视化*/
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_visual(new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_boundary(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;
			cloud_boundary->push_back(cloud_visual->points[i]);
		}
		else
		{
			cloud_visual->points[i].r = 255;
			cloud_visual->points[i].g = 255;
			cloud_visual->points[i].b = 255;
		}
	}
	pcl::io::savePCDFileBinaryCompressed("D:\\work\\Pointclouds\\clouds\\all.pcd", *cloud_visual);
	pcl::io::savePCDFileBinaryCompressed("D:\\work\\Pointclouds\\clouds\\boundaries.pcd", *cloud_boundary);
	return 0;
}

显示

	boundary_estimation.setKSearch(30); //设置k近邻数量
	boundary_estimation.setAngleThreshold(M_PI * 0.6); //设置角度阈值,大于阈值为边界
	boundary_estimation.compute(*boundaries); //计算点云边界,结果保存在boundaries中


M_PI*0.6 

M_PI*0.8

四、平面轮廓

原始点云:

步骤:

1、采用RANSAC提取平面

2、提取平面

代码:


int PointCloudBoundary2(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{

	// 1 计算法向量
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
	pcl::PointCloud<pcl::Normal>::Ptr  normals(new  pcl::PointCloud<pcl::Normal>);
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normalEstimation;
	normalEstimation.setInputCloud(cloud);
	normalEstimation.setSearchMethod(tree);
	normalEstimation.setRadiusSearch(0.02);  // 法向量的半径
	normalEstimation.compute(*normals);

	/*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(normals); //设置输入法线
	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.8); //设置角度阈值,大于阈值为边界
	boundary_estimation.compute(*boundaries); //计算点云边界,结果保存在boundaries中
	
	cout << "边界点云的点数   :  "<< boundaries->size()<< endl;


	/*可视化*/
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_visual(new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_boundary(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;
			cloud_boundary->push_back(cloud_visual->points[i]);
		}
		else
		{
			cloud_visual->points[i].r = 255;
			cloud_visual->points[i].g = 255;
			cloud_visual->points[i].b = 255;
		}
	}
	pcl::io::savePCDFileBinaryCompressed("D:\\work\\Pointclouds\\clouds\\all22.pcd", *cloud_visual);
	pcl::io::savePCDFileBinaryCompressed("D:\\work\\Pointclouds\\clouds\\boundaries222.pcd", *cloud_boundary);
	return 0;
}
/// <summary>
///   先提取点云的平面然后在进行边界林廓
/// </summary>
/// <param name="cloud"></param>
/// <returns></returns>
int  PlaneCloudContour(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{

	// 0 计算法向量
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
	pcl::PointCloud<pcl::Normal>::Ptr  normals(new  pcl::PointCloud<pcl::Normal>);
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normalEstimation;
	normalEstimation.setInputCloud(cloud);
	normalEstimation.setSearchMethod(tree);
	normalEstimation.setRadiusSearch(0.02);  // 法向量的半径
	normalEstimation.compute(*normals);


	 // 1 提出出平面 
		// 提取平面点云的索引
	pcl::PointIndices::Ptr  index_plane(new pcl::PointIndices);
	pcl::SACSegmentationFromNormals<pcl::PointXYZ, pcl::Normal> sacSegmentationFromNormals;
	pcl::ModelCoefficients::Ptr mdelCoefficients_plane(new pcl::ModelCoefficients);
	sacSegmentationFromNormals.setInputCloud(cloud);
	sacSegmentationFromNormals.setOptimizeCoefficients(true);//设置对估计的模型系数需要进行优化
	sacSegmentationFromNormals.setModelType(pcl::SACMODEL_NORMAL_PLANE); //设置分割模型
	sacSegmentationFromNormals.setNormalDistanceWeight(0.1);//设置表面法线权重系数
	sacSegmentationFromNormals.setMethodType(pcl::SAC_RANSAC);//设置采用RANSAC作为算法的参数估计方法
	sacSegmentationFromNormals.setMaxIterations(500); //设置迭代的最大次数
	sacSegmentationFromNormals.setDistanceThreshold(0.05); //设置内点到模型的距离允许最大值
	sacSegmentationFromNormals.setInputCloud(cloud);
  	sacSegmentationFromNormals.setInputNormals(normals);
	sacSegmentationFromNormals.segment(*index_plane, *mdelCoefficients_plane);
	std::cerr << "Plane coefficients: " << *mdelCoefficients_plane << std::endl;

	 // 点云提取
	pcl::ExtractIndices<pcl::PointXYZ> extractIndices;
	pcl::PointCloud<pcl::PointXYZ>::Ptr  cloud_p(new pcl::PointCloud<pcl::PointXYZ>);
	extractIndices.setInputCloud(cloud);
	extractIndices.setIndices(index_plane);
	extractIndices.setNegative(false);
	extractIndices.filter(*cloud_p);
	pcl::io::savePCDFileBinaryCompressed("D:\\work\\Pointclouds\\clouds\\planeCloud.pcd",*cloud_p);
	 // 2  
	PointCloudBoundary2(cloud_p);
	return 0;
}

 结果:

平面点云的轮廓线计算-alpha shapes算法原理和实现_α-shape算法-CSDN博客

双阈值Alpha Shapes算法提取点云建筑物轮廓研究 - 豆丁网

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/150960.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++初阶】STL详解(一)string类

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…

【Hello Go】Go语言基础类型

Go语言基础类型 基础类型命名变量变量声明变量初始化变量赋值匿名变量 常量字面常量常量定义iota枚举 基础数据类型分类 fmt包的标准输入输出格式说明输入类型转换类型取别名 基础类型 命名 Go语言中的命名遵循下面的几个规则 必须以字母或者是下划线开头不能使用Go语言中的…

Django——模板层、模型层

模板层 一. 模版语法 {{ }}: 变量相关 {% %}: 逻辑相关 1. 注释是代码的母亲 {# ... #} 2. 基本数据类型传值 int1 123 float1 11.11 str1 我也想奔现 bool1 True list1 [小红, 姗姗, 花花, 茹茹] tuple1 (111, 222, 333, 444) dict1 {username: jason, age: 18, i…

单片机的冷启动、热启动、复位

一文看懂STC单片机冷启动和复位有什么区别-电子发烧友网 单片机的冷启动、热启动和复位是不同的启动或重置方式&#xff0c;它们在系统状态和初始化方面有所不同&#xff1a; 1.冷启动&#xff08;Cold Start&#xff09;&#xff1a; 定义&#xff1a; 冷启动是指系统从完全关…

第14届蓝桥杯青少组python试题解析:22年10月选拔赛

选择题 T1. 执行print (5%3) 语句后&#xff0c;输出的结果是 ( ) 0 1 2 3 T2. 以下选项中&#xff0c;哪一个是乘法运算符?&#xff08;&#xff09; % // * ** T3. 已知x3&#xff0c;求x//2x**2的运算结果? 7.5 10 8 10.5 T4. 以下选项中&#xff0c;对下面程序的打印…

Unity地面交互效果目录

大家好&#xff0c;我是阿赵。   之前写了几篇关于地形交互、地面轨迹、脚印效果实现的博文。虽然写的篇数不多&#xff0c;但里面也包含了不少基础知识&#xff0c;比如局部UV采样、法线动态混合、曲面细分等知识&#xff0c;这些都是可以和别的效果组合在一起&#xff0c;做…

【刷题专栏—突破思维】142. 环形链表 II

前言&#xff1a;本篇博客将讲解三个OJ题&#xff0c;前两个作为铺垫&#xff0c;最后完成环形链表的节点的寻找 文章目录 一、160. 相交链表二、141. 环形链表三、142. 环形链表II 一、160. 相交链表 题目链接&#xff1a;LeetCode—相交链表 题目描述&#xff1a; 给你两个单…

排查线程阻塞问题

案例代码 package first;import java.util.concurrent.TimeUnit;public class DeadLock {private static volatile Object lock new Object();public static void main(String[] args) {new Thread(() -> {test1();}).start();new Thread(() -> {test2();}).start();}p…

队列的实现---超详细

队列的实现—超详细 文章目录 队列的实现---超详细一、队列的模型二、代码实现以及测试用例①队列初始化②入队③出队④输出队头⑤输出队尾⑥判断队列是否为空⑦队列的长度⑧队列的销毁⑨测试用例 一、队列的模型 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在…

Semantic Kernel 学习笔记2

本来想白瞟免费Bing Search API如下&#xff0c;但是报错无法链接利用免费的必应 Bing 自定义搜索打造站内全文搜索_bing_subscription_key-CSDN博客 改成按照官方推荐申请&#xff0c;并在.env文件中添加BING_API_KEY""字段。 1. 打开https://www.microsoft.com/en-…

《QT从基础到进阶·二十七》进度条QProgressBar

ui.ProgressBar.setValue(45); //45% ui.ProgressBar.setMin(0); ui.ProgressBar.setMax(255);0到100分为255份&#xff0c;值为215时&#xff0c;进度条为100/255*215 84% 点击主界面弹出进度条QProgressDialog 常用功能&#xff1a; setWindowFlags(Qt::Dialog | Qt::Cu…

基于象群算法优化概率神经网络PNN的分类预测 - 附代码

基于象群算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于象群算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于象群优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…

【C语言】自定义类型:结构体、枚举、联合

简单不先于复杂&#xff0c;而是在复杂之后 文章目录 1. 结构体的声明1.1 结构的基础知识1.2 结构的声明1.3 特殊的声明1.4 结构体的自引用1.5 结构体变量的定义和初始化1.6 结构体内存对齐1.7 修改默认对齐数1.8 结构体传参 2. 位段2.1 什么是位段2.2 位段的内存分配2.3 位段的…

解决:java: 错误: 不支持发行版本 5 最有效方法

报错信息如图&#xff1a; 直接上终极方法&#xff1a; 修改配置文件 如图找到settings.xml文件 在标签中间插入如下代码&#xff08;jdk更改为自己电脑上的版本&#xff09; <profile><id>development</id><activation><jdk>11</jdk><…

深入Rust:探索所有权和借用机制

大家好&#xff01;我是lincyang。 今天&#xff0c;我们将一起深入探索Rust语言中的一个核心概念&#xff1a;所有权和借用机制。 这些特性是Rust区别于其他语言的重要特点&#xff0c;它们在内存管理和并发编程中扮演着关键角色。 一、Rust所有权机制 1. 什么是所有权&#x…

USB复合设备构建CDC+HID鼠标键盘套装

最近需要做一个小工具&#xff0c;要用到USB CDCHID设备。又重新研究了一下USB协议和STM32的USB驱动库&#xff0c;也踩了不少坑&#xff0c;因此把代码修改过程记录一下。 开发环境&#xff1a; ST-LINK v2 STM32H743开发板 PC windows 11 cubeMX v6.9.2 cubeIDE v1.13.2 cub…

Google 向中国开发者开放数百份 TensorFlow 资源

Google 的机器学习框架 TensorFlow 自 2015 年开源后&#xff0c;已然成为 AI 领域最受欢迎的框架。 据统计&#xff0c;在广受欢迎的 Python 编程语言在线软件知识库 PyPi 上&#xff0c;TensorFlow 的下载次数已超过 90 万&#xff0c;其中有 15% 来自中国。谷歌官方博客也表…

11.10~11.15置信区间,均值、方差假设检验,正态,t,卡方,F分布,第一第二类错误

置信度&#xff0c;置信区间 给定一个置信度&#xff0c;就可以算出一个置信区间。 如果给的置信度越大&#xff0c;那么阿尔法就越小 给的置信度越小&#xff0c;那么α就越大&#xff0c;那么 考虑精确性&#xff0c;希望区间长度尽可能小&#xff0c;所以是取正态的中间…

Postman的常规断言/动态参数断言/全局断言

近期在复习Postman的基础知识&#xff0c;在小破站上跟着百里老师系统复习了一遍&#xff0c;也做了一些笔记&#xff0c;希望可以给大家一点点启发。 断言&#xff0c;包括状态码断言和业务断言&#xff0c;状态码断言有一个&#xff0c;业务断言有多个。 一&#xff09;常规的…

Python数据容器之(元组)

我们前面所了解的列表是可以修改的&#xff0c;但如果想要传递的信息&#xff0c;不被篡改&#xff0c;列表就不合适了。 元组同列表一样&#xff0c;都是可以封装多个、不同类型的元素在内。 但最大的不同点在于&#xff1a; 元组一旦定义完成&#xff0c;就不可修改 所以…