在许多情况下,我们希望生成密集的三维几何体,即三角形网格。然而,从多视点立体方法,或深度传感器,我们只能获得一个非结构化的点云。为了从非结构化输入中得到三角形网格,我们需要执行曲面重建。文献中有几种方法,Open3D目前实现了以下功能:
- Alpha shapes [Edelsbrunner1983]
- Ball pivoting [Bernardini1999]
- Poisson surface reconstruction [Kazhdan2006]
1. Alpha shapes
alpha形[Edelsbrunner1983]是凸壳的推广。正如这里所描述的那样,人们可以直观地把阿尔法形状想象成如下所示:想象一个巨大的冰激凌包含点S作为硬巧克力块。使用其中一个球形冰激凌勺子,我们可以雕刻出冰激凌块的所有部分,而不会撞到巧克力块,从而甚至可以在内部雕刻出孔(例如,通过简单地从外部移动勺子无法触及的部分)。我们最终会得到一个(不一定是凸的)由帽,弧和点限定的对象。如果我们现在把所有的面拉直成三角形和线段,我们就可以直观地描述S的alpha形状。
Open3D实现了一个方法create_from_point_cloud_alpha_shape
,该方法涉及到权衡参数alpha
。
import open3d as o3d
import numpy as np
import open3d_tutorial as o3dtut
mesh = o3dtut.get_bunny_mesh()
pcd = mesh.sample_points_poisson_disk(750)
o3d.visualization.draw_geometries([pcd])
alpha = 0.03
print(f"alpha={alpha:.3f}")
mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_alpha_shape(pcd, alpha)
mesh.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh], mesh_show_back_face=True)
实现是基于点云的凸包。如果我们想从一个给定的点云计算多个alpha形状,那么我们可以通过只计算一次凸包并传递它来从create_from_point_cloud_alpha_shape
来节省一些计算。
tetra_mesh, pt_map = o3d.geometry.TetraMesh.create_from_point_cloud(pcd)
for alpha in np.logspace(np.log10(0.5), np.log10(0.01), num=4):
print(f"alpha={alpha:.3f}")
mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_alpha_shape(
pcd, alpha, tetra_mesh, pt_map)
mesh.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh], mesh_show_back_face=True)
2. Ball pivoting
球旋转算法(BPA)[Bernardini1999]是一种与α形状相关的曲面重建方法。直观地说,想象一个给定半径的三维球,我们把它放在点云上。如果它碰到任何3个点(并且它没有穿过这3个点),它就会创建一个三角形。然后,该算法从现有三角形的边开始旋转,每次它击中球没有穿过的3个点,我们就创建另一个三角形。
Open3D在create_from_point_cloud_ball_pivoting
中实现了这个方法。该方法接受半径列表作为参数,该参数对应于以点云为轴的各个球的半径。
import open3d as o3d
import numpy as np
import open3d_tutorial as o3dtut
gt_mesh = o3dtut.get_bunny_mesh()
gt_mesh.compute_vertex_normals()
pcd = gt_mesh.sample_points_poisson_disk(3000)
o3d.visualization.draw_geometries([pcd])
radii = [0.005, 0.01, 0.02, 0.04]
rec_mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_ball_pivoting(
pcd, o3d.utility.DoubleVector(radii))
o3d.visualization.draw_geometries([pcd, rec_mesh])
3. Poisson 表面重建
Poisson曲面重建方法解决了一个正则化优化问题以获得光滑曲面。因此,Poisson曲面重建比上述方法更可取,因为它们产生非光滑结果,因为点云的点也是生成的三角形网格的顶点,而无需任何修改。
Open3D实现了create_from_point_cloud_poisson
的方法,基本上是Kazhdan代码的包装。函数的一个重要参数是depth,它定义了用于曲面重建的八叉树的深度,因此意味着生成的三角形网格的分辨率。“深度”(depth)值越高,表示网格的细节越多。
import open3d as o3d
import numpy as np
import open3d_tutorial as o3dtut
pcd = o3dtut.get_eagle_pcd()
print(pcd)
o3d.visualization.draw_geometries([pcd])
print('run Poisson surface reconstruction')
with o3d.utility.VerbosityContextManager(
o3d.utility.VerbosityLevel.Debug) as cm:
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
pcd, depth=9)
print(mesh)
o3d.visualization.draw_geometries([mesh],
zoom=0.664,
front=[-0.4761, -0.4698, -0.7434],
lookat=[1.8900, 3.2596, 0.9284],
up=[0.2304, -0.8825, 0.4101])
泊松曲面重建还将在低密度区域创建三角形,甚至可以外推到某些区域(请参见上面eagle输出的底部)。create_from_point_cloud_poisson
函数具有第二个密度返回值,该值指示每个顶点的密度。“低密度”(low density)值表示顶点仅由输入点云的少量点支持。
在下面的代码中,我们使用伪彩色在3D中显示密度。紫色表示低密度,黄色表示高密度。
print('visualize densities')
densities = np.asarray(densities)
density_colors = plt.get_cmap('plasma')(
(densities - densities.min()) / (densities.max() - densities.min()))
density_colors = density_colors[:, :3]
density_mesh = o3d.geometry.TriangleMesh()
density_mesh.vertices = mesh.vertices
density_mesh.triangles = mesh.triangles
density_mesh.triangle_normals = mesh.triangle_normals
density_mesh.vertex_colors = o3d.utility.Vector3dVector(density_colors)
o3d.visualization.draw_geometries([density_mesh],
zoom=0.664,
front=[-0.4761, -0.4698, -0.7434],
lookat=[1.8900, 3.2596, 0.9284],
up=[0.2304, -0.8825, 0.4101])
我们可以进一步使用“密度”(density)值删除支持度较低的顶点和三角形。在下面的代码中,我们删除了密度值低于所有密度值的0.01分位数的所有顶点(和连接的三角形)。
print('remove low density vertices')
vertices_to_remove = densities < np.quantile(densities, 0.01)
mesh.remove_vertices_by_mask(vertices_to_remove)
print(mesh)
o3d.visualization.draw_geometries([mesh],
zoom=0.664,
front=[-0.4761, -0.4698, -0.7434],
lookat=[1.8900, 3.2596, 0.9284],
up=[0.2304, -0.8825, 0.4101])
4. 正态估计
在上面的示例中,我们假设点云具有指向外部的法线。但是,并非所有的点云都已经具有关联的法线。Open3D可以用来估计点云法线,使用“估计法线”(estimate\u normals)对每个3D点局部拟合一个平面以导出法线。但是,估计的法线方向可能不一致。使用orient_normals_consistent_tangent_plane
可以生成最小树传播法线方向。
import open3d as o3d
import numpy as np
import open3d_tutorial as o3dtut
gt_mesh = o3dtut.get_bunny_mesh()
pcd = gt_mesh.sample_points_poisson_disk(5000)
pcd.normals = o3d.utility.Vector3dVector(np.zeros(
(1, 3)))
pcd.estimate_normals()
o3d.visualization.draw_geometries([pcd], point_show_normal=True)
pcd.orient_normals_consistent_tangent_plane(100)
o3d.visualization.draw_geometries([pcd], point_show_normal=True)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)