道格拉斯普克算法(DP)的点云轮廓线简化

2024-05-13 19:20

本文主要是介绍道格拉斯普克算法(DP)的点云轮廓线简化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、背景介绍

       由于点云无法精确刻画目标对象边缘信息,因此常规提取的边缘点直接相连所生成的轮廓线,锯齿现象显著,与真实情况相差甚远(图b所示)。

      道格拉斯-普克(Douglas-Peuker)抽稀算法是用来对大量冗余的图形数据点进行压缩以提取必要的数据点。利用DP算法实现的轮廓线简化结果,如图c,其真实反应了点云形状。因此,在实际点云三维重建中,DP算法常用于点云简化,进而用于后续三维重建。

(a)原始点云(b)边缘点直接相连(c)DP算法简化后轮廓

 2、DP原理介绍

        道格拉斯普克算法(Douglas-Peukcer)算法是一种简化线状要素的经典算法。对每一条曲线的首末点虚连一条直线,求所有点与直线的距离,并找出最大距离值dmax,用dmax与距离阈值D相比。若dmax<D,这条曲线上的中间点全部舍去;若dmax ≥D,保留dmax对应的坐标点,并以该点为界,把曲线分为两部分,对这两部分重复使用该方法。

         算法的详细步骤如下:

(1) 在散点首尾两点间连一条直线,求出其余各点到该直线的距离(如图1、3、5中红色直线)。

(2) 选其最大者与阈值相比较,若大于阈值,则离该直线距离最大的点保留,否则将直线两端点间各点全部舍去(如图2相对图1中增加的点、图4相对图3增加的点)

(3) 依据所保留的点,将已知曲线分成两部分处理,重复第1、2步操作,迭代操作,即仍选距离最大者与阈值比较,依次取舍,直到无点可舍去,最后得到满足给定精度限差的点,舍去其他点,即完成线的化简。如图6中最终剩下的点。

(1)(2)
(3)(4)
(5)(6)

3、程序编程思想

     通过对DP算法原理介绍,发现该算法是一种递归思想,将点云一直划分成左右两部分,类似构建二叉树过程。首先对输入的一组点,判断其是否需要分组。

判断是否需要分组

     当最大距离大于阈值时,需要进行划分成左右两部分(以4号点断开),如下图所示:

     对于每一部分,采用上边类似的思想,判断递归下去,直至不再分成左右两棵树。

    本程序编写过程:

3.1 边缘点提取

     使用alpha shapes提取轮廓点,并对其进行排序,得到有序轮廓点

boundExtract(cloud, r, bound, non_bound);

3.2 确定初始轮廓点分组    

    选取轮廓点中距离最远的两个点,依据这两个点确定是否需要将原始点进行分组。本程序采用x、y对应的最值(xmin、xmax、ymin、ymax)确定距离最远的两点。

GetTwoPointsMaxDis(bound, headpoint, endpoint, headindex, endindex);

相距最远两点

      其中划分组的策略如下:头部点对应的序列号(11)与尾部点序列号(62)之间的点(即11-62)化为一组,然后采62-93与1-11和起来分为一组。

3.3 对于每组点构造二叉树

     通过对点分组,确定头、尾节点。再根据位于头尾节点中的点,确定待要划分的关键点。节点示意如下:

struct DPNode
{pcl::PointXYZ HeadPoint;pcl::PointXYZ EndPoint;vector<pcl::PointXYZ> points;DPNode *Left_node;//左边节点DPNode *Right_node;//右边节点bool NodeType;//该节点是否可以继续划分,若可以则是true,否则为false
};

3.4 遍历二叉树寻找关键节点

   采用前序遍历的方式(即先遍历左结点),找到关键点,关键节点为将点云划分为2组的点。

if (maxds < thres_ds)//小于阈值的,不再分割{root->Left_node = NULL;root->Right_node = NULL;root->NodeType = false;//不能再划分}else{root->NodeType = true;//可以继续划分//将点划分成2部分,左边与右边vector<pcl::PointXYZ> Leftpointsvec, Rightpointsvec;for (int i = 0; i < points.size(); i++){if (i <= maxindex){Leftpointsvec.push_back(points[i]);//左边树包含的点}}for (int i = 0; i < points.size(); i++){if (i >= maxindex){Rightpointsvec.push_back(points[i]);//右边树包含的点}}//左边子树的头部点与尾部点pcl::PointXYZ left_headpoint = headpoint;pcl::PointXYZ left_endpoint = points[maxindex];//右边子树的头部点与尾部点pcl::PointXYZ right_headpoint = points[maxindex];pcl::PointXYZ right_endpoint = endpoint;//创建左、右树root->Right_node = new DPNode();BuildTree(root->Left_node, Leftpointsvec, left_headpoint, left_endpoint, thres_ds);BuildTree(root->Right_node, Rightpointsvec, right_headpoint, right_endpoint, thres_ds);}

3.5 剔除重复关键点与排序

      利用先序遍历,获取所有的关键点,即头、尾点,再对重复的关键点剔除,得到最终精简的轮廓关键点。

4、测试验证

     根据上述原理,本程序基于PCL、C++进行实现。将“道格拉斯-普克 .cpp”文件放入原文件下,如下图所示:

cpp文件存放示意图

4.1 边缘提取测试

         可以发现,两个点云数据中边缘点,均被成功提取出,其中红色点为边缘点,白色点为非边缘点。

边缘提取示意图边缘提取示意图

4.2 边缘点有序测试

       直接将提取的边缘点进行相连,若能够刻画点云形状,则说明点云有序。如下图所示,虽然直接将点进行相连,锯齿现象比较明显,但是其仍大体刻画点云形状,证明提取的轮廓点为有序结构。

轮廓点相连轮廓点相连

4.3 DP简化点云测试

      使用各类不同形状的点云数据进行测试,可以发现点云边缘简化结果相当理想,比较简单,证明本程序能够正常运行,精简点云数据。

5、小结

    DP算法计算原理简单,精简点云效果理想,简化结果只与点到直线距离有关。理论上,当距离阈值设置越大,那么舍弃的点越多,关键点越少。因此,在实际使用过程中,需要根据需要设置参数。

这篇关于道格拉斯普克算法(DP)的点云轮廓线简化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

Springboot实现推荐系统的协同过滤算法

《Springboot实现推荐系统的协同过滤算法》协同过滤算法是一种在推荐系统中广泛使用的算法,用于预测用户对物品(如商品、电影、音乐等)的偏好,从而实现个性化推荐,下面给大家介绍Springboot... 目录前言基本原理 算法分类 计算方法应用场景 代码实现 前言协同过滤算法(Collaborativ

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为