Open3d学习计划——3(点云)

点云(Point Cloud)

这篇文章将会介绍点云数据的一些基本用法。(本教程可视化的点云数据为官方图片,自己可以根据手头数据进行测试,或者去官方github主页下载对应测试数据

可视化点云

本教程的第一部分是读取点云数据并将其可视化。

print("Load a ply point cloud, print it, and render it")
pcd = o3d.io.read_point_cloud("../../TestData/fragment.ply")
print(pcd)
print(np.asarray(pcd.points))
o3d.visualization.draw_geometries([pcd])


>>>>Load a ply point cloud, print it, and render it
>>>>geometry::PointCloud with 196133 points.
  [[0.65234375 0.84686458 2.37890625]
   [0.65234375 0.83984375 2.38430572]
   [0.66737998 0.83984375 2.37890625]
   ...
   [2.00839925 2.39453125 1.88671875]
   [2.00390625 2.39488506 1.88671875]
   [2.00390625 2.39453125 1.88793314]]

在这里插入图片描述
read_point_cloud 用来读取点云数据。他尝试通过文件扩展名来解码文件。支持的文件格式在上一节有介绍。
draw_geometries 可视化点云数据。使用鼠标可以查看不同视角的数据。
这个图看着像一个密集表面,实际上还是由无数个点组成。可视化的GUI支持多个快捷键。比如可以通过 - 键来缩小点云中点的尺寸。

Note:

在GUI窗口按 h 键可以输出所有快捷键的列表,关于可视化更多的信息,后面会在介绍。

Note:

在OS X,GUI的快捷键可能不会生效,请使用pythonw替代python来启动Python

体素降采样

体素降采样通过使用规则体素网格从输入点云创造一致化降采样点云。这通常在点云处理任务的预处理步骤,这个算法分为两步:

  1. 把点云装进体素网格
  2. 把每个被占据的体素中的点做平均,取一个精确的点。
print("Downsample the point cloud with a voxel of 0.05")
downpcd = pcd.voxel_down_sample(voxel_size=0.05)
o3d.visualization.draw_geometries([downpcd])

>>>>Downsample the point cloud with a voxel of 0.05

在这里插入图片描述

顶点法线估计

点云的基本操作还包括定点法线估计。按 n 查看点云法线。使用 - 和 + 可以缩放法线长度。

print("Recompute the normal of the downsampled point cloud")
downpcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))
o3d.visualization.draw_geometries([downpcd], 
                                  point_show_normal=True)

>>>>Recompute the normal of the downsampled point cloud                               

在这里插入图片描述
estimate_normals 计算每个点的法线。这个函数通过对邻点采用协方差分析(covariance analysis)来计算主轴。
这个函数使用KDTreeSearchParamHybrid类的一个实例作为参数。这里有两个关键参数是radius = 0.1 & max_nn = =30 ,用来设置搜索半径和邻域最大点数。这里设置搜索半径为10cm,并且只考虑邻域内的30个点,以此来节约计算时间。

Note:

协方差分析会产生两个方向相反的法线候选,如果不考虑全局结构的话,二者都是正确的。这就是所谓的法向问题。如果法线存在open3d会尝试将法线定位与原始法线对齐。否则open3d将会随机选择。如果需要考虑朝向的话,可以使用函数:orient_normals_to_align_with_direction和orient_normals_towards_camera_location。

检索估计的顶点法线

通过downpcd的normals参数可以检索估计的点的法线。

print("Print a normal vector of the 0th point")
print(downpcd.normals[0])

>>>>Print a normal vector of the 0th point
>>>>[-0.21838377 -0.94240442 -0.25334252]

请使用help(downpcd)来查看其他变量。法线向量可以通过np.asarry来转化成numpy数组。

print("Print the normal vectors of the first 10 points")
print(np.asarray(downpcd.normals)[:10, :])

>>>>Print the normal vectors of the first 10 points
>>>>[[-0.21838377 -0.94240442 -0.25334252]
     [-0.39147152 -0.43746664 -0.8095511 ]
     [-0.00694405 -0.99478075 -0.10179902]
     [-0.00399871 -0.99965423 -0.02598917]
     [-0.93768261 -0.07378998  0.3395679 ]
     [-0.43476205 -0.62438493 -0.64894177]
     [-0.09739809 -0.9928602  -0.06886388]
     [-0.11728718 -0.95516445 -0.27185399]
     [-0.01038945 -0.99968858 -0.02268921]
     [-0.00816546 -0.99965616 -0.02491762]]

更多和numpy有关的信息后续会讲解,也可以直接查看有关的官方文档

裁剪点云

print("Load a polygon volume and use it to crop the original point cloud")
vol = o3d.visualization.read_selection_polygon_volume("../../TestData/Crop/cropped.json")
chair = vol.crop_point_cloud(pcd)
o3d.visualization.draw_geometries([chair])

>>>>加载一个多边形卷并使它裁剪点云

在这里插入图片描述
read_selection_polygon_volume函数是读取一个指定多边形区域得json文件。
vol.crop_point_cloud(pcd)过滤掉点,只保留椅子部分。

点云上色

print("Paint chair")
chair.paint_uniform_color([1, 0.706, 0])
o3d.visualization.draw_geometries([chair])

椅子上色
在这里插入图片描述
paint_uniform_color给所有的点上一个统一的颜色,颜色是在RGB空间得[0,1]范围内得值。

包围框

点云几何类型和其他类型一样,也有包围框。当前,open3d实现了两个包围框接口,同时他们也可以用来裁剪几何图形。

aabb = chair.get_axis_aligned_bounding_box()
aabb.color = (1,0,0)
obb = chair.get_oriented_bounding_box()
obb.color = (0,1,0)
o3d.visualization.draw_geometries([chair, aabb, obb])

在这里插入图片描述

凸包

点云得凸包是包含所有点的最小凸集。open3d实现了计算凸包的方法:compute_convex_hull.这个接口的实现基于Qhull。关于凸包的中文解释我这里给出百度百科链接
下面的代码中首先从三角网格中采样一个点云,之后计算凸包并且返回,返回类型为三角网格。最后将会用红色的线可视化凸包。

pcl = o3dtut.get_bunny_mesh().sample_points_poisson_disk(number_of_points=2000)
hull, _ = pcl.compute_convex_hull()
hull_ls = o3d.geometry.LineSet.create_from_triangle_mesh(hull)
hull_ls.paint_uniform_color((1, 0, 0))
o3d.visualization.draw_geometries([pcl, hull_ls])

downloading bunny mesh
extract bunny mesh

在这里插入图片描述

DBSCAN 聚类

给定一个点云,比如深度传感器,我们想将局部的点分组/聚合在一起,这时我们就需要聚类算法。open3d实现了DBSCAN[Ester1996] 算法,这是一种基于密度的聚类算法。(这里上面的链接里包含原论文名称,需要可自行查找,这里我也给出百度百科链接****建议大家看原论文)。该算法接口为cluster_dbscan,有两个必须的参数:eps表示聚类的领域距离,min_points表示聚类的最小点数。该函数返回一个label,其中label为-1表示为噪声。



pcd = o3d.io.read_point_cloud("../../TestData/fragment.ply")

with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug) as cm:
    labels = np.array(pcd.cluster_dbscan(eps=0.02, min_points=10, print_progress=True))

max_label = labels.max()
print(f"point cloud has {max_label + 1} clusters")
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])

>>>>point cloud has 10 clusters

在这里插入图片描述

Note:

该算法会对所有在半径内得所有点的邻域进行预计算,如果半径选的过大会占用大量的内存。

平面分割

open3d还支持使用RANSAC从点云中分割几何基元。要查找点云中最有可能存在的平面,我们使用segement_plane函数。这个函数需要三个参数,destance_threshold定义了一个点到一个估计平面的最大距离,这些距离内的点被认为是内点(inlier),ransac_n定义了使用随机抽样估计一个平面的点的个数,num_iterations定义了随机平面采样和验证的频率(迭代次数)。这个函数返回(a,b,c,d)作为一个平面,对于平面上每个点(x,y,z)我们有ax+by+cz+d=0。这个函数还会返回内点索引的列表。

pcd = o3d.io.read_point_cloud("../../TestData/fragment.pcd")
plane_model, inliers = pcd.segment_plane(distance_threshold=0.01,
                                         ransac_n=3,
                                         num_iterations=1000)
[a, b, c, d] = plane_model
print(f"Plane equation: {a:.2f}x + {b:.2f}y + {c:.2f}z + {d:.2f} = 0")

inlier_cloud = pcd.select_by_index(inliers)
inlier_cloud.paint_uniform_color([1.0, 0, 0])
outlier_cloud = pcd.select_by_index(inliers, invert=True)
o3d.visualization.draw_geometries([inlier_cloud, outlier_cloud])
 
 >>>>Plane equation: -0.06x + -0.10y + 0.99z + -1.06 = 0                                 

在这里插入图片描述

隐点移除

假如你想从一个给定视角去渲染一个点云,但是背景的点在前景也会被看到,这是因为并没有其他点把它遮挡住。为此看可以使用一种隐点去除算法。open3d实现了[Katz2007] 文章中的算法,该算法从没有重建和法线估计的给定视角去近似可视化点云。

print("Convert mesh to a point cloud and estimate dimensions")
pcd = o3dtut.get_armadillo_mesh().sample_points_poisson_disk(5000)
diameter = np.linalg.norm(np.asarray(pcd.get_max_bound()) - np.asarray(pcd.get_min_bound()))
o3d.visualization.draw_geometries([pcd])

在这里插入图片描述

print("Define parameters used for hidden_point_removal")
camera = [0, 0, diameter]
radius = diameter * 100

print("Get all points that are visible from given view point")
_, pt_map = pcd.hidden_point_removal(camera, radius)

print("Visualize result")
pcd = pcd.select_by_index(pt_map)
o3d.visualization.draw_geometries([pcd])

在这里插入图片描述

如果翻译的有什么问题或者您有更好的意见,请评论交流!!!!

欢迎大家加入知识星球一起学习。

在这里插入图片描述

  • 47
    点赞
  • 202
    收藏
    觉得还不错? 一键收藏
  • 57
    评论
open3d是一个用于处理三维数据(点云、三维模型等)的开源库。点云配准是将两个或多个点云数据进行对齐的过程,以便在一个全局坐标系下进行比较、分析或重建。其中,四元数法是一种常用的点云配准方法。 四元数是一种用四个实数表示的扩充复数,可以用于描述旋转变换。在点云配准中,使用四元数法是因为其具有以下优势: 第一,四元数具有紧凑的表示形式,只需要四个实数即可表示旋转变换,相较于旋转矩阵的九个实数表示方式节省了存储空间,降低了计算复杂度。 第二,四元数法能够有效地避免了“万向锁”问题。万向锁是指在使用欧拉角进行坐标变换时,由于旋转过程中会出现奇点,导致旋转角度无法精确表示的问题。而四元数法不会出现这个问题,具有更好的数值稳定性。 在open3d中,点云配准的四元数法通常有以下几个步骤: 首先,计算两个点云之间的特征描述子,例如FPFH(Fast Point Feature Histograms)或SHOT(Signature of Histograms of Orientations)。这些描述子能够表示点云的局部几何信息。 然后,根据特征描述子的相似性,寻找初始的点对应关系。 接下来,通过最小化点云之间的误差指标,例如最小化点到平面的距离或最小化点到点的距离,来优化点对应关系,并计算出旋转矩阵。 将旋转矩阵转换为四元数表示,即可完成点云的配准过程。 四元数法是open3d中常用的点云配准方法之一,其能够高效地实现点云的准确对齐。
评论 57
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值