ConvexHull-凸包-原理-代码实现

news2024/11/14 13:56:32
  • 定义:凸包是包围点云的最小凸多面体,所有点都在该多面体的内部或表面上。
  • 优点:能够精确地包围点云,并且不存在额外的空白区域。
  • 缺点:计算复杂度高,尤其是在高密度点云中,生成凸包的过程较慢。

下面,我将介绍两种常用的凸包算法的 Python 实现:

  1. Graham 扫描算法
  2. Andrew’s Monotone Chain 算法

生成测试数据

为了方便理解,我们现已二维数据进行测试

import numpy as np
mean = [0, 0]
cov = [[3, 1], [1, 2]]
points = np.random.multivariate_normal(mean, cov, 300)
plt.plot(points[:,0], points[:,1], 'o')

Pasted image 20240920112501

Graham 扫描算法

Graham 扫描算法的基本步骤如下:

  1. 找到所有点中 y 坐标最小的点(如果有多个,则选择 x 坐标最小的)。
  2. 将剩余的点按与起始点的极角进行排序。
  3. 遍历排序后的点,使用栈维护当前的凸包顶点,判断转向方向以决定是否需要弹出栈顶点。
def graham_scan(points):
    # 找到 y 最小的点,若有多个则取 x 最小的
    start = min(points, key=lambda p: (p[1], p[0]))
    
    def polar_angle(p):
        return math.atan2(p[1] - start[1], p[0] - start[0])
    
    # 按极角排序,如果极角相同,离起点近的排前面
    sorted_points = sorted(points, key=lambda p: (polar_angle(p), (p[0] - start[0])**2 + (p[1] - start[1])**2))
    
    # 初始化栈
    stack = []
    for point in sorted_points:
        while len(stack) >= 2:
            o = stack[-2]
            a = stack[-1]
            b = point
            cross = (a[0] - o[0])*(b[1] - o[1]) - (a[1] - o[1])*(b[0] - o[0])
            if cross > 0:
                break
            stack.pop()
        stack.append(point)
    
    return stack
hull = np.array(graham_scan(points))
print("凸包顶点:", hull)
plt.plot(points[:,0], points[:,1], 'o')
plt.plot(hull[:,0], hull[:,1], 's')

代码解释:

  1. 寻找起始点:使用 min 函数找到 y 坐标最小的点。如果存在多个点 y 坐标相同,则选择 x 坐标最小的。
  2. 极角排序:根据每个点相对于起始点的极角进行排序。如果两个点的极角相同,则按照与起始点的距离从近到远排序。
  3. 构建凸包:使用栈来维护当前的凸包顶点。对于每个点,检查它与栈顶两个点的转向方向。如果不是左转(即凸包的定义),则弹出栈顶点,直到满足凸包的条件。
  4. 输出结果:最终栈中的点即为凸包的顶点。

输出结果:

凸包顶点: [[-1.51703261 -3.63377384] [ 4.51410791 -0.65295975] [ 4.10717132 1.7159215 ] [ 3.7692037 2.85400883] [ 0.08295138 3.84684229] [-1.25805766 3.39871686] [-2.56864639 1.63912747] [-4.09232528 -0.68889519] [-4.57389002 -1.96169353] [-3.7899005 -3.00607501]]

Pasted image 20240920112528

Andrew’s Monotone Chain 算法

Andrew’s Monotone Chain 算法是一种更高效的凸包算法,其时间复杂度为 O(n log n)。该算法首先将所有点按 x 坐标排序,然后分别构建下凸包和上凸包,最后合并两者。

def monotone_chain(points):
    # 确保输入是 NumPy 数组
    points = np.asarray(points)

    # 如果点集少于3个点,凸包即为所有点
    if len(points) <= 1:
        return points

    # 按 x 坐标排序,若 x 相同则按 y 坐标排序
    sorted_idx = np.lexsort((points[:,1], points[:,0]))
    sorted_points = points[sorted_idx]

    # 构建下凸包
    lower = []
    for p in sorted_points:
        while len(lower) >= 2:
            o, a = lower[-2], lower[-1]
            cross = (a[0] - o[0]) * (p[1] - o[1]) - (a[1] - o[1]) * (p[0] - o[0])
            if cross > 0:
                break
            lower.pop()
        lower.append(tuple(p))

    # 构建上凸包
    upper = []
    for p in reversed(sorted_points):
        while len(upper) >= 2:
            o, a = upper[-2], upper[-1]
            cross = (a[0] - o[0]) * (p[1] - o[1]) - (a[1] - o[1]) * (p[0] - o[0])
            if cross > 0:
                break
            upper.pop()
        upper.append(tuple(p))

    # 去除最后一个点(重复的起点)
    convex_hull = lower[:-1] + upper[:-1]

    # 转换为 NumPy 数组
    return np.array(convex_hull)


hull = monotone_chain(points)
print("凸包顶点:", hull)
plt.plot(points[:,0], points[:,1], 'o')
plt.plot(hull[:,0], hull[:,1], 's')

代码解释:

  1. 排序:首先将所有点按 x 坐标排序,若 x 坐标相同则按 y 坐标排序。
  2. 构建下凸包
    • 遍历排序后的点。
    • 使用栈维护当前的下凸包顶点。
    • 对于每个新点,检查是否保持左转,如果不是,则弹出栈顶点。
  3. 构建上凸包
    • 遍历排序后的点的逆序(从右到左)。
    • 类似地,使用栈维护当前的上凸包顶点。
  4. 合并结果:将下凸包和上凸包合并,注意去除重复的起点和终点。
  5. 输出结果:最终的 convex_hull 列表即为凸包的顶点。
    输出结果
凸包顶点: [[-4.57389002 -1.96169353]
 [-3.7899005  -3.00607501]
 [-1.51703261 -3.63377384]
 [ 4.51410791 -0.65295975]
 [ 4.10717132  1.7159215 ]
 [ 3.7692037   2.85400883]
 [ 0.08295138  3.84684229]
 [-1.25805766  3.39871686]
 [-2.56864639  1.63912747]
 [-4.09232528 -0.68889519]]

Pasted image 20240920112758

Open3d实现代码

在三维点云处理中,我们在使用算法一般会利用别人高效实现的方式,这里使用open3d库实现凸包算法

import open3d as o3d
file_path = './data/5.txt'

point_cloud_data = np.loadtxt(file_path)

points = point_cloud_data[:, :3]
colors = point_cloud_data[:, 3:] / 255.0  # Open3D expects colors in [0, 1] range

pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points)
pcd.colors = o3d.utility.Vector3dVector(colors)

hull, _ = pcd.compute_convex_hull()
hull_ls = o3d.geometry.LineSet.create_from_triangle_mesh(hull)
hull_ls.paint_uniform_color([1, 0, 0])  # 红色
o3d.visualization.draw_geometries([pcd, hull_ls])

可视化结果:
Pasted image 20240920113021

Pasted image 20240920113004

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

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

相关文章

前后端分离项目--下载功能

文章目录 不使用代理服务器blobblob构造函数通过FormData对象的getBlob方法创建Blob对象将Blob对象转换成UR 使用代理服务器 前后端分离项目中下载与其他接口的使用不同&#xff0c;一般下载不走node&#xff0c;不通过代理服务器&#xff0c;而是直接在前台发送请求&#xff0…

人工智能安全治理新篇章:《2024人工智能安全治理框架1.0版》深度解读@附20页PDF文件下载

在数字化浪潮席卷全球的今天&#xff0c;人工智能&#xff08;AI&#xff09;技术正以前所未有的速度融入我们的日常生活&#xff0c;从智能助手到自动驾驶&#xff0c;从医疗诊断到金融风控&#xff0c;AI的身影无处不在。然而&#xff0c;技术的双刃剑特性也让我们不得不面对…

3657A/B/AM/BM矢量网络分析仪

苏州新利通 3657A/B/AM/BM 矢量网络分析仪 3657系列矢量网络分析仪适用于无线通信、有线电视、教育及汽车电子等领域&#xff0c;可用于对滤波器、放大器、天线、电缆、有线电视分接头等射频元件的性能测量。该产品采用Windows操作系统&#xff1b;具有误差校准功能、时域功能…

Java基础面试必知:HashMap的数据结构与扩容机制

Java基础面试必知&#xff1a;HashMap的数据结构与扩容机制 在Java开发中&#xff0c; HashMap是使用最为广泛的数据结构之一&#xff0c;它提供了基于键值对的快速存取功能。然而&#xff0c;在面试中&#xff0c;经常会涉及到HashMap的底层实现原理&#xff0c;特别是其数据…

Abp vNext(五)集成MQTTnet,可收发消息

一 前言 MQTT的相关理论内容这里不做过多介绍&#xff0c;请看下面两篇文章&#xff1a; Introduction MQTT协议中文版 MQTT协议-CSDN博客 这篇文章只做代码实现&#xff0c;文章中使用MQTTnet作为MQTT开发的组件。 MQTT分为服务端和客户端&#xff0c;一个服务端对应多个…

Docker + Win 10 学习记录

下载Docker Release notes | Docker Docs 推荐使用4.33版本&#xff0c;最新的Docker版本在win10 22H2无法安装。需要升级到win11. 查看Win10版本是否与最新版的Docker兼容 运行 win R&#xff0c; 然后输入winver 如果你的Docker版本无法在当前的win10安装&#xff0c;请更…

编码器-解码器架构_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录一、引言编码器解码器合并编码器和解码器小结练习答案1. 编码器和解码器是否必须是同一类型的神经网络&#xff1f;2. 除了机器翻译&#xff0c;还有其它可以适用于“编码器&#xff0d;解码器”架构的应用吗&#xff1f; 一、引言 正如我们…

LocalDateTime,OffsetDateTime和ZonedDateTime(上)

图片来源&#xff1a;https://www.cnblogs.com/yourbatman/p/14324575.html 一. LocalDate和LocalTime LocalDate&#xff1a;代表不含时区信息的日期&#xff0c;它只能表示年、月、日。它适用于记录一个日子&#xff0c;比如生日、纪念日、或者任何只需要日期而不需要具体时…

除猫毛用粘毛器还是宠物空气净化器?希喂/米家/352/范罗士/有哈空气净化器对比

微博之夜&#xff0c;明星互送礼物环节&#xff0c;要求所有嘉宾准备一份礼物&#xff0c;再由其他明星随机抽取互换礼物。田曦薇送粘毛器可是引起了广泛的争议和批评。不说价格&#xff0c;粘毛器对咱养猫人来讲还真是刚需啊。我朋友家三只猫&#xff0c;出门不用说啥&#xf…

掌握数据中心虚拟化:关键挑战与解决方案

数据中心虚拟化是使用云软件平台将物理数据中心转变为数字数据中心的过程&#xff0c;使企业能够远程访问信息和应用程序。它包括在数据中心内创建物理基础设施的多个虚拟版本&#xff0c;通过将服务器、存储和网络等资源划分为虚拟实体来实现资源的高效利用。 虚拟化环境中的关…

[c++进阶(八)]STL容器适配器之queue

1.前言 和stack一样&#xff0c;队列也没有把他放在容器的一栏里面&#xff0c;而是把他放在容器适配器的一栏。这也是因为queue是使用了别人的相关接口&#xff0c;空间然后来封装自己的内容&#xff0c;最后再给上层用户使用。 2.队列 队列的性质就是先进先出&#xff0c;他…

【C++ 学习】多态的基础和原理(10)

目录 前言1. 概念2. 多态的定义及实现2.1 多态的构成条件2.2 虚函数2.3 虚函数重写2.4 虚函数重写的例外2.4.1 协变2.4.1 析构函数的重写 2.5 多态调用和普通调用2.6 函数重写/函数隐藏/函数重载 的对比2.6.1 函数重写2.6.2 函数隐藏2.6.3 函数重载 2.7 C11 final 和override 3…

HT326 免电感滤波2x20W D类立体声音频功放

特点 输出功率(BTL模式) 2x20W (VDD14.5V,RL4Ω,THDN1%) 单电源系统: 4.5V-18V; 超过90%效率&#xff0c;无需散热器 扩频功能&#xff0c;免电感滤波 模拟差分/单端输入可选 增益:32dB 保护功能:过压/过流/过热/欠压异常&#xff0c;直流检测 和短路保护 无铅无卤封装&#x…

three.js 热力图

使用three.js 和 heatMap.js 实现在 三维场景中展示热力图的效果&#xff0c;以下代码复制粘贴即可在你的本机运行。 在线编辑运行预览可方位 https://threehub.cn/#/codeMirror?navigationThreeJS&classifyexpand&idheatmap3D 在 https://threehub.cn 中还有很多案例…

[PTA]7-3 乘法口诀数列

[PTA]7-3 乘法口诀数列 输出格式&#xff1a; 在一行中输出数列的前 n 项。数字间以 1 个空格分隔&#xff0c;行首尾不得有多余空格。 输入样例&#xff1a; 2 3 10 输出样例&#xff1a; 2 3 6 1 8 6 8 4 8 4 样例解释&#xff1a; 数列前 2 项为 2 和 3。从 2 开始&#…

Java集合(三)

目录 Java集合&#xff08;三&#xff09; Java双列集合体系介绍 HashMap类 HashMap类介绍 HashMap类常用方法 HashMap类元素遍历 LinkedHashMap类 LinkedHashMap类介绍 LinkedHashMap类常用方法 LinkedHashMap类元素遍历 Map接口自定义类型去重的方式 Set接口和Ma…

grafana 使用常见问题

一、点击 panel 没有反应&#xff0c;没有出现 edit 选项。 方法一 将鼠标放在 panel 的任意位置&#xff0c;然后键盘输入 "e"&#xff0c;然后再次点击 title&#xff0c;即可出现选项框。 方法二 F12 查看当前 panel id&#xff0c;然后在浏览器 url 地址上拼接…

探索AI大模型的未来:电信运营商与云服务商的新征途@附58页PDF文件下载

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;大模型正成为推动行业变革的关键力量。近日&#xff0c;腾讯云联合中国信通院及中国通信标准化协会发布了《2024年AI大模型应用发展研究报告》&#xff0c;深入探讨了电信运营商与云服务商在AI大模型领域的…

Unicode与UTF-8的关系

Unicode又称统一码&#xff0c;万国码。uni是一个英文词根&#xff0c;原型是one, 表示“单一, 一个”&#xff0c;所以unicode本意是“一个码”&#xff0c;就是让每个字符都有一个唯一的编码。它就像个武林盟主&#xff0c;把世上所有的语言符号一勺烩&#xff0c;一统了编码…

反相求和电路设计

1 简介 该电路可对两个输入信号进行求和&#xff08;相加&#xff09;&#xff0c;并将其在输出端反相。输入信号通常要求低阻抗源&#xff0c;因为该电路的输入阻抗由输入电阻R1和R2决定。反相放大器的共模电压等于连接到同相节点的电压。 2 设计目标 2.1 输入 2.2 输出 2.3…