《点云处理》 提取点云内点和外点

前言

关于内点(inliers)和外点(outliers)在点云处理方向上是个非常常见的名词。有时候,内点也会被称之为有效点,而外点会被称之为无效点。所谓有效和无效都是相对而言的,无效不一定是真的没有意义,并不等价于噪点,而有效也并不是绝对是想要的。有时候,可能既要内点,也要外点。之所以这么称呼,是想要从一整个大块的点云中将其分开来。通常,内点和外点具备不同的特征或者属性,根据这个属性,总能找到相应的算法将其分离。

举个例子,使用RANSAC进行平面拟合时,通常会设置一个距离阈值distance。RANSAC每次迭代都会从点云中任意取3个点,3个点在空间中确定了唯一的平面,可以得到平面方程。随即,遍历点云,计算每个点到该平面的欧氏距离,若距离大于设定的阈值distance,那么该点则是外点,这个点被认为是距离平面比较“远”的点,而点到平面的距离小于设定的阈值,则该点是内点,这个点被认为是距离平面比较“近”的点,或者干脆说在误差允许的范围内,该点在拟合的平面上。通常运行RANSAC算法后,会得到一个std::vector inliers动态数组,该数组中存放的一般就是内点的索引。

当然,除了拟合平面外,还有很多这样的例子,如欧式聚类。接下来就汇总一下,得到inliers之后,如何提取内点点云和外点点云。

方法一:

/// <summary>
/// 根据索引提取点云中的内点或者外点
/// </summary>
/// <param name="cloud">输入点云</param>
/// <param name="inliers">存放内点索引的数组</param>
/// <param name="cloud_out">输出点云</param>
/// <param name="is_true">输入true则提取内点点云,输入false则提取外点点云</param>
/// <returns>return true则表示提取成功,return false则表示提取失败</returns>
bool GetCloudByIndex(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, const std::vector<int>& inliers, pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_out, const bool& is_true)
{
    if (cloud == nullptr) return false;            // 判断输入点云是否为空
    if (cloud->points.size() < 10) return false;   // 判断输入点云的尺寸
    if (cloud_out == nullptr) cloud_out.reset(new pcl::PointCloud<pcl::PointXYZ>);  // 判断输出点云是否为空,如果为空则需要为其new出一个对象

    int PointSize = cloud->points.size();
    if (is_true)
    {
        pcl::copyPointCloud(*cloud, inliers, *cloud_out);
    }
    else
    {
        std::vector<int> indices(PointSize);
        std::vector<int> outliers;
        std::iota(indices.begin(), indices.end(), 0);
        std::set_difference(indices.begin(), indices.end(), inliers.begin(), inliers.end(), std::back_inserter(outliers));
        pcl::copyPointCloud(*cloud, outliers, *cloud_out);
    }

    return true;
}

上述代码中,直接使用了pcl::copyPointCloud(*cloud, inliers, *cloud_out);提取点云,传入的第二个参数可以是inliers,也可以是outliers。输入点云cloud中所有点的索引或者说是下标肯定是0~n-1,0是第一个点的索引,n-1为最后一个点的索引,n是cloud包含的点数。

通常,想查看点云cloud中某个点的坐标,一般都会写成cloud->points[index],这个index的范围就是0到n-1。std::iota(indices.begin(), indices.end(), 0);这句代码运行之后,就会得到一个cloud点云索引的容器,存放着0,1,2,3,4,5 … n-3,n-2,n-1。而inliers则是存放输内点索引的容器,std::set_difference(indices.begin(), indices.end(), inliers.begin(), inliers.end(), std::back_inserter(outliers));这句代码就是将点云cloud的全部索引indices与内点索引inliers进行比较,其中indices中有但是inliers中没有的索引值则存放进outliers。最后,用pcl::copyPointCloud(*cloud, outliers, *cloud_out);就提取到了外点点云。

运行时间对比如下图所示,可以发现内点运行时间还长一点,而外点提取时间还短一点。这个其实跟点的数量有关系,如果内点的数量多,当然就更耗时。但是总的来说,无论是提取内点还是外点,使用上述方法都是比较快的,而且很方便。
在这里插入图片描述

方法二:
除了上述方法之外,PCL库中特意集成有相应的方法提取点云的内点或者外点。
代码:

/// <summary>
/// 使用PCL库中pcl::ExtractIndices方法进行内点和外点的提取
/// </summary>
/// <param name="cloud">输入点云</param>
/// <param name="inliers">内点的索引数组</param>
/// <param name="cloudout">输出点云</param>
/// <param name="is_in">输入true则提取内点点云,输入false则提取外点点云</param>
/// <returns>return true则表示提取成功,return false则表示提取失败</returns>
bool ExtractCloudByIndices(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, const std::vector<int>& inliers, pcl::PointCloud<pcl::PointXYZ>::Ptr& cloudout, const bool& is_in)
{
    if (cloud == nullptr) return false;
    if (cloud->points.size() < 10) return false;
    if (cloudout == nullptr) cloudout.reset(new pcl::PointCloud<pcl::PointXYZ>);

    pcl::PointIndices::Ptr pinliers(new pcl::PointIndices());
    pinliers->indices.assign(inliers.begin(), inliers.end());
    pcl::ExtractIndices<pcl::PointXYZ> extractor;
    extractor.setInputCloud(cloud);
    extractor.setIndices(pinliers);
    extractor.setNegative(!is_in); //如果设为true,可以提取指定index之外的点云
    extractor.filter(*cloudout);

    return true;
}

上述代码中extractor.setNegative(!is_in);就是设置提取内点还是外点的成员函数。如果extractor.setNegative(true);即输入的是true,则提取除了inliers以外的,也就是外点的点云,而extractor.setNegative(false);才是提取inliers索引的点云,与方法一中的习惯相反,为了调整成一致,所以传入的是!is_in,而不是is_in。这个时候就与方法一保持一样的习惯了。

此外,还有一点不同的是pcl::ExtractIndices所需要的传入的索引不是std::vector 而必须是pcl::PointIndices::Ptr。其实这两种数据结构是可以互相转化的,所以用 pinliers->indices.assign(inliers.begin(), inliers.end());这句代码进行了一次转化。当然,在使用PCL库中算法时,有时候得到的不一定是std::vector,而是pcl::PointIndices::Ptr。这个就看每个人的习惯和喜好去封装函数了,也可以封装成一个传参为pcl::PointIndices::Ptr数据的函数。

运行结果如下:
在这里插入图片描述
可以从结果看出,方法二的耗时要比方法一长“很多”。具体使用哪种方法进行点云内点或者外点的提取则是要看个人习惯了。

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

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

相关文章

拖拽属性 draggable

H5 新增的属性 draggable&#xff0c;它能够给与一切的 html 元素拖动的效果。 拖拽元素 属性为 draggable"true" 的元素&#xff0c;可拖动&#xff0c;且拖动时鼠标变为禁用图标 ps: 直接写 draggable 可能无效 ondragstart 开始拖拽时触发&#xff08;按下鼠标…

【SpringMVC】SpringMVC简介、过程分析、bean的加载和控制

文章目录 1. SpringMVC简介2. SpringMVC入门案例文件结构第一步&#xff1a;坐标导入第二步&#xff1a;创建SpringMVC容器的控制器类第三步&#xff1a;初始化SpringMVC环境&#xff0c;设定Spring加载对应的bean第四步&#xff1a;初始化Servlet容器&#xff0c;加载SpringMV…

PyQt6 QScrollBar滚动条控件

锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计48条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话版…

实验记录:可能造成深度学习模型训练过程中准确率振荡的原因

可能造成模型训练过程中准确率振荡的原因&#xff1a; 数据集因素&#xff1a; 1.数据集中含有噪声或者样本分布不平衡&#xff0c;这会导致模型学习到一些错误的规律&#xff0c;从而引起训练准确率的震荡。 2.训练数据量过小。如果训练数据集过小&#xff0c;会导致样本不足…

Y4M视频文件格式

什么是Y4M 以YUV4Mpeg格式创建的视频文件;这个视频文件存储了一组未压缩的YCbCr图像&#xff0c;这些图像逐帧组成视频;在压缩成MPEG-2或Matroska等更流行的视频格式之前&#xff0c;用作原始的彩色视频格式 Y4M文件是一个纯文本格式的header开始&#xff0c;header有0或多个…

ARM架构简析

全局与局量等知识 断电后&#xff0c;程序以及数据都在FLASH中。 断电后&#xff0c;内存中就没有变量了。 程序在烧在FLASH中的&#xff1b; 程序运行的时候&#xff0c;全局变量的初始值&#xff0c;必然是从FLAASH中的来的&#xff1a; 初始化全局变量的过程&#xff1a;…

B01、JVM与Java体系结构-01

字节码与多语言混合编程 字节码概述&#xff1a; 我们平时说的java字节码&#xff0c;指的是用java语言编译成的字节码。准确的说任何能在jvm平台上执行的字节码格式都是一样的。所以应该统称为&#xff1a;jvm字节码。不同的编译器&#xff0c;可以编译出相同的字节码文件&…

【面试】广告优化

a1&#xff1a;点击率公式是什么&#xff1f;点击率低的原因是什么&#xff1f; 点击率点击/曝光&#xff0c;点击率低的原因主要有两点&#xff1a;一是创意不吸引人&#xff1b;二是目标受众不准确/定向过宽不精确&#xff0c;广告曝光给了对产品不感兴趣用户 a2&#xff1a;…

Google Gemini 模型本地可视化

Google近期发布了Gemini模型&#xff0c;而且开放了Gemini Pro API&#xff0c;Gemini Pro 可免费使用&#xff01; Gemini Pro支持全球180个国家的38种语言&#xff0c;目前接受文本、图片作为输入并生成文本作为输出。 Gemini Pro的表现超越了其他同类模型&#xff0c;当前版…

version `GLIBC_2.29‘ not found 的原因和怎么解决问题

程序上经常有在这台Linux上编译&#xff0c;然后放到另一个Linux上运行的情况。 如果Linux版本差别不大或都是ubuntu或centos系列还好。 如果不是一个系列很容易出现GLIBC 找不到的情况。 尤其是ubuntu上编译&#xff0c;然后放到centos系列。因为centos为了追求所谓的稳定&…

linux系统中出现大量不可中断进程和僵尸进程怎么办?

进程状态 当iowait升高时&#xff0c;进程很可能因为得不到硬件的响应&#xff0c;而长时间处于不可中断的状态&#xff0c;从ps或者top命令的输出中&#xff0c;可以发现它们都处于D状态&#xff0c;也就是不可中断状态。 通过top和ps可以查看进程的状态&#xff0c;S列表示…

20来岁,大专毕业,学软件测试可行吗?

转行软件测试找不到工作&#xff01; 转行软件测试找不到工作&#xff01; 转行软件测试找不到工作&#xff01; 重要的事情说三遍&#xff01;千万别听培训班咨询老师给你画饼 &#xff1b;我就是某某软件测试培训班出来的&#xff0c;大专&#xff0c;其他专业毕业&#x…

【数据分享】2019-2023年我国区县逐年新房房价数据(Excel/Shp格式)

房价是一个区域发展程度的重要体现&#xff0c;一个区域的房价越高通常代表这个区域越发达&#xff0c;对于人口的吸引力越大&#xff01;因此&#xff0c;房价数据是我们在各项城市研究中都非常常用的数据&#xff01;之前我们分享了2019—2023年我国区县逐月的新房房价数据&a…

实验用python实现决策树和随机森林分类

1.实验目的 1.会用Python提供的sklearn库中的决策树算法对数据进行分类 2.会用Python提供的sklearn库中的随机森林算法对数据进行分类 3.会用Python提供的方法对数据进行预处理 2.设备与环境 使用Spyder并借助Python语言进行实现 3.实验原理 决策树( Decision Tree) 又称为…

【合成数字】合成类游戏-uniapp项目开发流程详解

以前玩过2048游戏&#xff0c;从中发现规律&#xff0c;想到跟合成类游戏相似&#xff0c;知道为什么很相似吗&#xff0c;在这里&#xff0c;做一个数字合成游戏玩玩吧&#xff0c;感兴趣的话可以看看&#xff0c;这里给大家讲一讲数字合成游戏的开发过程。 文章目录 创建项目…

spring事务不生效的场景有哪些

参考文章地址 百度安全验证&#xff0c;https://www.cnblogs.com/novwind/p/17461448.html 这里讨论的是声明式事务的不生效场景。编程式事务不在此处讨论 要说明spring中哪些场景事务不生效&#xff0c;就要说明spring的事务控制是如何实现的。Spring框架中事务控制的运行原理…

磁力计LIS2MDL开发(2)----电子罗盘

磁力计LIS2MDL开发.2--电子罗盘 概述视频教学样品申请源码下载环境磁场建模消除硬铁误差软铁干扰主程序 概述 本文将介绍如何使用 LIS2MDL 传感器来读取数据来转化为指南针。 地磁场强度范围约为 23,000 至 66,000 nT &#xff0c;并且可以建模为磁偶极子&#xff0c;其场线起…

10天玩转Python第8天:python 文件和异常 全面详解与代码示例

今日内容 文件操作 普通文件的操作json 文件的操作[重点] 异常处理(程序代码运行时的报错) 文件介绍 计算机的 文件&#xff0c;就是存储在某种 长期储存设备 上的一段 数据 作用: 将数据长期保存下来&#xff0c;在需要的时候使用 ​ 1.计算机只认识 二进制(0 1) 2.文件中…

CMA、CNAS软件检测公司分享:压力测试应关注的指标和面临的问题

软件压力测试是容易被传统企业忽视的测试点&#xff0c;用户人数一旦超过预期&#xff0c;极易造成软件产品卡顿、崩溃的情况&#xff0c;不利于用户正常使用&#xff0c;严重影响企业公信力和盈利水平。今天卓码软件测评小编来聊聊压力测试过程中应该关注的指标和会面临的问题…

Mysql存储引擎-InnoDB

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…