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

曲线降采样之道格拉斯-普克算法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.hqwc.cn/news/587218.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux安装Tomcat保姆级教程

文章目录 前言一、安装JDK二、Tomcat下载三、Tomcat安装1.创建Tomcat的安装目录2.切换到Tomcat的安装目录3.上传Tomcat安装包4.解压缩5.切换到安装好的tomcat的bin目录6.启动tomcat7.网络请求测试 四、外部电脑访问nginx设置方法一&#xff1a;关闭防火墙方法二&#xff1a;添加…

回顾快速排序

快速排序 快速排序的核心&#xff1a; 找到一个key 通常左边的数比key小&#xff0c;右边的数比key大。 找key通常有三种方法&#xff1a; 1. 挖坑法&#xff1a; 代码实现&#xff1a; // int _pivot(int* a, int left, int right) {int begin left, end right;int in…

数据结构(初阶)第二节:顺序表

从本文正式进入对数据结构的讲解&#xff0c;开始前友友们要有C语言的基础&#xff0c;熟练掌握动态内存管理、结构体、指针等章节&#xff0c;方便后续的学习。 顺序表&#xff08;Sequence List&#xff09; 线性表的概念&#xff1a;线性表&#xff08;linear list&#xff…

ssm016基于 Java Web 的校园驿站管理系统+jsp

校园驿站管理系统的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对校园快递信息管理混乱&#xff0c;出…

stm32串口的使用

我们先简单介绍一下基本的概念 1数据的通信方式&#xff1a; 2 这里三种数据传输方向分类大家需要记住。后面的通信都会涉及到。 3 我们来看一下常见的通信&#xff0c;这里我们使用usart 一什么是串口 1.1概述 &#xff1a; 指按位发送和接收的接口 但是&#xff0c;rs233…

【“状态机” 解析UART不定长度的协议帧】

【“状态机” 解析UART不定长度的协议帧】 1. 数据帧格式2. 状态机原理3. 代码实现 通信设计中考虑协议的灵活性&#xff0c;经常把协议设计成“不定长度”。如果一个系统接收上述“不定长度”的协议帧&#xff0c;将会有一个挑战–如何高效接收与解析。一个实例如下图&#xf…

【蓝桥杯嵌入式】11届程序题刷题记录及反思

一、题目介绍 按键输入&#xff1a;短按 模拟电压输出&#xff1a;ADC LCD显示 PWM输出&#xff1a;PA6,PA7 二、usr.c #include "usr.h" #include "lcd.h" #include "stdio.h" #include "tim.h" #include "adc.h" /*v…

道本科技智慧合规助力企业转型升级

在当今这个快速变化的商业世界里&#xff0c;企业合规管理已经从一项基本的监管要求转变为推动企业持续发展的关键动力。合规不仅是避免法律麻烦的盾牌&#xff0c;它还充当着引领企业向更高效、更可靠和更可持续方向发展的催化剂。而在实现这一目标的过程中&#xff0c;智慧合…

隐私计算实训营第六讲-隐语PIR介绍及开发实践

隐私计算实训营第六讲-隐语PIR介绍及开发实践 文章目录 隐私计算实训营第六讲-隐语PIR介绍及开发实践1.隐语实现PIR总体介绍1.1按服务器数量分类1.2按查询类型分类 2. Index PIR - SealPIR3. Keyword PIR - Labeled PSI4.隐语PIR功能分层5.隐语PIR后续计划PIR协议开发PIR调用框…

ndk ffmpeg

报错&#xff1a; 解决办法&#xff1a; 报错 解决办法&#xff1a;

干货教程【AI篇】| AI大模型文字生成视频环境部署小白级教程

只需要一个主题、一个词语&#xff0c;或者一段描述&#xff0c;就可以生成一个完整的短视频的工具来啦&#xff01; 在文章下方公众号中回复关键词【aivd】即可获取完整代码和配套软件 工具获取 ps&#xff1a;本文不涉及任何代码开发工作&#xff0c;仅仅作为软件推荐。 如…

洛谷P1000超级玛丽游戏题解[Python, Rust, Go]

题目 打印超级玛丽字符图像 小技巧 直接复制题目的超级玛丽符号首行会有空格问题&#xff0c;一直AC不过&#xff0c;一行一行地复制就OK了&#x1f44c;。 Rust 题解 fn main() {println!(" ********************####....#.#..###.....##....###...…