Open3D 学习笔记:点云、KD-Tree、Octree 与基础处理
围绕 Open3D 中常见的数据结构、点云读写与可视化、KD-Tree / Octree、滤波与法线估计做一版更适合复习的整理。
这篇内容基于原始 Notion 导出整理,保留原笔记的知识主线,同时清理了导入残留、代码块污染和不自然的标题层级,方便后续复习与继续补充。
一、Open3D 是什么
Open3D 是一个常用的 3D 数据处理库,适合做:
- 点云读写与可视化
- 点云/网格/RGB-D 数据结构操作
- 邻域搜索
- 滤波、法线估计、配准、重建等基础几何处理
对后续 3D 视觉、点云处理或配准实验而言,Open3D 基本可以看作一个很好用的工具箱。

这里先把最常见的几个基础模块整理清楚:
- 点云、网格、RGB-D 这三类核心数据结构
- 点云的读写与可视化
- KD-Tree 与 Octree
- 常见滤波
- 法线估计
二、Open3D 中常见的数据结构
1. 点云(Point Cloud)
点云是最常见的 3D 数据形式,本质上是一组三维点的集合。一个点云对象中常见的数据包括:
- 点坐标
points - 颜色
colors - 法向量
normals
import open3d as o3d
pcd = o3d.geometry.PointCloud()
pcd.points # 点的坐标 (x, y, z)
pcd.colors # 点的颜色 (r, g, b),可选
pcd.normals # 点的法向量 (nx, ny, nz),可选
2. 网格(Mesh)
网格更适合表达连续表面,通常由顶点和三角面片组成。
import open3d as o3d
mesh = o3d.geometry.TriangleMesh()
mesh.vertices # 顶点坐标
mesh.triangles # 三角面片(顶点索引)
mesh.vertex_colors # 顶点颜色,可选
mesh.vertex_normals # 顶点法向量,可选
mesh.triangle_normals # 面片法向量,可选
3. RGB-D 图像
RGB-D 图像可以理解为“彩色图 + 深度图”的组合。每个像素不仅有颜色信息,还有到相机的距离信息。
import open3d as o3d
rgbd = o3d.geometry.RGBDImage()
rgbd.color # RGB 图像
rgbd.depth # 深度图像
直观上可以这样理解:
- RGB 图像对应像素的颜色信息
- 深度图对应像素到相机的距离信息
这也是很多三维重建流程的起点。
4. 三者之间的关系
在很多任务里,这三类数据并不是孤立的,而是能相互转换:
- RGB-D → 点云:根据相机内参把像素反投影到三维空间
- 点云 → 网格:通过曲面重建方法恢复连续表面
也就是说,常见链路可以概括为:
RGB-D 图像 → 点云 → 网格
5. RGB-D 转点云
import open3d as o3d
color = o3d.io.read_image("color.jpg")
depth = o3d.io.read_image("depth.png")
rgbd = o3d.geometry.RGBDImage.create_from_color_and_depth(color, depth)
intrinsic = o3d.camera.PinholeCameraIntrinsic(
width=640,
height=480,
fx=525.0,
fy=525.0,
cx=319.5,
cy=239.5,
)
pcd = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd, intrinsic)
这一步里最关键的是相机内参,因为它决定了二维像素如何被映射到三维坐标系中。
6. 点云转网格
点云转网格本质上属于表面重建问题,常见方法包括:
- 泊松重建(Poisson Reconstruction)
- Ball Pivoting
- Alpha Shapes
# 方法 1:泊松表面重建
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
pcd, depth=9
)
# 方法 2:Ball Pivoting
radii = [0.005, 0.01, 0.02, 0.04]
mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_ball_pivoting(
pcd, o3d.utility.DoubleVector(radii)
)
# 方法 3:Alpha Shapes
mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_alpha_shape(
pcd, alpha=0.03
)
这里要注意:不同重建方法对点云密度、噪声和法线质量都比较敏感,实际使用时通常要先做滤波和法线估计。
三、点云读写
1. 基本读写接口
import open3d as o3d
print("-> 正在加载点云...")
pcd = o3d.io.read_point_cloud("test.pcd")
print(pcd)
print("-> 正在保存点云...")
o3d.io.write_point_cloud("write.pcd", pcd, write_ascii=True)
常用接口就是:
o3d.io.read_point_cloud()o3d.io.write_point_cloud()
2. 常见点云文件格式
PLY
PLY 比较适合存储顶点、颜色、面片等信息。

PCD
PCD 是点云处理中非常常见的一种格式,常见头信息包括:

FIELDS:数据字段,例如x y z rgbSIZE:每个字段所占字节数TYPE:字段数据类型,例如F表示 floatCOUNT:每个字段的通道数WIDTH/HEIGHT:描述点云组织形式VIEWPOINT:采集视角或传感器位姿POINTS:点总数DATA:存储方式,例如ascii、binary
这里有一个值得记一下的问题:
VIEWPOINT背后会涉及位姿表示,所以后面确实有必要继续补四元数、旋转矩阵、欧拉角之间的关系。
这部分和点云配准、相机位姿估计都很相关。
四、点云可视化
最常见的可视化接口是:
import open3d as o3d
pcd = o3d.io.read_point_cloud("test.pcd")
o3d.visualization.draw_geometries([pcd])
如果要同时显示多个点云,也可以直接放进同一个列表里:
o3d.visualization.draw_geometries([pcd1, pcd2])
下图就是典型的点云显示效果:

五、KD-Tree 与 Octree
这两个结构本质上都是为了提高空间查询效率,但适用思路不一样。
5.1 KD-Tree
KD-Tree 是一种面向 k 维空间的二叉树结构。对三维点云来说,它通常会交替地按照 x、y、z 方向切分空间,因此非常适合做:
- 最近邻搜索
- 半径搜索
- 混合邻域搜索

在 Open3D 里建立 KD-Tree 很直接:
pcd_tree = o3d.geometry.KDTreeFlann(pcd)
1. K 近邻搜索
k = 5000
[num_k, idx_k, _] = pcd_tree.search_knn_vector_3d(pcd.points[1500], k)
返回值含义:
num_k:找到的邻域点数量idx_k:邻域点索引_:距离平方列表,很多时候可以忽略
如果想把邻域点染成蓝色,可以这样写:
import numpy as np
np.asarray(pcd.colors)[idx_k[1:], :] = [0, 0, 1]
这里 idx_k[1:] 的作用是跳过查询点本身。
2. 半径邻域与混合邻域
radius = 0.02
[num_radius, idx_radius, _] = pcd_tree.search_radius_vector_3d(
pcd.points[1500], radius
)
max_nn = 200
[num_hybrid, idx_hybrid, _] = pcd_tree.search_hybrid_vector_3d(
pcd.points[1500], radius, max_nn
)
- KNN:返回固定个数的最近邻
- Radius Search:返回固定半径内的所有点
- Hybrid Search:返回固定半径内、但不超过最大数量的点
3. 一个完整示例
import open3d as o3d
import numpy as np
pcd = o3d.io.read_point_cloud("bunny.pcd")
pcd.paint_uniform_color([0.5, 0.5, 0.5])
pcd_tree = o3d.geometry.KDTreeFlann(pcd)
# 查询点染成紫色
np.asarray(pcd.colors)[1500] = [0.5, 0.0, 0.5]
# K 近邻染成蓝色
k = 5000
[num_k, idx_k, _] = pcd_tree.search_knn_vector_3d(pcd.points[1500], k)
np.asarray(pcd.colors)[idx_k[1:], :] = [0, 0, 1]
# 半径邻域染成红色
radius = 0.02
[num_radius, idx_radius, _] = pcd_tree.search_radius_vector_3d(pcd.points[1500], radius)
np.asarray(pcd.colors)[idx_radius[1:], :] = [1, 0, 0]
# 混合邻域染成绿色
max_nn = 200
[num_hybrid, idx_hybrid, _] = pcd_tree.search_hybrid_vector_3d(
pcd.points[1500], radius, max_nn
)
np.asarray(pcd.colors)[idx_hybrid[1:], :] = [0, 1, 0]
o3d.visualization.draw_geometries([pcd])
5.2 Octree
Octree 是一种面向三维空间的树结构,每个节点对应一个立方体区域,并继续细分成 8 个子立方体。
它常用于:
- 空间划分
- 稀疏表示
- 体素化相关处理
- 空间查询
- 碰撞检测与渲染

1. 从点云创建 Octree
import open3d as o3d
pcd = o3d.io.read_point_cloud("tree.pcd")
octree = o3d.geometry.Octree(max_depth=4)
octree.convert_from_point_cloud(pcd, size_expand=0.01)
o3d.visualization.draw_geometries([octree])
这里 max_depth=4 表示树的最大深度。深度越大,空间划分越细,但开销也会增加。
2. 从体素栅格创建 Octree
import open3d as o3d
pcd = o3d.io.read_point_cloud("tree.pcd")
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd, voxel_size=0.2)
octree = o3d.geometry.Octree(max_depth=4)
octree.create_from_voxel_grid(voxel_grid)
o3d.visualization.draw_geometries([octree])
如果后面涉及大场景点云组织、稀疏表示或者搜索加速,Octree 是值得继续深入的。
六、点云滤波
点云处理中,滤波的目的通常是:
- 去掉离群点
- 做平滑
- 减少噪声
- 降低点数规模
原笔记里列了一个总表,核心可以先这样记:
- 统计滤波:去离群点
- 半径滤波:去孤立点
- 体素下采样:降采样
- 均值/中值滤波:平滑
- 直通滤波:按空间范围裁剪
1. 统计滤波示例
import open3d as o3d
pcd = o3d.io.read_point_cloud("desk.pcd")
num_neighbors = 20
std_ratio = 2.0
sor_pcd, ind = pcd.remove_statistical_outlier(num_neighbors, std_ratio)
sor_pcd.paint_uniform_color([0, 0, 1])
sor_noise_pcd = pcd.select_by_index(ind, invert=True)
sor_noise_pcd.paint_uniform_color([1, 0, 0])
o3d.visualization.draw_geometries([sor_pcd, sor_noise_pcd])
这里:
- 蓝色表示保留下来的点
- 红色表示被识别为噪声的点
这个接口在实际中很常用,因为统计滤波对离群噪声点有比较直观的处理效果。
2. 这一部分后续还可以继续补什么
原笔记里还列到了:
- 半径滤波
- 直通滤波
- 均值滤波
- 中值滤波
- 组合滤波
这部分后面很适合单独再扩成一篇“点云滤波专题”,因为它和点云质量、后续配准/重建效果直接相关。
七、点云特征提取:法线估计
法线是点云处理中非常基础的一类局部几何特征。很多任务都离不开它,比如:
- 表面重建
- 配准
- 曲率估计
- 局部特征描述
法线估计示例
import open3d as o3d
import numpy as np
pcd = o3d.io.read_point_cloud("bunny.pcd")
radius = 0.01
max_nn = 30
pcd.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=radius, max_nn=max_nn)
)
o3d.visualization.draw_geometries([pcd], point_show_normal=True)
print(np.asarray(pcd.normals)[:10, :])
这段代码本质上做了两件事:
- 在每个点附近找一小片邻域
- 用邻域几何关系估计该点法向量
这里又能看出 KD-Tree 的重要性:很多点云局部特征计算,本质上都依赖高效邻域搜索。
八、点云分割
点云分割的目标,是把一个大点云拆成更有意义的局部结构,比如:
- 不同物体
- 不同平面
- 不同区域
- 背景与前景
在 Open3D 的基础流程里,分割通常不是最后一步,而是很多下游任务前的预处理步骤。比如做目标提取、局部重建、局部配准时,先把点云拆开往往更有效。
8.1 DBSCAN 聚类分割
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类方法。它的优点是:
- 不需要提前指定簇的数量
- 能识别噪声点
- 对形状不规则的簇比较友好
在点云里,它很适合做“把局部连成片的点分成不同簇”这类任务。
Open3D 中常用接口是:
import open3d as o3d
import numpy as np
import matplotlib.pyplot as plt
pcd = o3d.io.read_point_cloud("cluster.pcd")
labels = np.array(pcd.cluster_dbscan(eps=0.02, min_points=10, print_progress=True))
max_label = labels.max()
print(f"点云共有 {max_label + 1} 个聚类")
colors = plt.get_cmap("tab20")(labels / (max_label if max_label > 0 else 1))
colors[labels < 0] = 0
pcd.colors = o3d.utility.Vector3dVector(colors[:, :3])
o3d.visualization.draw_geometries([pcd])
这两个参数最重要:
eps:邻域半径,决定多近的点会被看作一个局部密度区域min_points:最少邻域点数,决定一个区域是否足以形成簇
另外,Open3D 文档也提醒过:DBSCAN 在计算前会预计算 epsilon 半径内的邻居,如果 eps 设得太大,内存开销会明显上升。
所以这类参数一般要结合点云尺度来调:
- 点云尺度大,
eps通常也要更大 - 点云非常稠密,
min_points可以适当增大 - 如果噪声特别多,往往要先做滤波,再做聚类
8.2 DBSCAN 适合什么、不适合什么
它适合:
- 多个物体天然分离的场景
- 想直接找局部连通结构的场景
- 需要显式识别噪声点的情况
它不太适合:
- 不同区域密度差异特别大
- 大规模超密点云,参数非常难统一
- 物体本来就紧贴或交叠的场景
如果后面涉及桌面场景点云、物体分离或局部区域提取,DBSCAN 是一个值得优先上手的基础方法。
九、点云曲面重建
曲面重建的目标,是把离散点云恢复成连续曲面,也就是从“散点”变成“可渲染、可分析的表面网格”。
这一步在很多任务里都很重要,比如:
- 3D 建模
- 逆向工程
- 可视化展示
- 几何分析
- 后续网格处理
9.1 为什么重建前通常要先估计法线
很多曲面重建方法不仅需要点的位置,还依赖点的局部方向信息,也就是法线。尤其是:
- Poisson Surface Reconstruction
- Ball Pivoting
如果法线估计不准,或者方向不一致,生成的网格很容易:
- 破碎
- 过度平滑
- 出现错误连接
- 局部翻面
所以比较标准的流程通常是:
点云 → 滤波 / 下采样 → 法线估计 → 法线方向一致化 → 曲面重建
9.2 Alpha Shapes
Alpha Shapes 可以理解成凸包的一个推广,通过 alpha 参数控制最终保留几何细节的程度。
mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_alpha_shape(
pcd, alpha=0.03
)
特点是:
- 参数直观
- 适合快速试验
- 对局部细节有一定表达能力
但它对参数比较敏感,alpha 不同,结果可能差很多。
9.3 Ball Pivoting
Ball Pivoting Algorithm(BPA)可以直观理解成:
拿一个给定半径的小球在点云表面“滚动”,当球同时接触 3 个点时就形成一个三角形。
radii = [0.005, 0.01, 0.02, 0.04]
mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_ball_pivoting(
pcd, o3d.utility.DoubleVector(radii)
)
它比较适合:
- 点分布相对均匀
- 法线质量较好
- 希望表面细节尽量被保留
9.4 Poisson 重建
Poisson Surface Reconstruction 是很常见的曲面重建方法。它通常能生成比较平滑、整体性较强的网格。
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
pcd, depth=9
)
其中:
depth可以理解成控制重建分辨率的重要参数- 数值越大,结果可能越细,但计算量和内存开销也越大
Poisson 的优点是:
- 结果通常比较完整和平滑
- 对整体表面恢复效果较好
缺点是:
- 容易“补洞”过头
- 对噪声、法线和边界条件比较敏感
如果目标是“展示一个完整表面”,Poisson 常常是很实用的起点;如果更在意局部几何边界,BPA 或其他方法有时会更合适。
十、点云空间变换
点云空间变换本质上就是把点从一个坐标系映射到另一个坐标系。这在 3D 视觉里几乎到处都会遇到,比如:
- 相机坐标系 → 世界坐标系
- 局部坐标系 → 全局坐标系
- 单帧点云 → 配准到另一帧点云
10.1 常见变换类型
最常见的是刚体变换,包括:
- 旋转(Rotation)
- 平移(Translation)
如果用齐次矩阵表示,一般写成 4×4 形式:
[ T = \begin{bmatrix} R & t \ 0 & 1 \end{bmatrix} ]
其中:
R是 3×3 旋转矩阵t是 3×1 平移向量
10.2 Open3D 中的基本变换操作
import open3d as o3d
import numpy as np
pcd = o3d.io.read_point_cloud("test.pcd")
T = np.array([
[1, 0, 0, 0.1],
[0, 1, 0, 0.0],
[0, 0, 1, 0.2],
[0, 0, 0, 1.0],
])
pcd.transform(T)
除此之外,还会常见到:
translate():平移rotate():旋转scale():缩放transform():应用完整齐次变换矩阵
10.3 为什么这一节重要
很多刚接触点云的人会把“配准”和“变换”分开看,但实际上:
- 配准的输出,本质上就是一个空间变换矩阵
- 配准算法的目标,就是求出把 source 对齐到 target 的那组变换参数
如果后面要认真做配准,旋转矩阵、平移、齐次变换、坐标系切换这些内容都需要真正吃透。
十一、点云配准
点云配准的目标,是求出两个点云之间的空间变换,使它们在同一坐标系下尽可能对齐。
这是点云处理中最核心的任务之一,尤其在下面这些场景里特别常见:
- 多视角拼接
- SLAM / 建图
- 3D 重建
- 工业测量
- 扫描数据融合
11.1 配准一般分成两步
一个很经典的思路是:
- 粗配准(Global Registration)
- 不依赖很好的初始位姿
- 目标是先找到一个大致正确的对齐关系
- 精配准(Local Registration)
- 在粗配准结果基础上进一步优化
- 常见代表就是 ICP
Open3D 文档里也明确区分了这一点:
- Global registration 用来给初始化
- ICP 这类 local method 用来做精细对齐
11.2 FPFH + RANSAC 粗配准
如果 source 和 target 一开始差得比较远,通常不能直接上 ICP,因为它容易陷入局部最优。
所以常见做法是:
- 先下采样
- 再估计法线
- 再计算 FPFH 特征
- 然后用 RANSAC 找匹配并估计初始位姿
这里 FPFH(Fast Point Feature Histogram)本质上是一种局部几何特征描述子,能把一个点附近的几何形状编码成特征向量,方便做点之间的粗匹配。
11.3 ICP 精配准
ICP(Iterative Closest Point)是最经典的精配准方法之一。它的基本思路是反复执行两步:
- 建立当前最近邻对应关系
- 更新变换矩阵,使对应误差最小
Open3D 里常见两种版本:
- Point-to-Point ICP
- Point-to-Plane ICP
Point-to-Point ICP
目标函数可以理解成:
让对应点之间的欧氏距离平方和尽量小
它比较直观,也容易理解。
Point-to-Plane ICP
它不是直接最小化点到点距离,而是最小化:
source 点到 target 对应点切平面的距离
这种方法通常收敛更快,也常常更稳定,但前提是法线估计要靠谱。
Open3D 文档里也提到,point-to-plane ICP 在很多情况下比 point-to-point ICP 收敛更快。
11.4 配准结果怎么评估
Open3D 提供了比较实用的评估接口 evaluate_registration(),常见两个指标是:
- fitness:内点对应比例,可以粗略理解成重叠程度,越高越好
- inlier_rmse:内点对应的均方根误差,越低越好
如果后面要做实验,不能只看“图上好像对齐了”,还应该结合:
- fitness
- inlier RMSE
- 可视化重叠效果
- 局部区域误差
一起判断配准质量。
11.5 一条最常见的 Open3D 配准路线
一个非常经典的流程就是:
voxel_down_sampleestimate_normalscompute_fpfh_featureregistration_ransac_based_on_feature_matchingregistration_icpevaluate_registration
如果后面涉及多视角点云拼接,这条路线非常值得先彻底跑通。
十二、常用算法
如果从“点云基础处理”这个角度整理,Open3D 里最常见、也最值得优先掌握的一批算法可以按下面分:
12.1 预处理类
- Voxel Down Sampling:体素下采样,减少点数、统一密度
- Statistical Outlier Removal:统计滤波,去除离群点
- Radius Outlier Removal:半径滤波,去除局部孤立点
- Crop / Bounding Box:裁剪指定区域
12.2 邻域与空间组织类
- KD-Tree:高效最近邻搜索
- Octree:空间层次划分与稀疏组织
- Voxel Grid:体素网格表达
12.3 分割类
- DBSCAN:基于密度的聚类分割
- RANSAC Plane Segmentation:平面分割
12.4 特征与几何分析类
- 法线估计
- FPFH 特征
- 点云距离计算
- 凸包 / 包围盒
12.5 重建类
- Alpha Shapes
- Ball Pivoting
- Poisson Surface Reconstruction
12.6 配准类
- RANSAC 全局配准
- Fast Global Registration (FGR)
- Point-to-Point ICP
- Point-to-Plane ICP
如果是为了后续实验和毕设,最值得优先掌握的大致是这几项:
- 下采样与滤波
- 法线估计
- KD-Tree 邻域搜索
- FPFH 特征
- RANSAC + ICP 配准流程
- 配准结果评估
十三、这篇笔记里最该记住的几点
如果只快速回顾,这篇至少记住下面这些:
- Open3D 的核心对象:点云、网格、RGB-D
- RGB-D 可以反投影成点云,点云可以进一步重建成网格
- KD-Tree 适合邻域搜索,Octree 适合空间分割与稀疏组织
- 滤波、分割和法线估计是很多下游任务前的基础步骤
- 点云配准通常不是一步完成,而是粗配准 + 精配准的组合
- 点云处理不是一招解决问题,而是一条流程链:读入 → 清洗 → 分割 / 邻域 → 特征 → 配准 / 重建