Python Open3D几何图形 基础篇(一) 点云操作

Python Open3D几何图形 基础篇(一) 点云操作

本文参考的页面:

  • Point cloud — Open3D 0.15.1 documentation
  • File IO — Open3D 0.15.1 documentation

本文主要是介绍 Open3D中,点云的基本用法:

可视化点云(Visualize point cloud)

如何去读取点云并且将其可视化:

read_point_cloud从文件读取点云,它将会根据后缀名来解析点云文件,具体的类型需要参考File IO
draw_geometries可视化点云,使用鼠标可以从不同角度查看它
可视化的API可以查看 该页面

这看起来想密集的表面,但是实际上是点云被渲染成了表面图元(surfels)。这个GUI界面支持不同的按键功能

  • 按下 "-"键 可以减少 表面图元的尺寸
  • 按下 "H"键 可以打印出GUI所有的键盘指令列表
  • 自定义可视化可以参考 自定义可视化页面
  • 在macOS中GUI可能接收不到键盘事件,可以尝试pythonw 替代python

示例代码:

print("Load a ply point cloud, print it, and render it") # 加载PLY点云,打印它并显示它
ply_point_cloud = o3d.data.PLYPointCloud() # 获取点云数据,如果下载不成功,可以删除这行代码
pcd = o3d.io.read_point_cloud(ply_point_cloud.path) # ply_point_cloud.path,可以换成自己的点云文件的地址
print(pcd)
print(np.asarray(pcd.points))
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]
""" 
Load a ply point cloud, print it, and render it
[Open3D INFO] Downloading https://github.com/isl-org/open3d_downloads/releases/download/20220201-data/fragment.ply
[Open3D INFO] Downloaded to /home/runner/open3d_data/download/PLYPointCloud/fragment.ply
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]]
"""                                 

Python Open3D几何图形 基础篇(一) 点云操作_第1张图片

体素下采样(Voxel downsampling)

体素下采样使用一个规则的体素网格,从输入的点云创建一个均匀下采样的点云。它经常被用作许多点云处理任务的预处理步骤。算法分两步操作:

  1. 点从体素中得到(Points are bucketed into voxels.)
  2. 每个占用的体素会平均内部的点准确的生成一个点(Each occupied voxel generates exactly one point by averaging all points inside.)
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],
                                  zoom=0.3412,
                                  front=[0.4257, -0.2125, -0.8795],
                                  lookat=[2.6172, 2.0475, 1.532],
                                  up=[-0.0694, -0.9768, 0.2024])

Python Open3D几何图形 基础篇(一) 点云操作_第2张图片

估计顶点法向(Vertex normal estimation)

另外的基本点云操作是点的法向估计。在操作界面上按下“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],
                                  zoom=0.3412,
                                  front=[0.4257, -0.2125, -0.8795],
                                  lookat=[2.6172, 2.0475, 1.532],
                                  up=[-0.0694, -0.9768, 0.2024],
                                  point_show_normal=True)

Python Open3D几何图形 基础篇(一) 点云操作_第3张图片

  • estimate_normals 可以计算每个点的法向量。这个函数可以寻找附近点并且使用协方差分析(Covariance Analysis)计算附近点的主轴(Principal Axis)。
  • 该函数将KDTreeSearchParamHybrid类的一个实例作为参数。两个关键参数radius = 0.1max_nn = 30指定搜索半径和最大最近邻。它有10厘米的搜索半径,最多只考虑30个邻居,以节省计算时间。

协方差分析算法生成两个相反的方向作为候选法向方向。
在不知道几何体整体结构的情况下,两个方向都可能是对的。这也就是所谓的法向方向问题。
Open3D会尝试确定法线方向,使其与原始的法线(如果存在)对齐。否则,Open3D会进行随机猜测。
如果要考虑方向的话,可以调用进阶的方向函数如orient_normals_to_align_with_directionorient_normals_towards_camera_location

查看估计的顶点法向(Access estimated vertex normal)

  • 可以通过downpcd 的成员变量normals 来检索估计的法向量

    print("Print a normal vector of the 0th point")
    print(downpcd.normals[0])
    
    # Print a normal vector of the 0th point
    # [-0.27566603 -0.89197839 -0.35830543]
    
    
  • 如果要查看其他相关的变量可以使用help(downpcd)

  • 法向量可以通过np.asarray转换为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.27566603 -0.89197839 -0.35830543]
       [-0.04230441 -0.99410664 -0.09981149]
       [-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.27498453 -0.67317361 -0.68645524]
       [-0.11728718 -0.95516445 -0.27185399]
       [-0.00816546 -0.99965616 -0.02491762]
       [-0.11067463 -0.99205156 -0.05987351]]
      """
      
      
  • 与Numpy相关的函数例子可以看 这个链接

裁剪点云(Crop point cloud)

print("Load a polygon volume and use it to crop the original point cloud")
demo_crop_data = o3d.data.DemoCropPointCloud()
pcd = o3d.io.read_point_cloud(demo_crop_data.point_cloud_path)
vol = o3d.visualization.read_selection_polygon_volume(demo_crop_data.cropped_json_path)
chair = vol.crop_point_cloud(pcd)
o3d.visualization.draw_geometries([chair],
                                  zoom=0.7,
                                  front=[0.5439, -0.2333, -0.8060],
                                  lookat=[2.4615, 2.1331, 1.338],
                                  up=[-0.1781, -0.9708, 0.1608])

Python Open3D几何图形 基础篇(一) 点云操作_第4张图片

read_selection_polygon_volume 读取指定多边形选择区域的json文件。
vol.crop_point_cloud(pcd) 过滤其他点,只剩下椅子。

点云上色(Paint point cloud)

print("Paint chair")
chair.paint_uniform_color([1, 0.706, 0])
o3d.visualization.draw_geometries([chair],
                                  zoom=0.7,
                                  front=[0.5439, -0.2333, -0.8060],
                                  lookat=[2.4615, 2.1331, 1.338],
                                  up=[-0.1781, -0.9708, 0.1608])

Python Open3D几何图形 基础篇(一) 点云操作_第5张图片

paint_uniform_color 使用统一的颜色为所有点上色。上的色在RGB空间中,范围是0到1。

点云距离(Point cloud distance)

  • Open3D 提供了compute_point_cloud_distance 方法去计算目标点云与源点云之间的距离。即它可以计算源点云每个点与目标点云最近点之间的距离。
  • 下面的例子,我们使用这个函数去计算两个点云的差异。要注意的是这个方法也可以在两个点云间计算倒角距离(Chamfer distance)
# Load data
demo_crop_data = o3d.data.DemoCropPointCloud()
pcd = o3d.io.read_point_cloud(demo_crop_data.point_cloud_path)
vol = o3d.visualization.read_selection_polygon_volume(demo_crop_data.cropped_json_path)
chair = vol.crop_point_cloud(pcd)

dists = pcd.compute_point_cloud_distance(chair)
dists = np.asarray(dists)
ind = np.where(dists > 0.01)[0]
pcd_without_chair = pcd.select_by_index(ind)
o3d.visualization.draw_geometries([pcd_without_chair],
                                  zoom=0.3412,
                                  front=[0.4257, -0.2125, -0.8795],
                                  lookat=[2.6172, 2.0475, 1.532],
                                  up=[-0.0694, -0.9768, 0.2024])

Python Open3D几何图形 基础篇(一) 点云操作_第6张图片

包围盒(Bounding volumes)

PointCloud几何体类型与Open3D中的其他所有的几何体类型一样具有包围盒。
目前,Open3D实现AxisAlignedBoundingBoxOrientedBoundingBox 方法也可以用于裁剪几何体。

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],
                                  zoom=0.7,
                                  front=[0.5439, -0.2333, -0.8060],
                                  lookat=[2.4615, 2.1331, 1.338],
                                  up=[-0.1781, -0.9708, 0.1608])

Python Open3D几何图形 基础篇(一) 点云操作_第7张图片

凸包(Convex hull)

  • 点云的凸包是包含所有点的最小凸集。Open3D中有compute_convex_hull 方法用于计算点云的凸包,这个的实现是通过Qhull实现的。
  • 在下面的样例代码中,首先从网格中采样一个点云,然后计算作为三角形网格返回的凸包。然后使用红色的LineSet 可视化凸包。
bunny = o3d.data.BunnyMesh()
mesh = o3d.io.read_triangle_mesh(bunny.path)
mesh.compute_vertex_normals()

pcl = 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])

Python Open3D几何图形 基础篇(一) 点云操作_第8张图片

DBSCAN 聚类算法(DBSCAN clustering)

  • 给定一个点云,例如深度传感器,我们想要将本地点云群集分组在一起。为此,我们可以使用聚类算法。Open3D实现了DBSCAN [Ester1996]
    这是基于密度聚类算法。
  • 这个算法是通过cluster_dbscan 实现的,这个方法需要两个参数epsmin_points
    • eps 定义了在聚类中邻居之间的距离,
    • min_points 定义了形成聚类所需要的最小点数。
    • 这个函数将会返回labels 其中标签-1表示噪声。
  • 示例代码:

    ply_point_cloud = o3d.data.PLYPointCloud()
    pcd = o3d.io.read_point_cloud(ply_point_cloud.path)
    
    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],
                                      zoom=0.455,
                                      front=[-0.4999, -0.1659, -0.8499],
                                      lookat=[2.1813, 2.0619, 2.0999],
                                      up=[0.1204, -0.9852, 0.1215])
    

    Python Open3D几何图形 基础篇(一) 点云操作_第9张图片

  • 这个算法会提前计算半径为ε 的所有邻居。如果选择很大的ε的话将会需要很大的内存。

平面分割(Plane segmentation)

  • Open3D还支持使用RANSAC从点云分割几何基元(geometric primitives)。
  • 我们可以使用segment_plane 在点云中寻找最大支撑的平面。这个方法有3个参数distance_thresholdransac_nnum_iterations
    • distance_threshold 定义了一个点到估计平面中内点的最大距离。(the maximum distance a point can have to an estimated plane to be considered an inlier)
    • ransac_n 定义了在估计一个平面上随机采样的点数。
    • num_iterations 定义了随机平面的采样和验证频率。
    • 函数会返回平面$(a,b,c,d) 也 就 是 每 一 个 点 也就是每一个点 (x,y,z) 在 在 ax+by+cz+d=0$上
    • . The function further returns a list of indices of the inlier points.
  • 代码

    pcd_point_cloud = o3d.data.PCDPointCloud()
    pcd = o3d.io.read_point_cloud(pcd_point_cloud.path)
    
    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],
                                      zoom=0.8,
                                      front=[-0.4999, -0.1659, -0.8499],
                                      lookat=[2.1813, 2.0619, 2.0999],
                                      up=[0.1204, -0.9852, 0.1215])
    # Plane equation: -0.06x + -0.10y + 0.99z + -1.06 = 0
    

Python Open3D几何图形 基础篇(一) 点云操作_第10张图片

隐藏点移除(Hidden point removal)

  • 假设你要从给定的视点去渲染点云,但背景中的点将会在前景中漏出来,因为这些点不会被其他点遮挡。所以,我们可以使用隐藏点移除算法。
  • 在Open3D中是通过 [Katz2007]来实现这个算法的,这算法是基于给定视图近似点云的可见性,不需要进行曲面重建或法线估计。
  • 代码

    print("Convert mesh to a point cloud and estimate dimensions")
    armadillo = o3d.data.ArmadilloMesh()
    mesh = o3d.io.read_triangle_mesh(armadillo.path)
    mesh.compute_vertex_normals()
    
    pcd = 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])
    

Python Open3D几何图形 基础篇(一) 点云操作_第11张图片

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])

Python Open3D几何图形 基础篇(一) 点云操作_第12张图片

你可能感兴趣的:(Open3D,python,计算机视觉)