Mesh

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

import open3d as o3d
import numpy as npprint("Testing mesh in Open3D...")
armadillo_path = r'..\PointCloud\ArmadilloMesh.ply'
mesh = o3d.io.read_triangle_mesh(armadillo_path)knot_mesh_path = r'KnotMesh.ply'
mesh = o3d.io.read_triangle_mesh(knot_mesh_path)
print(mesh)
print('Vertices:')
print(np.asarray(mesh.vertices))
print('Triangles:')
print(np.asarray(mesh.triangles))

TriangleMesh 有一些字段如顶点(vertices)和三角(triangles)。可以用过numpy直接访问这些字段。

Visualize a 3D mesh 可视化

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])
print("A mesh with no normals and no colors does not look good.")

当前的’KnotMesh.ply’看起来并不3D,因为当前mesh没有法线。

Surface normal estimation 表面法线估计

我们计算法线后再绘制网格

print("Computing normal and rendering it.")
mesh.compute_vertex_normals()
print(np.asarray(mesh.triangle_normals))
o3d.visualization.draw_geometries([mesh])

使用的是mesh的成员函数 compute_vertex_normals、 paint_uniform_color。

Crop mesh 裁剪网格

我们借助numpy,操作triangle和triangle_normals数据可以直接移除一半的表面。

print("We make a partial mesh of only the first half triangles.")
mesh1 = copy.deepcopy(mesh)#import copy
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])

使用 numpy.asarray() 访问数据。

Paint mesh 网格涂色

paint_uniform_color 用均匀的颜色绘制网格. 颜色在RGB空间内,[0,1]范围内。

print("Painting the mesh")
mesh1.paint_uniform_color([1, 0.706, 0])
o3d.visualization.draw_geometries([mesh1])

Mesh properties 网格属性

三角形网格具有多个属性,可以使用 Open3D 进行测试。一个重要的属性是流形(manifold)属性,我们可以测试三角形网格是否边流形is_edge_manifold,和顶点流形is_vertex_manifold。如果每条边都与一个或两个三角形接壤,三角形网格是边流形,。函数is_edge_manifold具有bool类型参数allow_boundary_edges,定义是否允许边界边。此外,如果顶点的星形是边流形和边连接的,则三角形网格是顶点流形,例如,两个或多个面仅由顶点连接,而不是由边连接。

另一个属性是自交的测试。如果网格中存在与另一个网格相交的三角形,则函数is_self_intersecting返回True。水密网格可以定义为边流形、顶点流形而不自相交的网格。函数is_watertight实现检查水密网格。

我们还可以测试三角形网格,如果它是可定向的(orientable),即三角形可以以所有法线都指向外部的方式定向。Open3D 中的相应函数称为 is_orientable。

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

import open3d as o3d
import numpy as  np
#https://github.com/isl-org/Open3D/blob/master/examples/python/open3d_example.py
import  open3d_example as o3dtut
def check_properties(name, mesh):mesh.compute_vertex_normals()edge_manifold = mesh.is_edge_manifold(allow_boundary_edges=True)edge_manifold_boundary = mesh.is_edge_manifold(allow_boundary_edges=False)vertex_manifold = mesh.is_vertex_manifold()self_intersecting = mesh.is_self_intersecting()watertight = mesh.is_watertight()orientable = mesh.is_orientable()print(name)print(f"  edge_manifold:          {edge_manifold}")print(f"  edge_manifold_boundary: {edge_manifold_boundary}")print(f"  vertex_manifold:        {vertex_manifold}")print(f"  self_intersecting:      {self_intersecting}")print(f"  watertight:             {watertight}")print(f"  orientable:             {orientable}")geoms = [mesh]if not edge_manifold:edges = mesh.get_non_manifold_edges(allow_boundary_edges=True)geoms.append(o3dtut.edges_to_lineset(mesh, edges, (1, 0, 0)))if not edge_manifold_boundary:edges = mesh.get_non_manifold_edges(allow_boundary_edges=False)geoms.append(o3dtut.edges_to_lineset(mesh, edges, (0, 1, 0)))if not vertex_manifold:verts = np.asarray(mesh.get_non_manifold_vertices())pcl = o3d.geometry.PointCloud(points=o3d.utility.Vector3dVector(np.asarray(mesh.vertices)[verts]))pcl.paint_uniform_color((0, 0, 1))geoms.append(pcl)if self_intersecting:intersecting_triangles = np.asarray(mesh.get_self_intersecting_triangles())intersecting_triangles = intersecting_triangles[0:1]intersecting_triangles = np.unique(intersecting_triangles)print("  # visualize self-intersecting triangles")triangles = np.asarray(mesh.triangles)[intersecting_triangles]edges = [np.vstack((triangles[:, i], triangles[:, j]))for i, j in [(0, 1), (1, 2), (2, 0)]]edges = np.hstack(edges).Tedges = o3d.utility.Vector2iVector(edges)geoms.append(o3dtut.edges_to_lineset(mesh, edges, (1, 0, 1)))o3d.visualization.draw_geometries(geoms, mesh_show_back_face=True)if __name__ == '__main__':knot_mesh_path = r'KnotMesh.ply'knot_mesh = o3d.io.read_triangle_mesh(knot_mesh_path)check_properties('KnotMesh', knot_mesh)check_properties('Mobius', o3d.geometry.TriangleMesh.create_mobius(twists=1))check_properties("non-manifold edge", o3dtut.get_non_manifold_edge_mesh())check_properties("non-manifold vertex", o3dtut.get_non_manifold_vertex_mesh())check_properties("open box", o3dtut.get_open_box_mesh())check_properties("intersecting_boxes", o3dtut.get_intersecting_boxes_mesh())

Mesh filtering 网格过滤

Open3D 包含许多过滤网格的方法。在下文中,我们将展示用于平滑噪声三角形网格的滤波器。

Average filter 平均过滤

最简单的过滤器是平均过滤器。一个顶点可以通过和领接点平均进行平均过滤。
此过滤器可以用来去噪。
函数filter_smooth_simple的参数number_of_iterations指定平滑过滤的次数。

print('create noisy mesh')
knot_mesh = o3d.data.KnotMesh()
mesh_in = o3d.io.read_triangle_mesh(knot_mesh.path)
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 拉普拉斯

另一个重要的网格滤波器是拉普拉斯滤波器,定义为

vi=vi⋅λ∑n∈Nwnvn−vivi=vi⋅λ\sum_{n∈N}w_nv_n−v_ivi=vi⋅λn∈N∑​wn​vn​−vi​

其中λ是滤波器的强度,并且wnw_nwn​是与相邻顶点的距离相关的归一化权重。筛选器在filter_smooth_laplacian中实现,并具有参数number_of_iterations和lambda.

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中实现。

print('filter with Taubin with 10 iterations')
mesh_out = mesh_in.filter_smooth_taubin(number_of_iterations=10)
mesh_out.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh_out])print('filter with Taubin with 100 iterations')
mesh_out = mesh_in.filter_smooth_taubin(number_of_iterations=100)
mesh_out.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh_out])

Sampling 采样

Open3D 包含从三角形网格对点云进行采样的函数。最简单的方法sample_points_uniformly根据三角形面积均匀地从 3D 曲面对点进行采样。参数number_of_points定义从三角形曲面采样的点数。

mesh = o3d.geometry.TriangleMesh.create_sphere()
mesh.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh])
pcd = mesh.sample_points_uniformly(number_of_points=500)
o3d.visualization.draw_geometries([pcd])
bunny = o3d.data.BunnyMesh()
mesh = o3d.io.read_triangle_mesh(bunny.path)
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方法实现样本消除。它从采样的点云开始,并删除点以满足采样标准。该方法支持两个选项来提供初始点云:
1.默认通过参数init_factor:该方法首先用init_factor X number_of_points均匀地从网格中抽取点云,并使用它进行消除。

2.可以提供点云并将其传递给方法sample_points_poisson_disk。然后,此点云用于消除。

mesh = o3d.geometry.TriangleMesh.create_sphere()
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])
bunny = o3d.data.BunnyMesh()
mesh = o3d.io.read_triangle_mesh(bunny.path)
mesh.compute_vertex_normals()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中实现。3D 表面和面积保持不变,但顶点和三角形的数量会增加。参数number_of_iterations定义此过程应重复多少次。

import open3d as o3d
mesh = o3d.geometry.TriangleMesh.create_box()
mesh.compute_vertex_normals()
print(f'The mesh has {len(mesh.vertices)} vertices and {len(mesh.triangles)} triangles'
)
o3d.visualization.draw_geometries([mesh], mesh_show_wireframe=True)
mesh = mesh.subdivide_midpoint(number_of_iterations=1)
print(f'After subdivision it has {len(mesh.vertices)} vertices and {len(mesh.triangles)} triangles'
)
o3d.visualization.draw_geometries([mesh], mesh_show_wireframe=True)

Mesh simplification 网格简化

有时我们希望用较少三角形和顶点的表示高分辨率网格,但低分辨率网格仍应接近高分辨率网格。为此,Open3D实现了许多网格简化方法。

Vertex clustering 顶点聚类

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

bunny = o3d.data.BunnyMesh()
mesh = o3d.io.read_triangle_mesh(bunny.path)
mesh.compute_vertex_normals()print(f'Input mesh has {len(mesh_in.vertices)} vertices and {len(mesh_in.triangles)} triangles'
)
o3d.visualization.draw_geometries([mesh_in])voxel_size = max(mesh_in.get_max_bound() - mesh_in.get_min_bound()) / 32
print(f'voxel_size = {voxel_size:e}')
mesh_smp = mesh_in.simplify_vertex_clustering(voxel_size=voxel_size,contraction=o3d.geometry.SimplificationContraction.Average)
print(f'Simplified mesh has {len(mesh_smp.vertices)} vertices and {len(mesh_smp.triangles)} triangles'
)
o3d.visualization.draw_geometries([mesh_smp])voxel_size = max(mesh_in.get_max_bound() - mesh_in.get_min_bound()) / 16
print(f'voxel_size = {voxel_size:e}')
mesh_smp = mesh_in.simplify_vertex_clustering(voxel_size=voxel_size,contraction=o3d.geometry.SimplificationContraction.Average)
print(f'Simplified mesh has {len(mesh_smp.vertices)} vertices and {len(mesh_smp.triangles)} triangles'
)
o3d.visualization.draw_geometries([mesh_smp])

Mesh decimation 网格抽取

网格简化方法的另一类是网格抽取,它以增量步骤运行。我们选择一个三角形,以最小化误差指标并将其删除。重复此操作,直到达到所需数量的三角形。Open3D 实现了simplify_quadric_decimation最小化误差二次(到相邻平面的距离) 的实现。参数target_number_of_triangles定义抽取算法的停止规则。

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

Connected components 连接的组件

各种重建方法的结果。Open3D 实现了连接组件算法,算法cluster_connected_triangles将每个三角形分配给一组连接的三角形。它为每个三角形返回 triangle_clusters中的簇的索引,并为每个簇返回中的三角形数cluster_n_triangles 和簇的表面积cluster_area。

这在例如RGBD集成中很有用,RGBD集成并不总是单个三角形网格,而是许多网格。一些较小的部件是由于噪音引起的,我们很可能想要移除它们。

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

print("Generate data")
bunny = o3d.data.BunnyMesh()
mesh = o3d.io.read_triangle_mesh(bunny.path)
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])##
print("Cluster connected triangles")
with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug) as cm:triangle_clusters, cluster_n_triangles, cluster_area = (mesh.cluster_connected_triangles())
triangle_clusters = np.asarray(triangle_clusters)
cluster_n_triangles = np.asarray(cluster_n_triangles)
cluster_area = np.asarray(cluster_area)
###
print("Show mesh with small clusters removed")
mesh_0 = copy.deepcopy(mesh)
triangles_to_remove = cluster_n_triangles[triangle_clusters] < 100
mesh_0.remove_triangles_by_mask(triangles_to_remove)
o3d.visualization.draw_geometries([mesh_0])

Open3D Mesh 网格相关推荐

  1. Unity Mesh网格合并

    Mesh网格合并通常是优化中常用的小手段,目的是为了减少drawcall,大量的drawcall会造成CPU的性能瓶颈.例如下图中船只里的钢材货物,由诸多钢材模型堆砌而成. 我们将其放在一个空场景里查 ...

  2. 【Unity】Mesh网格编程(三)万能网格几何形体

    用一个通用代码,实现各种锥.柱.管状体的网格创建. 非原创的同系列.虽然总觉得代码上可以再优化,但是实现的功能已经超级屌了. 真是没有不能做的,只有想不到的. 原文: Mesh网格编程(二) 万能网格 ...

  3. Mesh网格编程(一) 流体水

    通过Mesh网格随Sin函数实时变化模拟液体的流动,从而达到动态水的效果. Mesh网格编程步骤: 一:确定数量 确定该几何图形应有多少个三角形面,顶点坐标.顶点序列.UV贴图.法线向量皆为三角形面数 ...

  4. 学习笔记:unity通过Mesh网格绘制图形:三角形正方体圆柱

    一,介绍 Mesh类:通过脚本创建或是获取网格的类,网格包含多个顶点和三角形数组.顶点信息包含坐标和所在面的法线. unity中3D的世界的所有图形全部都是由三角形构成的. 比如unity已经装配好的 ...

  5. 直线或线段与mesh网格相交的计算

    引言 在采样二指夹爪与mesh网格的抓取点时使用的点接触模型,抽象二指夹爪为一个线段,那么寻找夹爪与物体的接触点就抽象为直线与mesh网格的交点问题,而在mesh中物体表面是以空间三角形保存的,在进一 ...

  6. unity中Mesh网格编程

    上图是效果 一.关于mesh的意义 有了mesh网格,物体才能被渲染出来. (1)mesh中包含顶点, mesh.vertices (2)顶点对应的uv(一张图的uv左下角00,右上角11) mesh ...

  7. Open3d之网格(Mesh)操作

    网格 open3d有一种被称为TriangleMesh的3D三角网格的数据结构.下面的代码展示了如何从一个ply文件读取三角网格数据并打印它的顶点和三角形. # -*-coding:utf-8 -*- ...

  8. Open3d之网格变形

    如果我们想使用少量的约束使得三角网格变形,得使用相应的网格变形算法.Open3d实现了[SorkineAndAlexa2007] 中的尽可能严格的算法,该方法优化了以下能量函数: 这里的表示 我们要优 ...

  9. Open3d(三)——网格数据操作

    亲测代码程序可运行使用,open3d版本0.13.0. open3d数据资源下载:GitHub - Cobotic/Open3D: Open3D: A Modern Library for 3D Da ...

最新文章

  1. c++ 从文本中逐行读取,并按空格对读取的一行进行分割
  2. Android SDK上手指南:虚拟与物理设备
  3. ROBOMASTER 2018机甲大师赛 南部赛区三等奖!
  4. Html.RenderPartial与Html.RenderAction
  5. 算法笔记_029:约瑟夫斯问题(Java)
  6. Source Insight常用的快捷键
  7. android触摸事件触摸点坐标,Android开发——触摸事件TouchEvent详解及其应用
  8. DataBindings的用法
  9. ISO50001认证辅导,ISO50001能源管理体系的框架审核通过系统的提高能源效率和消耗
  10. canpro脚本_周立功CANPro软件下载
  11. 阿里云CentOS环境之docker安装,启动,加速器,docker-compose(十四)
  12. iPhone 11 Pro 的拍照好在哪?这是专业摄影师给出的答案
  13. html跑马灯编程,求一个HTML无缝的跑马灯代码。
  14. 硬盘安装FC6 linux
  15. 【ERP】实践3_会计科目_凭证_期初余额
  16. 日本旅馆业、民宿分类及管理规定
  17. 基于三菱运动控制系统生成丝滑无比的凸轮曲线(上)
  18. 基于二维矩阵的FFT计算原理
  19. 【计算机二级Python】模拟试卷第6套选择题
  20. 联想电脑装什么系统最好?系统下载

热门文章

  1. SQL删除数据库表中重复的数据
  2. linux临时关闭防火墙,和永久关闭防火墙
  3. UVM的configuration机制
  4. 码率控制、VBR、CBR
  5. 同一网段的概念以及计算
  6. 当程序员5年,他从来不加班,工资居然还每年都在涨...
  7. 微信小程序-实现上下、左右布局
  8. 风控建模十二:数据淘金——如何从APP数据中挖掘出有效变量
  9. 分光器尺寸及光损耗计算
  10. 扫雷小游戏(简易版)