官方文档位置: Visualization - Open3D 0.19.0 documentationhttps://www.open3d.org/docs/release/tutorial/visualization/visualization.html核心方法:
o3d.visualization.draw_geometries([几何对象列表])
import open3d as o3dprint("Load a ply point cloud, print it, and render it")
sample_ply_data = o3d.data.PLYPointCloud()
pcd = o3d.io.read_point_cloud(sample_ply_data.path)
o3d.visualization.draw_geometries([pcd],zoom=0.3412,front=[0.4257, -0.2125, -0.8795],lookat=[2.6172, 2.0475, 1.532],up=[-0.0694, -0.9768, 0.2024])
visualization窗体功能:
点击到窗口 按键盘 h 键
(1) 视图控制
操作 | 方法 |
---|---|
旋转视图 | 按住鼠标左键拖动 |
平移视图 | 按住鼠标滚轮拖动 |
缩放视图 | 鼠标滚轮 |
重置视角 | 按 R 键 |
切换全屏 | 按 F 键 |
快捷键 | 功能 |
---|---|
L | 切换点云渲染(点/线框/面) |
N | 显示/隐藏法线(需提前计算) |
C | 显示/隐藏颜色(如果数据包含颜色) |
S | 保存当前视角截图 |
添加几何元素
下面的代码使用 、 和 生成一个长方体、一个球体和一个圆柱体。长方体涂成红色,球体涂成蓝色,圆柱体涂成绿色。为所有网格计算法线以支持 Phong 着色(请参见可视化 3D 网格和表面法线估计)。我们甚至可以使用 创建一个坐标轴,其原点设置为 (-2, -2, -2)。
import open3d as o3d
print("Let's define some primitives")
#创建立方体
mesh_box = o3d.geometry.TriangleMesh.create_box(width=1.0,height=1.0,depth=1.0)
#设置颜色
mesh_box.paint_uniform_color([0.9, 0.1, 0.1])
#创建球体
mesh_sphere = o3d.geometry.TriangleMesh.create_sphere(radius=1.0)
#设置颜色
mesh_sphere.paint_uniform_color([0.1, 0.1, 0.7])
#创建圆柱体
mesh_cylinder = o3d.geometry.TriangleMesh.create_cylinder(radius=0.3,height=4.0)
mesh_cylinder.paint_uniform_color([0.1, 0.9, 0.1])
#创建坐标轴
mesh_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=0.6, origin=[-2, -2, -2])
print("We draw a few primitives using collection.")
o3d.visualization.draw_geometries([mesh_box, mesh_sphere, mesh_cylinder, mesh_frame])print("We draw a few primitives using + operator of mesh.")
o3d.visualization.draw_geometries([mesh_box + mesh_sphere + mesh_cylinder + mesh_frame])
绘制线
Visualization - Open3D 0.19.0 documentationhttps://www.open3d.org/docs/release/tutorial/visualization/visualization.html#Draw-line-set
import open3d as o3d
print("Let's draw a box using o3d.geometry.LineSet.")
# 点坐标
points = [[0, 0, 0],[1, 0, 0],[0, 1, 0],[1, 1, 0],[0, 0, 1],[1, 0, 1],[0, 1, 1],[1, 1, 1],
]
# 线段索引 [0, 1] 表示0号点到1号点的线段
lines = [[0, 1],[0, 2],[1, 3],[2, 3],[4, 5],[4, 6],[5, 7],[6, 7],[0, 4],[1, 5],[2, 6],[3, 7],
]
colors = [[1, 0, 0] for i in range(len(lines))]
line_set = o3d.geometry.LineSet(points=o3d.utility.Vector3dVector(points), # 点坐标 需要转换为Vector3dVector Vector3dVector 是一个关键的数据转换工具,# 用于将常见的数值数组(如 NumPy 数组或 Python 列表)转换为 Open3D 内部高效处理的 3D 向量格式 N×3lines=o3d.utility.Vector2iVector(lines),
)
line_set.colors = o3d.utility.Vector3dVector(colors)
o3d.visualization.draw_geometries([line_set])
Vector2iVector
是一个用于处理 二维整数向量数据 的实用工具类,类似于 Vector3dVector
,但专为 (N, 2)
形状的整数数据设计
Vector3dVector
是一个关键的数据转换工具,用于将常见的数值数组(如 NumPy 数组或 Python 列表)转换为 Open3D 内部高效处理的 3D 向量格式,主要功能:将 N×3
的数值数据(如点云坐标、法线、颜色)转换为 Open3D 几何体(如 PointCloud
、TriangleMesh
)可识别的内部数据结构。
自定义可视化 自定义创建功能
Customized visualization - Open3D 0.19.0 documentationhttps://www.open3d.org/docs/release/tutorial/visualization/customized_visualization.html#customized-visualization使点云旋转
import open3d as o3d
def custom_draw_geometry_with_rotation(pcd):def rotate_view(vis):ctr = vis.get_view_control()ctr.rotate(10.0, 0.0)return Falseo3d.visualization.draw_geometries_with_animation_callback([pcd],rotate_view)sample_pcd_data = o3d.data.PCDPointCloud()
pcd = o3d.io.read_point_cloud(sample_pcd_data.path)
custom_draw_geometry_with_rotation(pcd)
https://www.open3d.org/docs/release/_images/rotate_small.gif
交互式可视化
Interactive visualization - Open3D 0.19.0 documentation
# examples/python/visualization/interactive_visualization.pyimport numpy as np
import copy
import open3d as o3ddef demo_crop_geometry():print("Demo for manual geometry cropping")print("1) Press 'Y' twice to align geometry with negative direction of y-axis")print("2) Press 'K' to lock screen and to switch to selection mode")print("3) Drag for rectangle selection,")print(" or use ctrl + left click for polygon selection")print("4) Press 'C' to get a selected geometry")print("5) Press 'S' to save the selected geometry")print("6) Press 'F' to switch to freeview mode")pcd_data = o3d.data.DemoICPPointClouds()pcd = o3d.io.read_point_cloud(pcd_data.paths[0])o3d.visualization.draw_geometries_with_editing([pcd])def draw_registration_result(source, target, transformation):source_temp = copy.deepcopy(source)target_temp = copy.deepcopy(target)source_temp.paint_uniform_color([1, 0.706, 0])target_temp.paint_uniform_color([0, 0.651, 0.929])source_temp.transform(transformation)o3d.visualization.draw_geometries([source_temp, target_temp])def prepare_data():pcd_data = o3d.data.DemoICPPointClouds()source = o3d.io.read_point_cloud(pcd_data.paths[0])target = o3d.io.read_point_cloud(pcd_data.paths[2])print("Visualization of two point clouds before manual alignment")draw_registration_result(source, target, np.identity(4))return source, targetdef pick_points(pcd):print("")print("1) Please pick at least three correspondences using [shift + left click]")print(" Press [shift + right click] to undo point picking")print("2) After picking points, press 'Q' to close the window")vis = o3d.visualization.VisualizerWithEditing()vis.create_window()vis.add_geometry(pcd)vis.run() # user picks pointsvis.destroy_window()print("")return vis.get_picked_points()def register_via_correspondences(source, target, source_points, target_points):corr = np.zeros((len(source_points), 2))corr[:, 0] = source_pointscorr[:, 1] = target_points# estimate rough transformation using correspondencesprint("Compute a rough transform using the correspondences given by user")p2p = o3d.pipelines.registration.TransformationEstimationPointToPoint()trans_init = p2p.compute_transformation(source, target,o3d.utility.Vector2iVector(corr))# point-to-point ICP for refinementprint("Perform point-to-point ICP refinement")threshold = 0.03 # 3cm distance thresholdreg_p2p = o3d.pipelines.registration.registration_icp(source, target, threshold, trans_init,o3d.pipelines.registration.TransformationEstimationPointToPoint())draw_registration_result(source, target, reg_p2p.transformation)def demo_manual_registration():print("Demo for manual ICP")source, target = prepare_data()# pick points from two point clouds and builds correspondencessource_points = pick_points(source)target_points = pick_points(target)assert (len(source_points) >= 3 and len(target_points) >= 3)assert (len(source_points) == len(target_points))register_via_correspondences(source, target, source_points, target_points)print("")if __name__ == "__main__":demo_crop_geometry()demo_manual_registration()
draw_geometries_with_editing 默认绑定的功能 可以实现点云的剪切
以下是打印信息中提到的按键及其作用:
按键 | 功能 | 触发条件 |
---|---|---|
Y (按两次) | 将几何体对齐到 Y 轴负方向 | 必须在非锁定模式下 |
K | 锁定屏幕并进入选择模式 | 任意时刻 |
Ctrl + 左键 | 多边形选择模式 | 必须在选择模式下 |
拖动鼠标 | 矩形框选 | 必须在选择模式下 |
C | 提取选中区域的几何体 | 必须在选择模式下有选中区域 |
S | 保存当前几何体到 edited_model.ply | 任意时刻 |
F | 退出选择模式,返回自由视角 | 必须在选择模式下 |
自定义按键绑定(高级用法)
如果需要覆盖默认行为或添加新功能,可以通过 注册回调函数 实现:
def custom_key_callback(vis):print("Custom key pressed!")return Falsevis = o3d.visualization.VisualizerWithEditing()
vis.create_window()
vis.register_key_callback(ord("Q"), custom_key_callback) # 绑定Q键
vis.add_geometry(pcd)
vis.run()
Interactive visualization - Open3D 0.19.0 documentation
实现手动选点
核心代码
def pick_points(pcd):print("")print("1) Please pick at least three correspondences using [shift + left click]")print(" Press [shift + right click] to undo point picking")print("2) After picking points, press 'Q' to close the window")vis = o3d.visualization.VisualizerWithEditing()vis.create_window()vis.add_geometry(pcd)vis.run() # user picks pointsvis.destroy_window()print("")return vis.get_picked_points()
进行点云配准
def register_via_correspondences(source, target, source_points, target_points):"""通过用户提供的对应点对进行点云粗配准 + ICP精配准参数:source (open3d.geometry.PointCloud): 待配准的源点云target (open3d.geometry.PointCloud): 目标点云source_points (list/np.array): 源点云中选取的对应点索引数组target_points (list/np.array): 目标点云中对应的点索引数组"""# 1. 构建对应点对矩阵 (N x 2)corr = np.zeros((len(source_points), 2)) # 初始化对应点对容器corr[:, 0] = source_points # 第一列填充源点云索引corr[:, 1] = target_points # 第二列填充目标点云索引# 2. 基于对应点对计算初始变换矩阵print("Compute a rough transform using the correspondences given by user")# 创建点对点变换估计器p2p = o3d.pipelines.registration.TransformationEstimationPointToPoint()# 计算初始变换矩阵(将source_points对齐到target_points)trans_init = p2p.compute_transformation(source, target,o3d.utility.Vector2iVector(corr) # 将对应点对转换为Open3D格式)# 3. 使用ICP进行精细配准print("Perform point-to-point ICP refinement")threshold = 0.03 # 3cm距离阈值,超过此距离的点对不参与计算reg_p2p = o3d.pipelines.registration.registration_icp(source, # 源点云target, # 目标点云threshold, # 最大对应点距离阈值trans_init, # 上一步计算的初始变换o3d.pipelines.registration.TransformationEstimationPointToPoint(), # 使用点对点ICP# 可选参数(未显式设置时使用默认值):# criteria = ICP迭代停止条件(默认最大迭代30次,相对变化1e-6)# estimation_method = 变换估计方法)# 4. 可视化配准结果draw_registration_result(source, target, reg_p2p.transformation)# 返回配准结果(包含变换矩阵、拟合度等信息)return reg_p2p
典型使用场景:
# 示例:手动选取5对对应点进行配准
source_idx = [10, 20, 30, 40, 50] # 源点云中选取的点索引
target_idx = [15, 25, 35, 45, 55] # 目标点云中对应的点索引
result = register_via_correspondences(source_pcd, target_pcd, source_idx, target_idx)
print("Final transformation matrix:\n", result.transformation)
非阻塞窗口 不停止窗口 并更新窗口显示
import open3d as o3d
import numpy as npdef prepare_data():# 加载Open3D提供的示例点云数据(两帧扫描数据)pcd_data = o3d.data.DemoICPPointClouds()source_raw = o3d.io.read_point_cloud(pcd_data.paths[0]) # 源点云target_raw = o3d.io.read_point_cloud(pcd_data.paths[1]) # 目标点云# 体素下采样(降低计算量)source = source_raw.voxel_down_sample(voxel_size=0.02)target = target_raw.voxel_down_sample(voxel_size=0.02)# 对源点云施加初始变换(模拟初始位姿偏差)trans = [[0.862, 0.011, -0.507, 0.0], [-0.139, 0.967, -0.215, 0.7],[0.487, 0.255, 0.835, -1.4], [0.0, 0.0, 0.0, 1.0]] # 4x4变换矩阵source.transform(trans)# 对两个点云施加镜像翻转(使可视化效果更直观)flip_transform = [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]source.transform(flip_transform)target.transform(flip_transform)return source, targetdef demo_non_blocking_visualization():# 设置日志级别为Debug(显示详细运行信息)o3d.utility.set_verbosity_level(o3d.utility.VerbosityLevel.Debug)# 加载数据source, target = prepare_data()# 创建可视化窗口并添加几何体vis = o3d.visualization.Visualizer()vis.create_window()vis.add_geometry(source) # 添加源点云(红色)vis.add_geometry(target) # 添加目标点云(蓝色)# ICP参数设置threshold = 0.05 # 距离阈值(5cm内视为对应点)icp_iteration = 100 # 总迭代次数save_image = False # 是否保存每帧截图# 迭代执行ICPfor i in range(icp_iteration):# 执行单次ICP迭代(max_iteration=1表示每步只迭代一次)reg_p2l = o3d.pipelines.registration.registration_icp(source, target, threshold, np.identity(4),o3d.pipelines.registration.TransformationEstimationPointToPlane(),o3d.pipelines.registration.ICPConvergenceCriteria(max_iteration=1))# 更新源点云位置source.transform(reg_p2l.transformation)# 刷新可视化vis.update_geometry(source) # 通知可视化器几何体已更新vis.poll_events() # 处理UI事件(如窗口缩放)vis.update_renderer() # 重绘场景# 可选:保存当前帧截图if save_image:vis.capture_screen_image("temp_%04d.jpg" % i)# 关闭窗口vis.destroy_window()o3d.utility.set_verbosity_level(o3d.utility.VerbosityLevel.Info) # 恢复日志级别if __name__ == '__main__':demo_non_blocking_visualization()
关键点:
-
非阻塞可视化三要素:
-
update_geometry()
:标记需要更新的几何体 -
poll_events()
:处理用户交互事件 -
update_renderer()
:触发画面重绘
-
-
ICP配置:
-
TransformationEstimationPointToPlane
:使用点到面ICP(比点到点更鲁棒) -
max_iteration=1
:每次外部循环只做一次ICP迭代,实现逐步可视化
-
此脚本调用每次迭代。请注意,它通过 .这是从单个 ICP 迭代中检索轻微姿势更新的技巧。在 ICP 之后,源几何体会相应地变换。registration_icp
ICPConvergenceCriteria(max_iteration = 1)