考虑到大部分使用open3d的都不算是初级用户,故而先介绍open3d中提供的接口,而后对kd树的原理进行说明。
import open3d as o3d
pcd = o3d.io.read_point_cloud('rabbit.pcd')
# 通过点云创建kd树
kdt = o3d.geometry.KDTreeFlann(pcd)
kd树,和其他树一样,有一个重要的功能就是提高索引速度,将原本的
O
(
n
)
O(n)
O(n)复杂度降低到类似
O
(
ln
n
)
O(\ln n)
O(lnn)的水平。KDTreeFlann针对索引半径、索引范围,主要提供了三种索引方式
# 通过距离和最大点数来索引
search_hybrid_vector_3d(query, radius, max_nn)
# 通过点数索引
search_knn_vector_3d(self, query, knn)
# 通过半径索引
search_radius_vector_3d(self, query, radius)
# 综合索引方法, search_param为专门为kd树提供的参数对象
search_vector_3d(self, query, search_param)
对于这四种3d方法,都有一个与之对应的xd版本,可以用于更高或者更低维度的kd-tree的索引。
下面演示一下knn和半径搜索的方法
pcd.paint_uniform_color([0.5, 0.5, 0.5])
k, idx, _ = kdt.search_knn_vector_3d(pcd.points[250], 200)
for i in idx:
pcd.colors[i] = [0, 0, 1]
pcd.colors[250] = [1,0,0]
得到结果如下

搜索返回的参数中,k即搜索到的点数,idx为点的序号。由于knn方法指定了搜索个数为200,故k=200。相比之下,radius方法返回的点数数目就不这么确定了
pcd.paint_uniform_color([0.5, 0.5, 0.5])
k, idx, _ = kdt.search_radius_vector_3d(pcd.points[250], 0.2)
for i in idx:
pcd.colors[i] = [0, 0, 1]
pcd.colors[250] = [1,0,0]
最终得到k=8,由于点数过少,所以下面的图单独把兔耳朵放大

考虑到演示方便,所以用二维空间中的数据来做一点说明,假设现有六个数据点{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},如何能够通过这六个点将空间分割,就如下图一样。

当然上面这个图只是一种方案,或许还有其他的划分方式。
对此例来说,其分割步骤为
而这个生成分割图的过程,也是生成kd树的过程,其树图为