Geometry-(Mesh)

打印triangle的顶点和三角形

Open3D有一个3D三角网格(triangle meshes)的数据结构,叫做TriangleMesh。下面的代码显示了如何从ply文件中读取一个TriangleMesh,并打印其顶点和三角形。

TriangleMesh类有一些数据字段,如顶点和三角形。Open3D通过numpy提供对这些字段的直接内存访问。

import open3d as o3d
import numpy as np
import matplotlib as plt

print("Convert mesh to a point cloud and estimate dimensions")
mesh = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")

print(mesh)
print('Vertices:')
print(np.asarray(mesh.vertices))
print('Triangles:')
print(np.asarray(mesh.triangles))

在这里插入图片描述

可视化3D mesh (Visualize a 3D mesh)

尝试渲染一个带有法线(exist: False)和颜色(exist: False)的网格。 [Open3D警告] [ViewControl] SetViewPoint()失败,因为没有设置窗口的高度和宽度。

import open3d as o3d
import numpy as np
import matplotlib as plt

print("Convert mesh to a point cloud and estimate dimensions")
mesh = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")

print(mesh)
print('Vertices:')
print(np.asarray(mesh.vertices))
print('Triangles:')
print(np.asarray(mesh.triangles))

print("Try to render a mesh with normals (exist: " +
      str(mesh.has_vertex_normals()) + ") and colors (exist: " +
      str(mesh.has_vertex_colors()) + ")")
o3d.visualization.draw_geometries([mesh])

# Try to render a mesh with normals (exist: False) and colors (exist: False)
# [Open3D WARNING] [ViewControl] SetViewPoint() failed because window height and width # are not set.

你可以旋转和移动网格,但它被涂成统一的灰色,看起来不像是3D。原因是当前的网格没有顶点或面的法线。所以使用了统一颜色的阴影,而不是更复杂的Phong阴影。

表面法线估计(Surface normal estimation)

使用了compute_vertex_normals和paint_uniform_color,它们是Mesh的成员函数。

import open3d as o3d
import numpy as np
import matplotlib as plt

print("Convert mesh to a point cloud and estimate dimensions")
mesh = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")

print(mesh)
print('Vertices:')
print(np.asarray(mesh.vertices))
print('Triangles:')
print(np.asarray(mesh.triangles))

print("Try to render a mesh with normals (exist: " +
      str(mesh.has_vertex_normals()) + ") and colors (exist: " +
      str(mesh.has_vertex_colors()) + ")")

# 表面法线估计
print("Computing normal and rendering it.")
mesh.compute_vertex_normals()
print(np.asarray(mesh.triangle_normals))
o3d.visualization.draw_geometries([mesh])

在这里插入图片描述

裁剪网格(Crop mesh)

我们通过直接操作网格中的三角形和三角形法向量数据域来移除一半的表面。这是通过numpy实现的.

import open3d as o3d
import numpy as np
import matplotlib as plt
import copy

print("Convert mesh to a point cloud and estimate dimensions")
mesh = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")

print(mesh)
print('Vertices:')
print(np.asarray(mesh.vertices))
print('Triangles:')
print(np.asarray(mesh.triangles))

print("Try to render a mesh with normals (exist: " +
      str(mesh.has_vertex_normals()) + ") and colors (exist: " +
      str(mesh.has_vertex_colors()) + ")")

# 表面法线估计
print("Computing normal and rendering it.")
mesh.compute_vertex_normals()

# Crop mesh
mesh1 = copy.deepcopy(mesh)
mesh1.triangles = o3d.utility.Vector3iVector(np.asarray(mesh1.triangles)[:len(mesh1.triangles) // 2, :])
mesh1.triangle_normals = o3d.utility.Vector3dVector(np.asarray(mesh1.triangle_normals)[:len(mesh1.triangle_normals) // 2, :])
print(mesh1.triangles)
o3d.visualization.draw_geometries([mesh1])

绘制mesh(Paint mesh)

paint_uniform_color为网格画上一个统一的颜色。颜色是在RGB空间,[0, 1]范围。

import open3d as o3d
import numpy as np
import matplotlib as plt
import copy

print("Convert mesh to a point cloud and estimate dimensions")
mesh = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")

print(mesh)
print('Vertices:')
print(np.asarray(mesh.vertices))
print('Triangles:')
print(np.asarray(mesh.triangles))

print("Try to render a mesh with normals (exist: " +
      str(mesh.has_vertex_normals()) + ") and colors (exist: " +
      str(mesh.has_vertex_colors()) + ")")

##############################
# 表面法线估计
print("Computing normal and rendering it.")
mesh.compute_vertex_normals()

##############################
# 绘制颜色
print("Painting the mesh")
mesh.paint_uniform_color([1, 0.706, 0])
#####################################
o3d.visualization.draw_geometries([mesh])
o3d.visualization.draw_geometries([mesh])

在这里插入图片描述

网格属性(Mesh properties)(没有数据)

一个三角形网格有几个属性可以用Open3D来测试。其中一个重要的属性是流形属性,我们可以测试三角形网格是否是边缘流形is_edge_manifold和是否是顶点流形is_vertex_manifold。一个三角形网格是边缘流形的,如果每条边缘都与一个或两个三角形相连接。函数is_edge_manifold有一个bool参数allow_boundary_edges,定义了是否允许边界边的存在。此外,如果一个三角形网格是边-流形的,并且是边连接的,例如,两个或多个面只由一个顶点连接,而不是由一条边连接,那么这个三角形网格就是顶点流形。

另一个属性是自相交的测试。函数is_self_intersecting在网格中存在一个三角形与另一个网格相交时返回True。一个不漏水的网格可以被定义为一个边缘流形、顶点流形且不自交的网格。函数is_watertight在Open3D中实现了这个检查。

我们也可以测试三角形网格是否可定向,也就是说,三角形可以以这样的方式定向,即所有法线都指向外部。Open3D中的相应函数叫做is_orientable。

下面的代码针对这些属性测试了一些三角形网格,并将结果可视化。非网格边缘显示为红色,边界边缘显示为绿色,非网格顶点可视化为绿色点,自相交三角形显示为粉红色。

http://www.open3d.org/docs/release/tutorial/geometry/mesh.html

Mesh 过滤

Open3D包含一些过滤网格的方法。在下文中,我们展示了实现的过滤器,以平滑嘈杂的三角形网格。

Average filter

最简单的过滤器是平均过滤器。一个给定的顶点 v i v_{i} vi是由相邻顶点的平均值 N \mathcal{N} N

v i = v i + ∑ n ∈ N v n ∣ N ∣ + 1   . v_i = \frac{v_i + \sum_{n \in \mathcal{N}} v_n}{|N| + 1} \,. vi=N+1vi+nNvn.

这个滤波器可以用来对网格进行去噪,如下代码所示。函数filter_smooth_simple中的参数number_of_iterations定义了滤波器应用于网格的频率。

import open3d as o3d
import numpy as np
import matplotlib as plt
import copy

print('create noisy mesh')
mesh_in = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")
vertices = np.asarray(mesh_in.vertices)
noise = 5
vertices += np.random.uniform(0, noise, size=vertices.shape)
mesh_in.vertices = o3d.utility.Vector3dVector(vertices)
mesh_in.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh_in])

print('filter with average with 1 iteration')
mesh_out = mesh_in.filter_smooth_simple(number_of_iterations=1)
mesh_out.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh_out])

print('filter with average with 5 iterations')
mesh_out = mesh_in.filter_smooth_simple(number_of_iterations=5)
mesh_out.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh_out])

Laplacian

另一个重要的网格过滤器是拉普拉斯,定义为:
v i = v i ⋅ λ ∑ n ∈ N w n v n − v i   , v_i = v_i \cdot \lambda \sum_{n \in N} w_n v_n - v_i \,, vi=viλnNwnvnvi,
其中λ是过滤器的强度,wn是与相邻顶点的距离有关的归一化权重。滤波器在filter_smooth_laplacian中实现,有参数number_of_iterations和lambda。

import open3d as o3d
import numpy as np
import matplotlib as plt
import copy

print('create noisy mesh')
mesh_in = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")
vertices = np.asarray(mesh_in.vertices)
noise = 5
vertices += np.random.uniform(0, noise, size=vertices.shape)
mesh_in.vertices = o3d.utility.Vector3dVector(vertices)
mesh_in.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh_in])

print('filter with Laplacian with 10 iterations')
mesh_out = mesh_in.filter_smooth_laplacian(number_of_iterations=10)
mesh_out.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh_out])

print('filter with Laplacian with 50 iterations')
mesh_out = mesh_in.filter_smooth_laplacian(number_of_iterations=50)
mesh_out.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh_out])

Taubin filter

平均滤波和拉普拉斯滤波的问题是,它们会导致三角形网格的缩减。[Taubin1995]表明,应用两个不同λ参数的拉普拉斯滤波器可以防止网格收缩。该滤波器在 filter_smooth_taubin 中实现。

用Taubin进行10次迭代的过滤器
在这里插入图片描述

用Taubin进行100次迭代的过滤器
在这里插入图片描述

Sampling

Open3D 包括从三角形网格中采样点云的函数。最简单的方法是 sample_points_uniformly,它根据三角形区域从 3D 表面均匀采样点。参数 number_of_points 定义了从三角形表面采样的点数。

import open3d as o3d
import numpy as np
import matplotlib as plt
import copy

print('create noisy mesh')
mesh = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")
mesh.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh])
pcd = mesh.sample_points_uniformly(number_of_points=500)
o3d.visualization.draw_geometries([pcd])

在这里插入图片描述
均匀抽样可以在曲面上产生点群,而一种叫做泊松盘抽样的方法可以使曲面上的点均匀分布。sample_points_poisson_disk方法实现了抽样消除。它从抽样的点云开始,删除满足抽样标准的点。该方法支持两个选项来提供初始点云。

默认通过参数init_factor。该方法首先从网格中均匀地取样一个点云,init_factor x number_of_points,并使用这个点云进行消除。

人们可以提供一个点云并将其传递给sample_points_poisson_disk方法。然后,这个点云被用于消除。

import open3d as o3d
import numpy as np
import matplotlib as plt
import copy

print('create noisy mesh')
mesh = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")
pcd = mesh.sample_points_poisson_disk(number_of_points=500, init_factor=5)
o3d.visualization.draw_geometries([pcd])

pcd = mesh.sample_points_uniformly(number_of_points=2500)
pcd = mesh.sample_points_poisson_disk(number_of_points=500, pcl=pcd)
o3d.visualization.draw_geometries([pcd])

在这里插入图片描述
在这里插入图片描述

网格细分(Mesh subdivision)

在网格细分中,我们将每个三角形分成若干个更小的三角形。在最简单的情况下,我们计算每个三角形每条边的中点,然后将三角形分成四个小三角形。这在subdivide_midpoint函数中实现。三维表面和面积保持不变,但顶点和三角形的数量增加。参数number_of_iterations定义了这个过程应该重复多少次。


网格简化(Mesh simplification)

有时我们想用更少的三角形和顶点来表示高分辨率的网格,但低分辨率的网格仍应接近高分辨率的网格。为此,Open3D实现了一系列的网格简化方法。

Vertex clustering(顶点聚类)

顶点聚类方法将所有落入给定大小的体素的顶点集中到一个顶点上。该方法在simplify_vertex_clustering中实现,它的参数是voxel_size(定义体素网格的大小)和contraction(定义顶点集合的方式)。 o3d.geometry.SimplificationContraction.Average计算一个简单的平均值。

{
	"class_name" : "ViewTrajectory",
	"interval" : 29,
	"is_loop" : false,
	"trajectory" : 
	[
		{
			"boundingbox_max" : [ 60.728670932124928, 95.006359059323543, 56.887657165527344 ],
			"boundingbox_min" : [ -61.726297833430721, -52.899296813847243, -55.126294031630465 ],
			"field_of_view" : 60.0,
			"front" : [ 0.0, 0.0, 1.0 ],
			"lookat" : [ -0.49881345065289651, 21.05353112273815, 0.88068156694843935 ],
			"up" : [ 0.0, 1.0, 0.0 ],
			"zoom" : 0.69999999999999996
		}
	],
	"version_major" : 1,
	"version_minor" : 0
}

输入网格有 35947 个顶点和 69451 个三角形
在这里插入图片描述
voxel_size = 4.865594e-03
Simplified mesh has 3222 vertices and 6454 triangles
在这里插入图片描述
voxel_size = 9.731187e-03
Simplified mesh has 845 vertices and 1724 triangles
在这里插入图片描述

Mesh decimation(网格抽取)

另一类网格简化方法是以增量步骤操作的网格抽取。我们选择一个三角形来最小化错误度量并将其删除。重复此过程,直到获得所需数量的三角形。Open3D 实现了将误差二次曲线(到相邻平面的距离)最小化的 simple_quadric_decimation。参数 target_number_of_triangles 定义了抽取算法的停止标准。

import open3d as o3d
import numpy as np
import matplotlib as plt
import copy

mesh_in = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")

mesh_in.compute_vertex_normals()

mesh_smp = mesh_in.simplify_quadric_decimation(target_number_of_triangles=6500)
print(
    f'Simplified mesh has {len(mesh_smp.vertices)} vertices and {len(mesh_smp.triangles)} triangles'
)
o3d.visualization.draw_geometries([mesh_smp])

mesh_smp = mesh_in.simplify_quadric_decimation(target_number_of_triangles=1700)
print(
    f'Simplified mesh has {len(mesh_smp.vertices)} vertices and {len(mesh_smp.triangles)} triangles'
)
o3d.visualization.draw_geometries([mesh_smp])

Simplified mesh has 4405 vertices and 6499 triangles
在这里插入图片描述

Simplified mesh has 1978 vertices and 1700 triangles
在这里插入图片描述

Connected components (连接组件)

各种重建方法的结果。Open3D实现了一个连接部件算法cluster_connected_triangles,该算法将每个三角形分配到一个连接三角形簇中。它为每个三角形返回triangle_clusters中的集群索引,为每个集群返回cluster_n_triangles中的三角形数量以及cluster_area中的集群表面积。

这在例如RGBD集成中很有用,它并不总是一个单一的三角形网格,而是若干个网格。一些较小的部分是由于噪声造成的,我们很可能想把它们删除。

下面的代码显示了cluster_connected_triangles的应用,以及如何用它来移除假三角形。

import open3d as o3d
import numpy as np
import matplotlib as plt
import copy

mesh = o3d.io.read_triangle_mesh("./data/ArmadilloMesh.ply")

mesh.compute_vertex_normals()

mesh = mesh.subdivide_midpoint(number_of_iterations=2)
vert = np.asarray(mesh.vertices)
min_vert, max_vert = vert.min(axis=0), vert.max(axis=0)
for _ in range(30):
    cube = o3d.geometry.TriangleMesh.create_box()
    cube.scale(0.005, center=cube.get_center())
    cube.translate(
        (
            np.random.uniform(min_vert[0], max_vert[0]),
            np.random.uniform(min_vert[1], max_vert[1]),
            np.random.uniform(min_vert[2], max_vert[2]),
        ),
        relative=False,
    )
    mesh += cube
mesh.compute_vertex_normals()
print("Show input mesh")
o3d.visualization.draw_geometries([mesh])

在这里插入图片描述

http://www.open3d.org/docs/latest/tutorial/geometry/mesh.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值