使用PCL滤波器实现点云裁剪

news/2024/11/15 8:34:42/

主要目的就是根据已知的ROI区域,对点云进行裁剪。要么留下点云ROI区域,要么去除。
ROI区域一般都是一个矩形,即(x,y,width,height)。
那么封装的函数形式一般如下:

pcl::PointCloud<pcl::PointXYZ>::Ptr CloudClipper(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud,double x,double y, double width, double height)
{// 实现点云滤波// 创建滤波后点云pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>());// 调用filter方法得到滤波后点云return cloud_filtered;
}

比较简单直接粗暴的方法就是使用直通滤波

PCL库中其实有条件滤波的,感觉听起来确实很像想要用的滤波器,于是就尝试了一下

#include <pcl/filters/conditional_removal.h>
pcl::PointCloud<pcl::PointXYZ>::Ptr ConditionFilter(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, double x, double y, double width, double height){//创建条件限定下的滤波器//pcl::ConditionBasepcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond(new pcl::ConditionAnd<pcl::PointXYZ>());//创建条件定义对象//为条件定义对象添加比较算子: 使用大于0.0和小于0.8这两个条件用于建立滤波器。range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(newpcl::FieldComparison<pcl::PointXYZ>("x", pcl::ComparisonOps::GT, x)));//添加在x字段上大于0的比较算子  range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(newpcl::FieldComparison<pcl::PointXYZ>("x", pcl::ComparisonOps::LT, x + width)));//添加在x字段上小于0.8的比较算子range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(newpcl::FieldComparison<pcl::PointXYZ>("y", pcl::ComparisonOps::GT, y)));range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(newpcl::FieldComparison<pcl::PointXYZ>("y", pcl::ComparisonOps::LT, y + height)));//创建滤波器并用条件定义对象初始化pcl::ConditionalRemoval<pcl::PointXYZ> condrem;condrem.setCondition(range_cond);condrem.setInputCloud(cloud);           //设置输入点云condrem.setKeepOrganized(false);         //设置保持点云的结构:为true时被剔除的点为NAN//condrem.setUserFilterValue(0.1f);//condrem.getIndicespcl::IndicesConstPtr inliers = condrem.getRemovedIndices();if (inliers != nullptr){std::cout << "indice number: " << inliers->size() << std::endl;}pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>());condrem.filter(*cloud_filtered);        //执行条件滤波,存储结果到cloud_filteredstd::cout << "filter size: " << cloud_filtered->points.size() << std::endl;/* pcl::IndicesConstPtr inliers = condrem.getRemovedIndices();pcl::copyPointCloud<pcl::PointXYZ>(*cloud, *inliers, *cloud_filtered);*/return cloud_filtered;}

逐行解释:
1.首先需要创建一个条件定义对象,用于设定条件。其实条件主要有两种pcl::ConditionAndpcl::PointT::Ptr和pcl::ConditionOrpcl::PointT::Ptr,前者是与条件,意思是所有条件都要满足,其实就是每个条件得到的点云求交集,后者是或条件,那么就是每个条件滤波结果求并集。可以根据需要进行使用。

2.设置字段,即range_cond->addComparison(pcl::FieldComparisonpcl::PointXYZ::ConstPtr(new
pcl::FieldComparisonpcl::PointXYZ(“x”, pcl::ComparisonOps::GT, x)));中的“x”其实是指点云的点的x坐标值,如果是“r”可以筛选出RGB中R通道的值,那么点云的数据结构必须是PointXYZRGB而不是PointXYZ,这个得注意。另外对于pcl::ComparisonOps::GT和pcl::ComparisonOps::LT,其实GT就是greater than即大于,LT就是less than即小于。

3.//创建滤波器并用条件定义对象初始化
pcl::ConditionalRemovalpcl::PointXYZ condrem;
condrem.setCondition(range_cond);
condrem.setInputCloud(cloud); //设置输入点云
以上几句无非是实例化滤波器对象后,将上述设置好了的条件和点云都输入进去

4.setKeepOrganized则是用于进行条件移除之后是否保持点云的有序性,但是一般处理的点云都是无序点云,大多数情况下这个地方设置为false,但一定要视实际情况而定。如果无序点云中的点之间不存在明确定义的拓扑关系,例如没有明确的连接关系或者边界关系,那么在移除点云中的一些点后,点云的有序属性也会被破坏。此时,即使设置 setKeepOrganized 为 true,输出点云仍然是无序的。

5.setUserFilterValue:该函数用于设置条件移除的阈值参数。对于某些条件(例如欧式距离),需要指定阈值才能进行移除。setUserFilterValue 函数可以设置这个阈值。该函数需要传递一个模板参数,表示阈值的类型,可以是 float、double、int 等。在使用 setUserFilterValue 函数时,应该根据实际情况设置合适的阈值,避免移除过多或者过少的点。

6.条件滤波器还可以得到
condrem.getIndices();
condrem.getRemovedIndices();即得到点的索引和去除点的索引。有的滤波器中有setNegative方法,设置为true时可以得到滤波器滤掉的点,设置为false时可以得到滤波器留下来的点。但是条件滤波器中没有该方法。于是想通过得到去除点的索引,然后再通过pcl::copyPointCloudpcl::PointXYZ(*cloud, *inliers, *cloud_filtered);提取得到滤波器去掉的点。结果发现滤波器得到的索引中size为0,也就是无索引。可能有以下几个原因:

在执行条件滤波操作之前,没有设置条件对象。条件滤波器必须先设置条件对象,才能根据条件对点云进行筛选。如果没有设置条件对象,则条件滤波器会将输入点云中的所有点都保留下来,因此“已移除索引”列表中就没有任何点。设置的条件不满足任何点。如果设置的条件不满足输入点云中的任何点,则条件滤波器不会移除任何点,因此“已移除索引”列表中也就没有任何点。使用的数据类型不正确。条件滤波器的输入点云和条件对象都必须是相同的数据类型,否则条件滤波器会出现异常,导致“已移除索引”列表为空。例如,如果输入点云是 XYZRGB 类型的,而条件对象是 XYZ 类型的,则条件滤波器会出现异常。

其实上述三个情况都没问题。可是为啥size为0呢,是点云数据结构中本身就没有索引嘛???

而使用condrem.getIndices();返回的更是一个nullptr空指针,原因如下:

1.在执行条件滤波操作之前,没有设置输入点云。条件滤波器必须设置输入点云,才能根据条件对点云进行筛选。如果没有设置输入点云,则 getIndices() 函数返回的指针是 nullptr。

2.条件滤波器没有将任何点移除。如果设置的条件不满足输入点云中的任何点,或者输入点云本身已经满足条件,条件滤波器不会移除任何点,因此 getIndices() 函数返回的指针是 nullptr。

3.没有启用索引输出。条件滤波器默认情况下不会输出被移除点的索引。如果要输出被移除点的索引,需要在条件滤波器上启用索引输出,即pcl::ConditionalRemovalpcl::PointXYZ condrem;
condrem.setKeepOrganized(true); // 启用索引输出
将setKeepOrganized设置为true后,getIndices返回的指针不为空了,但是size为输入点云的数量。而且!!!此时竟然无法滤除点云了。
在这里插入图片描述
后面发现其实pcl::IndicesPtr outliers = condrem.getIndices();放在condrem.filter(*cloud_filtered);后面之后,返回得指针也不为空,但是其size还是和输入的点云中点的size一致。

所以,目前通过条件滤波可以得到从点云中剪裁下来的点云,但是无法获取到除了剪裁下来的点之外的点。

使用crop_box进行剪裁

代码如下,可以直接使用

pcl::PointCloud<pcl::PointXYZ>::Ptr cropclipper3D(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, double x, double y, double width, double height){pcl::PointXYZ min_point(x, y, -100);pcl::PointXYZ max_point(x + width, y + height, 100);Eigen::Vector4f minpt(x, y, -100, 1);Eigen::Vector4f maxpt(x + width, y + height, 100, 1);pcl::CropBox<pcl::PointXYZ> crop_box;crop_box.setMin(minpt);crop_box.setMax(maxpt);// 将点云限制在 3D 盒子内部或者外部,并保存输出点云pcl::PointCloud<pcl::PointXYZ>::Ptr clipped_cloud(new pcl::PointCloud<pcl::PointXYZ>);crop_box.setInputCloud(cloud);crop_box.setNegative(true);crop_box.filter(*clipped_cloud);return clipped_cloud;}

这个滤波器需要先确定box的两个坐标点,这两个点得是对角线上两个点(一个长方体距离最远的两个点)。然后这个滤波器就有我们之前提到的setNegative方法,就可以按照意愿来获取ROI内的点还是区域外的点。效果如下图所示,感觉还行
在这里插入图片描述
在这里插入图片描述
运行时间大概是44ms,这个速度感觉还好,如果能更快就好了。一块平面点云上如果有很多区域要提取或者裁剪掉,可以考虑多线程。

紧接着,想要研究BoxClipper3D的使用方法,但是找了好久终于在github上找到了,戳这里查看!


http://www.ppmy.cn/news/49014.html

相关文章

有始有终的编码原则

基本情况 在程序员的修炼之道之中&#xff0c;说到&#xff1a; 这个建议能简单地应用到大多数场合。简单说就是&#xff0c;分配资源的函 数或对象&#xff0c;对释放资源应负有责任。 这其实就是我们常说的谁分配的就谁负责释放&#xff0c;这也是内存释放的一个原则&#x…

18、定制化原理

文章目录 1、定制化的常见方式2、原理分析套路 【尚硅谷】SpringBoot2零基础入门教程-讲师&#xff1a;雷丰阳 笔记 路还在继续&#xff0c;梦还在期许 1、定制化的常见方式 ● 修改配置文件&#xff1b; ● xxxxxCustomizer&#xff1b; ● 编写自定义的配置类 xxxConfigurat…

ROS学习第二十九节——URDF之joint

此处留疑问&#xff0c;link,joint的origin子标签到底是怎么样的一种位置关系&#xff1f;&#xff1f;&#xff1f; https://download.csdn.net/download/qq_45685327/87717336 urdf 中的 joint 标签用于描述机器人关节的运动学和动力学属性&#xff0c;还可以指定关节运动的…

WIFI-OmniPeek抓包

一、简介 有时候&#xff0c;我们需要抓取空口数据包来分析数据&#xff0c;此时就需要了解抓包软件如何使用。此时需要准备如下东西&#xff1a; SNIFFER&#xff1a;AC-12000 软件&#xff1a;OmniPeek 二&#xff1a;抓包 打开OmniPeek&#xff0c;新建捕获。 确认设备是否…

面试华为,花了2个月才上岸,真的难呀····

花2个月时间面试一家公司&#xff0c;你们觉得值吗&#xff1f; 背景介绍 美本计算机专业&#xff0c;代码能力一般&#xff0c;之前有过两段实习以及一个学校项目经历。第一份实习是大二暑期在深圳的一家互联网公司做前端开发&#xff0c;第二份实习由于大三暑假回国的时间比…

华为,找寻科技秋天里的春光

捷克导演伏拉基米尔米切尔在2001年指导了一部电影&#xff0c;叫做《秋天里的春光 Bab lto》。 我很喜欢这个名字&#xff0c;它吐露着一种简单质朴的美好。回望历史&#xff0c;我们会发现文明与知识经常陷入秋天&#xff0c;但却因为人类的一次次努力&#xff0c;最终我们迎来…

Pandas 练习, 常见功能查阅

Pandas 安装 pandas 库: conda install pandas数据 git clone https://github.com/KeithGalli/pandas.git练习 import pandas as pddata_dir "/data_dir"df pd.read_csv(f{data_dir}/pandas/pokemon_data.csv) df.shape(800, 12)df.head()#NameType 1Type 2HPA…

C++设计模式:面试题精选集

目录标题 引言&#xff08;Introduction&#xff09;面试的重要性设计模式概述面试题的选择标准 设计模式简介 面试题解析&#xff1a;创建型模式&#xff08;Creational Patterns Analysis&#xff09;面试题与解答代码实例应用场景分析 面试题解析&#xff1a;结构型模式&…