当前位置: 首页 > news >正文

Python实现点云投影到直线、平面、柱面和球面

        本节我们分享点云的投影计算,按直线 → 平面 → 圆柱 → 球面顺序介绍。其实无论投影到哪种几何基元,都遵循“三步走”:① 读取或生成点云 → ② 计算每个点在目标几何体上的最近点 → ③ 用最近点坐标替换原坐标并可视化。
下面分别给出四种算法的具体做法。

1、投影到直线

目标
把点云 P = {pᵢ} 压到一条无限长直线上,得到新的点云 P′ = {p′ᵢ},其中 p′ᵢpᵢ 到直线的正交投影。

输入

  • 直线方向向量 d(单位化)

  • 直线上任意一点 o

算法

  1. 读取点云 P

  2. 对每个点 pᵢ 计算参数 tᵢ = (pᵢ − o)·d

  3. 投影点 p_{i}{}'= o + tᵢ d

  4. p_{i}{}'更新 P

  5. 使用 Open3D 的 draw_geometries 可视化。


2、投影到平面

目标
P 正交投影到给定平面。

输入

  • 平面法向量 n(单位化)

  • 平面上任意一点 o

算法

  1. 读取点云 P

  2. 对每个点 pᵢ 计算有符号距离 dᵢ = (pᵢ − o)·n

  3. 投影点 p_{i}{}'= pᵢ − dᵢ n

  4. p_{i}{}'更新 P

  5. 可视化。


3、投影到圆柱面

目标
P 投影到半径为 r、轴线已知的圆柱面上。

输入

  • 圆柱轴线方向向量 d(单位化)

  • 轴线上任意一点 o

  • 半径 r

算法

  1. 读取点云 P

  2. 对每个点 pᵢ
    ① 计算到轴线的最近点 qᵢ = o + ((pᵢ − o)·d) d
    ② 计算离轴距离向量 vᵢ = pᵢ − qᵢ
    ③ 归一化 uᵢ = vᵢ / ‖vᵢ‖(若 ‖vᵢ‖ = 0 可任取正交于 d 的单位向量);
    ④ 圆柱表面点 p_{i}{}'= qᵢ + r uᵢ

  3. p_{i}{}'更新 P

  4. 可视化。


4、投影到球面

目标
P 投影到以 c 为中心、半径 R 的球面上。

输入

  • 球心 c

  • 半径 R

算法

  1. 读取点云 P

  2. 对每个点 pᵢ
    ① 计算方向向量 vᵢ = pᵢ − c
    ② 归一化 uᵢ = vᵢ / ‖vᵢ‖
    ③ 球面点 p′ᵢ = c + R uᵢ

  3. p′ᵢ 更新 P

  4. 可视化。

当然,本次使用的数据依然是兔砸,闪亮登场:

一、四种投影程序

import open3d as o3d
import numpy as np
import os# -------------------------------------------------
# 基础工具
# -------------------------------------------------
def load_pcd(name):pcd = o3d.io.read_point_cloud(name)if pcd.is_empty():raise RuntimeError(f"找不到或无法读取 {name}")return pcddef save_pcd(pcd, src_name, suffix):out = src_name.replace(".pcd", f"_proj_{suffix}.pcd")o3d.io.write_point_cloud(out, pcd, print_progress=False)print(f"已保存:{out}")def view(pcd, title):o3d.visualization.draw_geometries([pcd], window_name=title, width=900, height=700, left=50, top=50)# -------------------------------------------------
# 四种投影实现
# -------------------------------------------------
def proj_line(pcd, line_p, line_d):pts = np.asarray(pcd.points)d = line_d / np.linalg.norm(line_d)t = ((pts - line_p) @ d)[:, None]proj = line_p + t * dnew = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(proj))if pcd.has_colors():new.colors = pcd.colorsreturn newdef proj_plane(pcd, coeffs):A, B, C, D = coeffsn = np.array([A, B, C], dtype=float)n = n / np.linalg.norm(n)pts = np.asarray(pcd.points)dist = (pts @ n + D)[:, None]proj = pts - dist * nnew = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(proj))if pcd.has_colors():new.colors = pcd.colorsreturn newdef proj_cylinder(pcd, axis_p, axis_d, radius):pts = np.asarray(pcd.points)d = axis_d / np.linalg.norm(axis_d)vec = pts - axis_pt = (vec @ d)[:, None]axis_proj = axis_p + t * dradial = pts - axis_projnorms = np.linalg.norm(radial, axis=1, keepdims=True)norms[norms == 0] = 1surf = axis_proj + radius * radial / normsnew = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(surf))if pcd.has_colors():new.colors = pcd.colorsreturn newdef proj_sphere(pcd, center, radius):pts = np.asarray(pcd.points)vec = pts - centernorms = np.linalg.norm(vec, axis=1, keepdims=True)norms[norms == 0] = 1surf = center + radius * vec / normsnew = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(surf))if pcd.has_colors():new.colors = pcd.colorsreturn new# -------------------------------------------------
# 主流程
# -------------------------------------------------
def main():src_file = "E:/CSDN/规则点云/bunny.pcd"if not os.path.isfile(src_file):print("请确保点云存在!")returnpcd = load_pcd(src_file)print(f"已加载 {src_file},共 {len(pcd.points)} 点")# 1) 直线line_p = np.array([0., 0., 0.])line_d = np.array([1., 1., 1.])line_proj = proj_line(pcd, line_p, line_d)# save_pcd(line_proj, src_file, "line")view(line_proj, "投影到直线")# 2) 平面(示例:x+2y+3z+4=0)plane_coeff = [1, 2, 3, 4]plane_proj = proj_plane(pcd, plane_coeff)# save_pcd(plane_proj, src_file, "plane")view(plane_proj, "投影到平面")# 3) 圆柱(Z轴,半径 2.5)axis_p = np.array([0., 0., 0.])axis_d = np.array([0., 0., 1.])radius = 2.5cyl_proj = proj_cylinder(pcd, axis_p, axis_d, radius)# save_pcd(cyl_proj, src_file, "cylinder")view(cyl_proj, "投影到圆柱")# 4) 球(原点,半径 1)center = np.array([0., 0., 0.])sphere_r = 1.0sphere_proj = proj_sphere(pcd, center, sphere_r)# save_pcd(sphere_proj, src_file, "sphere")view(sphere_proj, "投影到球面")print("全部完成!")if __name__ == "__main__":main()

二、四种投影结果

        可以看到,兔砸分别被投影到了直线、平面、圆柱和球面上。当然,根据参数的不同,最后显示的不同,有兴趣的同学可以调节参数试一试。

就酱,下次见^-^

http://www.dtcms.com/a/350089.html

相关文章:

  • 视频孪生技术赋能电力巡检:从“平面监控”到“立体智控”的跨越
  • Vue 3 customRef 完全指南:自定义响应式引用的终极教程
  • 前端面试题vue合集
  • 华为云Stack环境中计算资源,存储资源,网络资源发放前的准备工作(中篇)
  • week4-[二维数组]平面上的点
  • win11中系统的WSL安装Centos以及必要组件
  • 基于 Prometheus+Alertmanager+Grafana 打造监控报警后台(一)-Prometheus介绍及安装
  • 企业级监控可视化系统 Prometheus + Grafana
  • 检索模型与RAG
  • 【Day 13】189.轮转数组
  • 项目文章|MeRIP-seq助力解析m6A RNA甲基化与康乃馨花衰老的调控机制
  • Day8--HOT100--160. 相交链表,206. 反转链表,234. 回文链表,876. 链表的中间结点
  • 30.throw抛异常
  • 项目前后端分离部署
  • LVM基本操作
  • LeetCode100-189轮转数组
  • 20.15 Hugging Face Whisper-large-v2中文微调实战:LoRA+混合精度单卡训练指南,3倍效率省90%显存
  • 正则表达式学习(基础)
  • AUTOSAR进阶图解==>AUTOSAR_RS_Features
  • 电脑隐私安全防护|快速清理Windows系统/浏览器/应用数据,支持文件粉碎与磁盘级擦除!
  • 从MyJUnit反思Java项目的工程实践(版本控制篇)
  • 数据库迁移幂等性介绍(Idempotence)(Flyway、Liquibase)ALTER、ON DUPLICATE
  • RabbitMQ面试精讲 Day 30:RabbitMQ面试真题解析与答题技巧
  • 深入解析MyBatis Mapper接口工作原理
  • Ubuntu24.04配置yolov5
  • 封装的form表单,校验规则(rules)只在提交时触发,为空时点击提交触发,再次输入内容也不显示校验规则(rules)
  • 机器学习】(12) --随机森林
  • Day27 进程管理(PCB、状态、调度、原语与资源管理)
  • pikachu之Over permission
  • 基于SpringBoot的宠物领养系统的设计与实现(代码+数据库+LW)