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

使用 Open3D 批量渲染并导出固定视角点云截图

一、前言

在三维点云处理与可视化中,固定视角批量生成点云渲染截图是一个常见的需求。例如,想要将同一系列的点云(PCD 文件)在同样的视角下生成序列图片,以便后续合成为视频或进行其他可视化演示。本文将介绍如何使用 Python + Open3D 实现批量加载 PCD 文件、设置统一的相机视角,并导出渲染截图。

二、环境准备

  1. Python 环境:建议使用 Python 3.7+。
  2. Open3D 库:本文使用的是 open3dopen3d.visualization.gui。安装方式如下:
    pip install open3d
    
  3. 其他依赖库
    • numpy
    • pickle
    • glob
    • time
    • os
      如果缺少对应的库,使用 pip install 库名 即可。

三、代码解析

下面的代码分为几个主要部分:

  1. 相机矩阵转换:将 Open3D 的模型矩阵转换为外参矩阵。
  2. 相机内参生成:根据视口大小与视场角(FOV)生成相机内参。
  3. 保存与加载相机视角:使用 pickle 将当前相机的内外参持久化保存,方便在下次使用时快速恢复相机位置。
  4. 批量处理函数:遍历指定文件夹下所有 .pcd 文件,加载点云、应用相机视角、并自动保存渲染截图。
  5. 主函数:在 if __name__ == "__main__": 中调用批处理函数,或先进行单独的相机视角设置保存操作。

完整代码如下(可直接复制使用):

import numpy as np
import open3d as o3d
import open3d.visualization.gui as gui
from pickle import load, dump
import os
import glob
import time# 用于将坐标系转换为OpenGL风格
ToGLCamera = 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):"""将Open3D的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):"""根据视口大小与水平/垂直FOV生成相机内参"""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"Camera view saved to {fname}")except Exception as e:print("Error saving view:", 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"Camera view loaded from {fname}")except Exception as e:print("Can't load view file:", e)def process_pcd_folder(input_folder, output_folder, view_file='saved_view.pkl'):"""批量处理文件夹中的所有 PCD 文件,应用指定视角并保存截图"""os.makedirs(output_folder, exist_ok=True)pcd_files = sorted(glob.glob(os.path.join(input_folder, "*.pcd")))if not pcd_files:print(f"No PCD files found in {input_folder}")returnprint(f"Found {len(pcd_files)} PCD files")# 初始化GUIgui.Application.instance.initialize()vis = o3d.visualization.O3DVisualizer("PCD Batch Renderer", 1920, 1080)gui.Application.instance.add_window(vis)# 设置渲染参数vis.point_size = 4vis.show_axes = Falsevis.show_skybox(False)def process_next(idx):if idx >= len(pcd_files):print("Batch processing completed!")gui.Application.instance.quit()returnpcd_file = pcd_files[idx]print(f"Processing {idx+1}/{len(pcd_files)}: {os.path.basename(pcd_file)}")try:# 加载点云文件pcd = o3d.io.read_point_cloud(pcd_file)geom_name = f"PointCloud_{idx}"# 清除之前所有几何体,确保内存资源不会累积if idx > 0:vis.remove_geometry(f"PointCloud_{idx-1}")vis.add_geometry(geom_name, pcd)# 加载预先保存的视角load_view(vis, view_file)# 构建输出路径base_name = os.path.splitext(os.path.basename(pcd_file))[0]output_path = os.path.join(output_folder, f"{base_name}.png")def take_screenshot():# 延迟1秒以确保视角和渲染完全加载time.sleep(1)vis.export_current_image(output_path)print(f"Screenshot saved to {output_path}")# 处理完当前文件后,处理下一个process_next(idx + 1)# 使用post_to_main_thread确保截图任务在GUI线程执行gui.Application.instance.post_to_main_thread(vis, take_screenshot)except Exception as e:print(f"Error processing {pcd_file}: {e}")# 出错时跳过当前文件,继续下一个process_next(idx + 1)# 开始处理第一个文件process_next(0)gui.Application.instance.run()def batch_process():"""主函数:指定输入、输出文件夹以及相机视角文件,然后进行批量处理"""input_folder = './input'output_folder = './screenshots'os.makedirs(output_folder, exist_ok=True)view_file = 'saved_view.pkl'process_pcd_folder(input_folder, output_folder, view_file)if __name__ == "__main__":# 若需要先设置视角,运行 save_view 所在的逻辑# 若已设置好视角,运行 batch_process()批量处理batch_process()

1. 代码主要流程

  • 读取文件列表:通过 glob.glob 获取指定文件夹下的所有 .pcd 文件并排序。
  • 初始化 Open3D GUI:使用 O3DVisualizer 进行可视化。
  • 循环处理每个 PCD
    1. 读取点云数据 pcd = o3d.io.read_point_cloud(...)
    2. 加载之前保存的视角参数 load_view(vis, view_file)
    3. 设置几何体到渲染窗口
    4. 通过 vis.export_current_image(...) 将当前视图截图保存
  • 处理结束后退出:当全部 .pcd 文件处理完毕,自动退出 GUI。

2. 视角保存与加载

  • save_view(vis, fname='saved_view.pkl'):从当前的 vis.scene.camera 获取 model_matrix,然后计算外参矩阵、内参矩阵并存储到一个字典中,通过 pickle 持久化到 saved_view.pkl 文件。
  • load_view(vis, fname='saved_view.pkl'):从文件中读取上述字典,调用 vis.setup_camera(...) 将相机恢复到保存时的视角。

这样做的好处是,我们可以先交互式地在 Open3D 中调整一个理想的点云视角,然后保存该视角。后续就可以用同样的参数去渲染其他点云,实现“统一视角”输出。

3. 视角设置的两种方式

  1. 先在单个点云上用脚本交互式设置并保存
    • 先运行一个类似的脚本,只加载一个点云,不做批处理。
    • 在界面中使用鼠标旋转/平移点云至理想位置,然后调用 save_view(vis)
  2. 直接修改代码中的相机参数:如果你对内参、外参很熟悉,也可以直接硬编码想要的矩阵。

四、使用说明

  1. 准备 PCD 文件:将所有需要处理的 .pcd 文件放在同一个文件夹中。
  2. 保存视角(可选):
    • 若你已知道要使用的视角参数,可以跳过这一步;否则先写个简单脚本,加载一两个 PCD 文件后,通过交互操作找到满意的视角,执行 save_view(vis)
    • 此时会生成一个 saved_view.pkl 文件,里面记录了相机的内外参。
  3. 运行批处理
    • 修改 batch_process() 中的 input_folderoutput_folder 为你的输入、输出路径。
    • 运行脚本后,Open3D 窗口会依次加载每个 .pcd,应用保存好的视角,然后自动截图并存储到 output_folder 中。

在所有点云都处理完后,你就能在输出文件夹下看到对应的 .png 文件序列。

五、结果展示

下面是一张示例截图,展示了点云在固定视角下的渲染效果(仅做示意,非实际数据):
在这里插入图片描述

六、后续扩展

  1. 生成视频:如果想将渲染好的序列图片合成为视频,可使用 ffmpeg,示例命令如下:
    ffmpeg -framerate 10 -i labeled_sync_frame_%03d.png -c:v libx264 -pix_fmt yuv420p output.mp4
    
    其中 -framerate 10 表示每秒 10 帧,可根据需要调整。
  2. 更多可视化选项:如改变 point_size、背景颜色、或添加坐标轴等,可参考 Open3D 文档或修改 O3DVisualizer 的属性。
  3. 其他文件格式:如果想批量处理 .ply.xyz,只需要在代码中修改对应的读取方式,以及 glob.glob 匹配模式即可。

七、总结

通过上述方法,可以轻松地在同一视角下对多份点云进行批量渲染和截图,适用于制作点云动画、对比分析等场景。核心思想是事先保存好相机参数,并在批处理过程中为每个点云恢复相同的内外参,保证输出图像的视角一致。希望对你的三维可视化工作有所帮助,欢迎交流讨论!

相关文章:

使用 Open3D 批量渲染并导出固定视角点云截图

一、前言 在三维点云处理与可视化中,固定视角批量生成点云渲染截图是一个常见的需求。例如,想要将同一系列的点云(PCD 文件)在同样的视角下生成序列图片,以便后续合成为视频或进行其他可视化演示。本文将介绍如何使用…...

汽车无钥匙进入一键启动操作正确步骤

汽车智能无钥匙进入和一键启动的技术在近年来比较成熟,不同车型的操作步骤可能略有不同,但基本的流程应该是通用的,不会因为时间变化而有大的改变。 移动管家汽车一键启动无钥匙进入系统通常是通过携带钥匙靠近车辆,然后触摸门把…...

JMeter 的基础知识-安装部分

以下将从环境配置开始,为你详细介绍 JMeter 的基础知识,涵盖环境搭建、界面认知、测试计划创建、常用组件使用等方面内容。 1. 环境配置 1.1 安装 Java JMeter 是基于 Java 开发的,所以需要先安装 Java 开发工具包(JDK)。 下载 JDK:访问 Oracle 官方网站(https://www…...

解决后端跨域问题

目录 一、什么是跨域问题? 1、跨域问题的定义 2、举例 3、为什么会有跨域问题的存在? 二、解决跨域问题 1、新建配置类 2、编写代码 三、结语 一、什么是跨域问题? 1、跨域问题的定义 跨域问题(Cross-Origin Resource Sh…...

补题A-E Codeforces Round 953 (Div. 2)

https://codeforces.com/contest/1979 A. Guess the Maximum 原题链接&#xff1a;https://codeforces.com/contest/1979/problem/A 求相邻元素的最大值的最小值。 #include <bits/stdc.h> using namespace std; #define IOS ios::sync_with_stdio(0), cin.tie(0), cout…...

【WordPress】发布文章时自动通过机器人推送到钉钉

在您的主题下functions.php中添加如下代码&#xff1a; function wpso_dingding_publish_notify($post_ID) {// 获取文章对象$post get_post($post_ID);// 检查是否是文章首次发布&#xff08;即不是修订版&#xff09;if (get_post_status($post_ID) publish && !g…...

鸿蒙开发深入浅出04(首页数据渲染、搜索、Stack样式堆叠、Grid布局、shadow阴影)

鸿蒙开发深入浅出04&#xff08;首页数据渲染、搜索、Stack样式堆叠、Grid布局、shadow阴影&#xff09; 1、效果展示2、ets/pages/Home.ets3、ets/views/Home/SearchBar.ets4、ets/views/Home/NavList.ets5、ets/views/Home/TileList.ets6、ets/views/Home/PlanList.ets7、后端…...

管道文件(1)

1.无名管道&#xff1a;在同一主机下&#xff0c;具有亲缘关系的进程间的通信&#xff0c;如父子进程间的通信。 2.有名管道&#xff1a;在同一主机下的任意进程间的通信。 &#xff08;1&#xff09;管道的创建 &#xff08;2&#xff09;管道的打开&#xff08;open&#xff…...

什么是AI agent技术,有哪些著名案例

AI Agent技术是一种基于人工智能的智能实体&#xff0c;能够感知环境、进行决策和执行动作&#xff0c;以实现特定目标。近年来&#xff0c;随着大模型&#xff08;如GPT-4&#xff09;和生成式AI技术的发展&#xff0c;AI Agent在多个领域得到了广泛应用&#xff0c;并取得了显…...

Cursor结合Claude 3.7零基础开发愤怒的小鸟【深夜Claude 3.7系列发布,类似DeepSeek-R1和V3的合体?】

文章目录 前言介绍“市面上唯一的混合模型”卓越的编程能力、精准控制思考时间Cursor已接入Cursor结合Claude 3.7快速编写游戏完整html代码 &#x1f343;作者介绍&#xff1a;双非本科大四网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;前三年专注于Java领域学习&am…...

基于 Python 的天气数据分析与可视化

基于 Python 的天气数据分析与可视化 1. 项目背景 天气数据分析与可视化项目旨在通过爬取天气数据并进行分析&#xff0c;生成可视化图表&#xff0c;帮助用户了解天气变化趋势。通过该项目&#xff0c;学生可以掌握 Python 的数据爬取、数据分析和可视化技能。该项目适用于气…...

Bybit事件技术分析

事件概述 2025年2月21日UTC时间下午02:16:11&#xff0c;Bybit的以太坊冷钱包&#xff08;0x1db92e2eebc8e0c075a02bea49a2935bcd2dfcf4&#xff09;因恶意合约升级遭到资金盗取。根据Bybit CEO Ben Zhou的声明&#xff0c;攻击者通过钓鱼攻击诱骗冷钱包签名者错误签署恶意交易…...

JavaWeb-在idea中配置Servlet项目

文章目录 在idea中进行Servlet项目的配置(较新的idea版本)创建一个空的JavaSE项目(Project)创建一个普通的JavaSE板块(module)添加Web项目的配置定义一个对象模拟实现接口在web.xml中配置路径映射配置项目到Tomcat服务器启动Tomcat服务器进行测试 在idea中进行Servlet项目的配置…...

redis小记

redis小记 下载redis sudo apt-get install redis-server redis基本命令 ubuntu16下的redis没有protected-mode属性&#xff0c;就算sudo启动&#xff0c;也不能往/var/spool/cron/crontabs写计划任务&#xff0c;感觉很安全 #连接到redis redis-cli -h 127.0.0.1 -p 6379 …...

垂类大模型微调(一):认识LLaMA-Factory

LlamaFactory 是一个专注于 高效微调大型语言模型(LLMs) 的开源工具框架,尤其以支持 LLaMA(Meta 的大型语言模型系列)及其衍生模型(如 Chinese-LLaMA、Alpaca 等)而闻名。它的目标是简化模型微调流程,降低用户使用门槛; 官方文档 一、介绍 高效微调支持 支持多种微调…...

企业为什么要选择软件测试外包公司?湖南软件测试公司有哪些?

在当今快速发展的技术背景下&#xff0c;软件测试已成为软件开发生命周期的重要一环。随着企业对软件质量要求的不断提高&#xff0c;软件测试外包公司逐渐被越来越多的企业所青睐。 软件测试外包公司通过将软件测试从内部团队外包出去&#xff0c;帮助企业减少开发成本、提升…...

数据保护API(DPAPI)深度剖析与安全实践

Windows DPAPI 安全机制解析 在当今数据泄露与网络攻击日益频繁的背景下&#xff0c;Windows 提供的 DPAPI&#xff08;Data Protection API&#xff09;成为开发者保护本地敏感数据的重要工具。本文将从 双层密钥体系、加密流程、跨上下文加密、已知攻击向量与防御措施、企业…...

java23种设计模式-桥接模式

桥接模式&#xff08;Bridge Pattern&#xff09;学习笔记 &#x1f31f; 定义 桥接模式属于结构型设计模式&#xff0c;将抽象部分与实现部分分离&#xff0c;使它们可以独立变化。通过组合代替继承的方式&#xff0c;解决多维度的扩展问题&#xff0c;防止类爆炸。 &#x…...

3D Web轻量化引擎HOOPS Communicator如何赋能航空航天制造?

在当今航空航天制造领域&#xff0c;精确度、效率和协作是推动行业发展的关键要素。随着数字化技术的飞速发展&#xff0c;3D Web可视化开发包HOOPS Communicator 为航空航天制造带来了革命性的变化。它凭借强大的功能和灵活的应用&#xff0c;助力企业在设计、生产、培训等各个…...

iOS手机App爬虫- (1) Mac安装Appium真机运行环境

iOS手机App爬虫 一、环境准备与工具安装1. 开发基础环境配置1.1 Node.js环境1.2 Xcode套件1.3 Java环境 2. 核心测试工具链2.1 Appium主程序2.2 辅助工具集 3. 可视化工具 二、设备与环境验证1. 设备信息获取2. 环境健康检查 三、WebDriverAgent编译部署1. 设备端准备2. 项目配…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...

「Java基本语法」变量的使用

变量定义 变量是程序中存储数据的容器&#xff0c;用于保存可变的数据值。在Java中&#xff0c;变量必须先声明后使用&#xff0c;声明时需指定变量的数据类型和变量名。 语法 数据类型 变量名 [ 初始值]; 示例&#xff1a;声明与初始化 public class VariableDemo {publi…...