曲线降采样之道格拉斯-普克算法Douglas–Peucker

news2024/12/24 2:29:22

曲线降采样之道格拉斯-普克算法Douglas–Peucker

该算法的目的是,给定一条由线段构成的曲线,找到一条点数较少的相似曲线,来近似描述原始的曲线,达到降低时间、空间复杂度和平滑曲线的目的。

image

附赠自动驾驶学习资料和量产经验:链接

示例(B中红色为降采样后的曲线)

image

算法流程

image

image

image

4. 此时算法已经完成了一个循环,后续按照递归的方式,分别对线段p1-p6和p6-p7进行同样的处理,p6-p7之间的点已经处理完,则直接结束;距离p1-p6线段最远的点为p5,p5到线段距离大于image,则p5也是需要保留的点;

image

image

5. 同理,p5-p6线段之间无待处理点,直接结束;距离p1-p5线段最远的点为p3,其到线段的距离仍大于image,则保留p3;

image

image

6. 距离线段p1-p3最远的点为p2,其距离小于image,将p1到p3中间所有的点标记为忽略;距离线段p3-p5最远的点为p4,其距离也小于image,将p3-p5之间所有待处理点标记为忽略;

7. 至此,算法结束,上述过程中,所有组成线段的端点即为降采样后曲线的点。

时间复杂度

引用:https://zh.wikipedia.org/wiki/%E9%81%93%E6%A0%BC%E6%8B%89%E6%96%AF-%E6%99%AE%E5%85%8B%E7%AE%97%E6%B3%95

image

代码

  • 定义基本结构和工具函数

    // 二维点
    struct Point2D {
    double x;
    double y;
    Point2D() : x(0.0), y(0.0) {}
    Point2D(double x, double y) : x(x), y(y) {}
    };

    // 计算点到线段的距离
    double DouglasPeucker::GetDistanceOnLine(const Point2D &start,
    const Point2D &end,
    const Point2D &point) {
    float a = end.y - start.y;
    float b = start.x - end.x;
    float c = end.x * start.y - start.x * end.y;
    return std::fabs(a * point.x + b * point.y + c) / std::sqrt(a * a + b * b);
    }

  • 核心处理函数

    std::vector
    DouglasPeucker::Process(const std::vector &points_in,
    const double delta) {
    std::vector result;
    // points为需要处理的点集,其中第一个和最后一个点为线段的端点,delta为误差阈值
    // step1. 找到距离线段最远的点
    double max_dist = -1;
    int max_idx = -1;
    // 两头的点默认是保留的, 不需要进行计算
    for (int i = 1; i < points_in.size() - 1; i++) {
    double d = GetDistanceOnLine(points_in[0], points_in[points_in.size() - 1],
    points_in[i]);
    if (d > max_dist) {
    max_dist = d;
    max_idx = i;
    }
    }
    // step2. 如果最远点的距离大于阈值,则将其作为新的线段端点,递归处理
    if (max_dist > delta) {
    std::vector left_pts, right_pts;
    left_pts.reserve(max_idx + 1);
    right_pts.reserve(points_in.size() - max_idx);
    for (int i = 0; i <= max_idx; i++) {
    left_pts.push_back(points_in[i]);
    }
    for (int i = max_idx; i < points_in.size(); i++) {
    right_pts.push_back(points_in[i]);
    }
    std::vector left_result = DouglasPeucker::Process(left_pts, delta);
    std::vector right_result =
    DouglasPeucker::Process(right_pts, delta);
    result.insert(result.end(), left_result.begin(), left_result.end() - 1);
    result.insert(result.end(), right_result.begin(), right_result.end());
    } else { // 两种情况到这里: 点到线段的最大距离小于阈值,或者只有两个点
    result.push_back(points_in[0]);
    result.push_back(points_in[points_in.size() - 1]);
    }
    return result;
    }

  • 运行,将上述p1… p7组成的曲线进行降采样, image

    int main()
    {
    std::vectorAD::Point2D points_in;
    points_in.push_back(AD::Point2D(0.0, 5.0)); // p1
    points_in.push_back(AD::Point2D(1.0, 4.0)); // p2
    points_in.push_back(AD::Point2D(2.0, 3.2)); // p3
    points_in.push_back(AD::Point2D(3.0, 4.5)); // p4
    points_in.push_back(AD::Point2D(4.0, 4.7)); // p5
    points_in.push_back(AD::Point2D(5.0, 1.0)); // p6
    points_in.push_back(AD::Point2D(7.0, 6.0)); // p7
    std::vectorAD::Point2D points_out;
    points_out = AD::DownSampling::DouglasPeucker::Process(points_in, 0.5);
    for (auto &point : points_out) {
    std::cout << "x: " << point.x << ", y: " << point.y << std::endl;
    }

    return 0;
    }

    // 输出
    x: 0, y: 5 // p1
    x: 2, y: 3.2 // p3
    x: 4, y: 4.7 // p5
    x: 5, y: 1 // p6
    x: 7, y: 6 // p7

算法缺点

  • 需要调参,但通常参数也比较好确定;

  • 时间复杂度不稳定;

  • 自顶向下的递归优化,不一定能保证是全局最优,前面做出的选择会影响后续的结果。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】哈希之位图

目录 一、位图概念二、海量数据面试题 一、位图概念 假如有40亿个无重复且没有排序的无符号整数&#xff0c;给一个无符号整数&#xff0c;如何判断这个整数是否在这40亿个数中&#xff1f; 我们用以前的思路有这些&#xff1a; 把这40亿个数遍历一遍&#xff0c;直到找到为…

AI音乐GPT时刻来临:Suno 快速入门手册!

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

使用fusesource的mqtt-client-1.7-uber.jar,mqtt发布消息出去,接收端看到的是中文乱码,如何解决?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

是否有替代U盘,可安全交换的医院文件摆渡方案?

医院内部网络存储着大量的敏感医疗数据&#xff0c;包括患者的个人信息、病历记录、诊断结果等。网络隔离可以有效防止未经授权的访问和数据泄露&#xff0c;确保这些敏感信息的安全。随着法律法规的不断完善&#xff0c;如《网络安全法》、《个人信息保护法》等&#xff0c;医…

基于Springboot+Mybatis实现个人理财系统

基于SpringbootMybatis实现个人理财系统 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式…

Python机器学习实验 数据处理之Numpy

一、实验目的 1. 了解numpy库的基本功能 2. 掌握Numpy库的对数组的操作与运算 二、实验工具&#xff1a; 1. Anaconda 2. Numpy 三、Numpy简介 Numpy 的英文全称为 Numerical Python&#xff0c;指Python 面向数值计算的第三方库。Numpy 的特点在于&#xff0c;针对 Pyt…

多模态学习实战手册:读懂CompassRank榜单的评测指标!

1. 前言 榜单链接:CompassRank CompassRank 是一个中立且全面的性能榜单,作为大模型评测体系 OpenCompass2.0 中各类榜单的承载平台。它覆盖多领域、多任务下的模型性能,并定期更新,以提供动态的行业洞察。 CompassRank 保持中立性,不受任何商业利益干扰,并依托于 Com…

Springboot集成knife4j (swagger)

1、添加依赖 在pom.xml 文件中添加 knife4j-spring-boot-starter 的依赖 <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>3.0.3</version> </depe…

D-迷恋网游(遇到过的题,做个笔记)

我的代码&#xff1a; #include <iostream> using namespace std; int main() {int a, b, c; //a表示内向&#xff0c;b表示外向&#xff0c;c表示无所谓cin >> a >> b >> c; //读入数 if (b % 3 0 || 3-b % 3 < c) //如果外向的人能够3人组成…

大数据学习第十二天(mysql不会的查询1)

1、数据 /*创建部门表*/ CREATE TABLE dept( deptno INT PRIMARY KEY, dname VARCHAR(50) comment 部门名称, loc VARCHAR(50) comment 工作地点 ); /*创建雇员表*/ CREATE TABLE emp( empno INT PRIMARY KEY, ena…

C++语言学习(三)——内联函数、auto、for循环、nullptr

1. 内联函数 &#xff08;1&#xff09;概念 以inline修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用内联函数的地方展开&#xff0c;没有函数调 用建立栈帧的开销&#xff0c;内联函数提升程序运行的效率。 内联函数是一种编译器指令&#xff0c;用于告诉编译器…

操作系统—读者-写者问题及Peterson算法实现

文章目录 I.读者-写者问题1.读者-写者问题和分析2.读者—写者问题基本解法3.饥饿现象和解决方案总结 II.Peterson算法实现1.Peterson算法问题与分析(1).如何无锁访问临界区呢&#xff1f;(2).Peterson算法的基本逻辑(3).写对方/自己进程号的区别是&#xff1f; 2.只包含意向的解…

软考高级架构师:存储管理-磁盘管理概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

GD32F470_MPU-6050模块 三轴加速度 陀螺仪6DOF模块 有代码原理图 GY-521模块移植

2.13 MPU6050六轴传感器 MPU6050 是 InvenSense 公司推出的整合性 6 轴运动处理组件&#xff0c;其内部整合了 3 轴陀螺仪和 3 轴加速度传感器&#xff0c;并且含有一个IIC 接口&#xff0c; 可用于连接外部磁力传感器&#xff0c;并利用自带的数字运动处理器&#xff08;DMP: …

基于ssm的寝室管理系统(java项目+文档+源码)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的寝室管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 寝室管理系统设计的主要使用者分为…

参数传值机制

在 Java 中&#xff0c;方法的所有参数都是 “传值” 的 基本类型&#xff1a;数值的拷贝 引用类型&#xff1a;引用的拷贝 方法内部改变参数对象的状态&#xff08;修改某属性&#xff09;&#xff0c;改变将反映到原始对象上 因为方法内部和外部引用的是同一个对象 方法内部…

探索广告行业业务模型的创新与发展

标随着数字化时代的到来&#xff0c;广告行业正经历着前所未有的变革和发展。在这个充满挑战和机遇的时代&#xff0c;广告公司和从业者们正在探索各种创新的业务模型&#xff0c;以适应市场的变化并取得成功。本文将深入探讨广告行业的业务模型&#xff0c;探索创新与发展的路…

华为交换机配置指引(包含安全配置部分)以 S5735S-L48T4S-A1 配置为例

华为S5735S-L48T4S-A1 是一款千兆以太网交换机: 端口结构: 48个10/100/1000BASE-T以太网端口和4个千兆SFP光接口供电方式: 交流电源背板带宽: 432Gbps包转发率: 87/166Mpps机箱高度: 1U重量: 2.76kg(不含包材)功耗: 典型功耗为43.3W接口: 48个10/100/1000BASE-T以太网电接口…

Python-VBA编程500例-029(入门级)

连续字符段索引(Index of Consecutive Character Segments)在实际应用中具有多种场景。常见的应用场景有&#xff1a; 1、文本分析&#xff1a;在文本处理和分析中&#xff0c;连续字符段索引可以用于识别重复的字符序列或模式。这些模式可能对于理解文本的结构、风格或特定含…

详解人工智能(概念、发展、机遇与挑战)

前言 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一门新兴的技术科学&#xff0c;是指通过模拟、延伸和扩展人类智能的理论、方法、技术和应用系统&#xff0c;以实现对人类认知、决策、规划、学习、交流、创造等智能行为的模拟、延伸和扩展…