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

利用 Open3D 保存并载入相机视角的简单示例

1. 前言

在使用 Open3D 进行三维可视化和点云处理时,有时需要将当前的视角(Camera Viewpoint)保存下来,以便下次再次打开时能够还原到同样的视角。本文将演示如何在最新的 Open3D GUI 界面(o3d.visualization.gui / o3d.visualization.O3DVisualizer)中实现这一功能,并展示完整示例代码及运行效果。

2. 环境准备

  • Python 版本:3.x
  • Open3D 版本:0.15+ 或 0.16+(支持新的 GUI)
  • 其他依赖:Numpy

如果你使用的是 pip 安装,确保安装最新的 Open3D:

pip install --upgrade open3d

3. 实现思路

在 Open3D 中,相机视角主要由**内参(Intrinsic)外参(Extrinsic)**两个部分组成。

  • 内参(Intrinsic):描述相机的焦距(fx, fy)和主点坐标(cx, cy)。
  • 外参(Extrinsic):描述世界坐标系到相机坐标系的变换关系,包含旋转和平移。

由于 Open3D 内部在新的 GUI 中使用了 OpenGL 风格的坐标系,因此需要进行一次坐标变换。文中使用了一个 ToGLCamera 矩阵与其逆矩阵来做坐标系之间的转换。

当我们在 O3DVisualizer 中查看点云并进行旋转、平移等操作时,可以通过 vis.scene.camera.get_model_matrix() 获取到对应的模型矩阵(model matrix),然后再转换为外参矩阵(extrinsic)。最后,我们把这些参数序列化(用 pickle)存储起来,之后就可以再读取并还原相机的视角。

4. 关键函数解析

4.1 model_matrix_to_extrinsic_matrix(model_matrix)

def model_matrix_to_extrinsic_matrix(model_matrix):return np.linalg.inv(model_matrix @ FromGLGamera)
  • 这里 model_matrix 来自 vis.scene.camera.get_model_matrix()
  • 由于 Open3D GUI 中的相机使用了与传统坐标系不同的变换,需要先右乘一个 FromGLGameraToGLCamera 的逆)矩阵,然后对结果取逆,才能得到最终的外参矩阵。

4.2 create_camera_intrinsic_from_size(width, height, hfov, vfov)

def create_camera_intrinsic_from_size(width=1024, height=768, hfov=60.0, vfov=60.0):fx = (width / 2.0) / np.tan(np.radians(hfov)/2)fy = (height / 2.0) / np.tan(np.radians(vfov)/2)return np.array([[fx, 0, width / 2.0],[0, fy, height / 2.0],[0, 0,  1]])
  • 该函数根据窗口的宽度 width、高度 height 以及水平和垂直视角(hfov, vfov)来计算焦距。
  • 其中 (width / 2) / tan(hfov / 2) 的含义是根据给定视场角和图像尺寸来估计相机焦距。
  • 最终返回一个 3×3 的内参矩阵。

4.3 save_view(vis, fname='saved_view.pkl')

def save_view(vis, fname='saved_view.pkl'):try:model_matrix = np.asarray(vis.scene.camera.get_model_matrix())extrinsic = model_matrix_to_extrinsic_matrix(model_matrix)width, height = vis.size.width, vis.size.heightintrinsic = create_camera_intrinsic_from_size(width, height)saved_view = dict(extrinsic=extrinsic, intrinsic=intrinsic, width=width, height=height)with open(fname, 'wb') as pickle_file:dump(saved_view, pickle_file)except Exception as e:print(e)
  • 获取当前相机的模型矩阵并转换成外参矩阵 extrinsic
  • 读取当前窗口大小,计算内参矩阵 intrinsic
  • 将外参、内参、窗口大小一起打包到一个字典 saved_view 里并用 pickle 序列化保存到文件。

4.4 load_view(vis, fname="saved_view.pkl")

def load_view(vis, fname="saved_view.pkl"):try:with open(fname, 'rb') as pickle_file:saved_view = load(pickle_file)vis.setup_camera(saved_view['intrinsic'], saved_view['extrinsic'],saved_view['width'], saved_view['height'])except Exception as e:print("Can't find file", e)
  • 反序列化读取之前保存的 intrinsicextrinsic 和窗口大小信息。
  • 调用 vis.setup_camera(...) 还原相机视角。
    点击Action按钮,即可导出或载入视角

5. 完整示例代码

下面贴出完整的示例代码,供参考(假设文件名为 demo_save_load_view.py)。代码中已经包含了上述四个关键函数,并演示了如何加载点云、如何在 GUI 中添加菜单项来保存/加载视角,以及如何在程序启动后自动加载之前保存的视角并截图保存。

import numpy as np
import open3d as o3d
import open3d.visualization.gui as gui
from pickle import load, dumpToGLCamera = np.array([[1,  0,  0,  0],[0, -1,  0,  0],[0,  0, -1,  0],[0,  0,  0,  1]
])
FromGLGamera = np.linalg.inv(ToGLCamera)def model_matrix_to_extrinsic_matrix(model_matrix):return np.linalg.inv(model_matrix @ FromGLGamera)def create_camera_intrinsic_from_size(width=1024, height=768, hfov=60.0, vfov=60.0):fx = (width / 2.0) / np.tan(np.radians(hfov)/2)fy = (height / 2.0) / np.tan(np.radians(vfov)/2)return np.array([[fx, 0, width / 2.0],[0, fy, height / 2.0],[0, 0,  1]])def save_view(vis, fname='saved_view.pkl'):try:model_matrix = np.asarray(vis.scene.camera.get_model_matrix())extrinsic = model_matrix_to_extrinsic_matrix(model_matrix)width, height = vis.size.width, vis.size.heightintrinsic = create_camera_intrinsic_from_size(width, height)saved_view = dict(extrinsic=extrinsic, intrinsic=intrinsic, width=width, height=height)with open(fname, 'wb') as pickle_file:dump(saved_view, pickle_file)print(f"View saved to {fname}")except Exception as e:print(e)def load_view(vis, fname="saved_view.pkl"):try:with open(fname, 'rb') as pickle_file:saved_view = load(pickle_file)vis.setup_camera(saved_view['intrinsic'], saved_view['extrinsic'],saved_view['width'], saved_view['height'])print(f"View loaded from {fname}")except Exception as e:print("Can't find file", e)def main():gui.Application.instance.initialize()vis = o3d.visualization.O3DVisualizer("Demo to Load a Camera Viewpoint for O3DVisualizer", 1920, 1080)# 添加窗口gui.Application.instance.add_window(vis)# 设置一些可视化参数vis.point_size = 8vis.show_axes = True# 在菜单中添加保存和加载相机视角的选项vis.add_action("Save Camera View", save_view)vis.add_action("Load Camera View", load_view)# 调整一些可视化效果vis.point_size = 4          vis.show_axes = False       vis.show_skybox(False)# 读取并添加点云到可视化pcd = o3d.io.read_point_cloud('/10.pcd')vis.add_geometry("Random Point Cloud", pcd)# 延迟加载视角def load_view_delayed():load_view(vis, 'saved_view.pkl')gui.Application.instance.post_to_main_thread(vis, load_view_delayed)# 延迟一秒后截图def take_screenshot():import timetime.sleep(1)  vis.export_current_image("screenshot.png")print("Screenshot saved to screenshot.png")gui.Application.instance.post_to_main_thread(vis, take_screenshot)gui.Application.instance.run()if __name__ == "__main__":main()

6. 使用方法

  1. 确保安装好 Open3D(最好是最新版本),并将上面的代码保存为 demo_save_load_view.py
  2. 修改点云文件路径:将 pcd = o3d.io.read_point_cloud('/path/to/your.pcd') 替换为你自己的点云文件路径。
  3. 运行脚本:
    python demo_save_load_view.py
    
  4. 首次运行时,如果本地没有 saved_view.pkl 文件,会提示找不到文件;你可以手动在菜单里选择 Actions -> Save Camera View 来保存当前视角。
  5. 下次再运行脚本时,程序会自动执行 load_view_delayed(),从上次保存的 saved_view.pkl 中加载相机视角,并在 1 秒后截图。

7. 总结

通过本文示例,我们可以看到,在新的 Open3D GUI(O3DVisualizer)中,保存并还原相机视角的核心思路就是:

  1. 获取当前相机的 model_matrix
  2. 结合一个与 OpenGL 坐标系相关的转换矩阵,计算出外参;
  3. 根据窗口大小和视场角,生成内参;
  4. 将这些数据保存到文件,日后可以轻松加载还原相机视角。

这样就能方便地在多次打开程序或者不同机器上还原同一个观察视角。希望这篇文章能给你在使用 Open3D 的项目中带来帮助。

相关文章:

利用 Open3D 保存并载入相机视角的简单示例

1. 前言 在使用 Open3D 进行三维可视化和点云处理时,有时需要将当前的视角(Camera Viewpoint)保存下来,以便下次再次打开时能够还原到同样的视角。本文将演示如何在最新的 Open3D GUI 界面(o3d.visualization.gui / o…...

智绘教:Windows平台上的高效悬浮窗画笔工具深度解析

在Windows平台上,一款高效、实用的悬浮窗画笔工具对于提升工作效率和演示效果至关重要。今天,我要为大家介绍一款备受好评的悬浮窗画笔程序——智绘教。这款软件以其丰富的功能和便捷的操作,成为了众多用户心中的首选。接下来,让我们一起深入了解智绘教的各项特性。 一、体…...

从“Switch-case“到“智能模式“:C#模式匹配的终极进化指南

当代码开始"思考" 你是否厌倦了层层嵌套的if-else地狱?是否想过让代码像侦探推理一样优雅地解构数据?C#的模式匹配正是这样一把瑞士军刀,从C# 7.0到C# 12,它已悄然进化成改变编程范式的利器。 一、模式匹配的三重境界…...

【Linux】进程优先级 | 进程调度(三)

目录 前言: 一、进程优先级: 1.通过nice值修改优先级: 二、进程切换: 三、上下文数据 四、Linux真实调度算法: 五、bitmap位图: 六、命令总结: 总结: 前言: 我…...

wordpress按不同页调用不同的标题3种形式

在WordPress中,可以通过多种方式根据不同的页面调用不同的标题。这通常用于实现SEO优化、自定义页面标题或根据页面类型显示不同的标题内容。 使用wp_title函数 wp_title函数用于在HTML的title标签中输出页面标题。你可以通过修改主题的header.php文件来实现自定义…...

音频进阶学习十六——LTI系统的差分方程与频域分析一(频率响应)

文章目录 前言一、差分方程的有理式1.差分方程的有理分式2.因果系统和ROC3.稳定性与ROC 二、频率响应1.定义2.幅频响应3.相频响应4.群延迟 总结 前言 本篇文章会先复习Z变换的有理分式,这是之前文章中提过的内容,这里会将差分方程和有理分式进行结合来看…...

css实现左右切换平滑效果

2025.02.25今天我学习了如何用css实现平滑效果 一、html相关代码 &#xff08;1&#xff09;设置往左、往右的动画属性&#xff0c;样式可以放在同一级。 &#xff08;2&#xff09;必须设置唯一key进行刷新数据&#xff0c;使用v-show来展示每次渲染的组件数量。 <tran…...

详解Tomcat下载安装以及IDEA配置Tomcat(2023最新)

目录 步骤一&#xff1a;首先确认自己是否已经安装JDK步骤二&#xff1a;下载安装Tomcat步骤三&#xff1a;Tomcat配置环境变量步骤四&#xff1a;验证Tomcat配置是否成功步骤五&#xff1a;为IDEA配置Tomcat 步骤一&#xff1a;首先确认自己是否已经安装JDK jdk各版本通用安…...

Docker快速使用指南

docker pull ubuntu:22.04 //先拉取一个基础镜像&#xff0c;一般是操作系统创建一个Dockerfile&#xff0c;放在任意目录下&#xff0c;内容如下 # 使用 Ubuntu 22.04 作为基础镜像 FROM ubuntu:22.04# 设置环境变量&#xff0c;避免安装过程中出现交互提示 ENV DEBIAN_FRONT…...

【Project】基于Prometheus监控docker平台

一、设计背景 1.1项目简介 本项目旨在创建一个全面的容器化应用程序监控解决方案&#xff0c;基于Prometheus监控Docker平台上的各种服务。在当今的软件开发环境中&#xff0c;容器化技术已成为一种关键的工具&#xff0c;使应用程序能够更快速、可靠地交付和扩展。然而&…...

Binder通信协议

目录 一,整体架构 二,Binder通信协议 三&#xff0c;binder驱动返回协议 四&#xff0c;请求binder驱动协议 一,整体架构 二,Binder通信协议 三&#xff0c;binder驱动返回协议 binder_driver_return_protocol共包含18个命令&#xff0c;分别是&#xff1a; 四&#xff0c…...

使用 Postman 访问 Keycloak 端点

1. 引言 在本教程中&#xff0c;我们将首先快速回顾 OAuth 2.0、OpenID 和 Keycloak。然后&#xff0c;我们将了解 Keycloak REST API 以及如何在 Postman 中调用它们。 2. OAuth 2.0 OAuth 2.0 是一个授权框架&#xff0c;它允许经过身份验证的用户通过令牌向第三方授予访问…...

uniapp-X 对象动态取值

有个对象&#xff0c;例如 const data{age:12,list:[1,2,3,4]} 有个函数如下 export function getValueByPath(obj:UTSJSONObject, path:string):any {const current obj.getAny(path) as any;// 返回最终的值return current; } 期待 通过执行getValueByPath("xx.xx…...

建模软件Blender与Blender GIS插件安装教程

Blender&#xff08;blender.org - Home of the Blender project - Free and Open 3D Creation Software&#xff09;是一款功能强大的开源3D创作套件&#xff0c;它支持整个3D管道—建模、渲染、动画制作、模拟、渲染、合成和运动跟踪&#xff0c;甚至视频编辑和游戏制作&…...

数据解析与处理

数据解析与处理是数据科学、分析或开发中的核心步骤&#xff0c;涉及从原始数据中提取、清洗、转换和存储有效信息的过程。 一、数据解析 数据解析就是将原始数据&#xff08;如文本、二进制、日志、API响应等&#xff09;转换为结构化格式&#xff08;如表格、字典、JSON等&…...

强化学习概览

强化学习的目标 智能体&#xff08;Agent&#xff09;通过与环境&#xff08;Environment&#xff09;交互&#xff0c;学习最大化累积奖励&#xff08;Cumulative Reward&#xff09;​的策略。 数学抽象 马尔科夫决策过程&#xff08;MDP&#xff09; 收益 由于马尔科夫决…...

如何在netlify一键部署静态网站

1. 准备你的项目 确保你的静态网站文件&#xff08;如 HTML、CSS、JavaScript、图片等&#xff09;都在一个文件夹中。通常&#xff0c;项目结构如下&#xff1a; my-static-site/ ├── index.html ├── styles/ │ └── styles.css └── scripts/└── script.js…...

2024中国信通院“集智”蓝皮书合集(附下载)

【目 录】 1. 数字政府一体化建设蓝皮书&#xff08;2024年&#xff09; 2. 数字乡村发展实践蓝皮书&#xff08;2023年&#xff09; 3. 中国工业互联网发展成效评估报告&#xff08;2024年&#xff09; 4. 云计算蓝皮书&#xff08;2024年&#xff09; 5. 具身智能发展报告…...

springboot单机支持1w并发,需要做哪些优化

Spring Boot单机如何支持1万并发&#xff0c;需要做哪些优化。 首先&#xff0c;我得回想一下Spring Boot处理高并发的关键点在哪里。可能涉及到多个层面&#xff0c;比如Web服务器配置、数据库优化、代码层面的调整&#xff0c;还有JVM调优之类的。 首先&#xff0c;用户可能…...

HBuilderx 插件开发变量名称翻译 ,中文转(小驼峰,大驼峰,下划线,常量,CSS类名)

HBuilderx 插件开发变量名称翻译 &#xff0c;中文转&#xff08;小驼峰&#xff0c;大驼峰&#xff0c;下划线&#xff0c;常量&#xff0c;CSS类名&#xff09; 插件开发文档 工具HBuilderx &#xff0c;创建项目 创建成功后目录 插件需求 开发时 用来将中文转为&#xff0…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

Qt的学习(一)

1.什么是Qt Qt特指用来进行桌面应用开发&#xff08;电脑上写的程序&#xff09;涉及到的一套技术Qt无法开发网页前端&#xff0c;也不能开发移动应用。 客户端开发的重要任务&#xff1a;编写和用户交互的界面。一般来说和用户交互的界面&#xff0c;有两种典型风格&…...

CTF show 数学不及格

拿到题目先查一下壳&#xff0c;看一下信息 发现是一个ELF文件&#xff0c;64位的 ​ 用IDA Pro 64 打开这个文件 ​ 然后点击F5进行伪代码转换 可以看到有五个if判断&#xff0c;第一个argc ! 5这个判断并没有起太大作用&#xff0c;主要是下面四个if判断 ​ 根据题目…...

【大模型】RankRAG:基于大模型的上下文排序与检索增强生成的统一框架

文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构C.1 指令微调阶段C.2 排名与生成的总和指令微调阶段C.3 RankRAG推理&#xff1a;检索-重排-生成 D 实验设计E 个人总结 A 论文出处 论文题目&#xff1a;RankRAG&#xff1a;Unifying Context Ranking…...