Mesh¶

Open3D has a data construction for 3D triangle meshes called TriangleMesh . The code beneath shows how to read a triangle mesh from a ply file and print its vertices and triangles.

                                print                (                "Testing mesh in Open3D..."                )                armadillo_mesh                =                o3d                .                data                .                ArmadilloMesh                ()                mesh                =                o3d                .                io                .                read_triangle_mesh                (                armadillo_mesh                .                path                )                knot_mesh                =                o3d                .                data                .                KnotMesh                ()                mesh                =                o3d                .                io                .                read_triangle_mesh                (                knot_mesh                .                path                )                print                (                mesh                )                impress                (                'Vertices:'                )                print                (                np                .                asarray                (                mesh                .                vertices                ))                print                (                'Triangles:'                )                print                (                np                .                asarray                (                mesh                .                triangles                ))              
Testing mesh in Open3D... [Open3D INFO] Downloading https://github.com/isl-org/open3d_downloads/releases/download/20220201-data/KnotMesh.ply [Open3D INFO] Downloaded to /home/runner/open3d_data/download/KnotMesh/KnotMesh.ply TriangleMesh with 1440 points and 2880 triangles. Vertices: [[  4.51268387  28.68865967 -76.55680847]  [  vii.63622284  35.52046967 -69.78063965]  [  vi.21986008  44.22465134 -64.82303619]  ...  [-22.12651634  31.28466606 -87.37570953]  [-thirteen.91188431  25.4865818  -86.25827026]  [ -v.27768707  23.36245346 -81.43279266]] Triangles: [[   0   12   xiii]  [   0   13    i]  [   1   13   14]  ...  [1438   11 1439]  [1439   11    0]  [1439    0 1428]]              

The TriangleMesh course has a few data fields such as vertices and triangles . Open3D provides direct retentivity access to these fields via numpy.

Visualize a 3D mesh¶

                                    impress                  (                  "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                  ])                  impress                  (                  "A mesh with no normals and no colors does not look good."                  )                
Try to render a mesh with normals (exist: Fake) and colors (exist: False)                

../../_images/tutorial_geometry_mesh_5_1.png

A mesh with no normals and no colors does not look adept.                

You lot can rotate and move the mesh just it is painted with uniform gray color and does not look 3d . The reason is that the current mesh does non have normals for vertices or faces. So uniform color shading is used instead of a more sophisticated Phong shading.

Surface normal estimation¶

Let's depict the mesh with surface normals.

                                    print                  (                  "Computing normal and rendering information technology."                  )                  mesh                  .                  compute_vertex_normals                  ()                  print                  (                  np                  .                  asarray                  (                  mesh                  .                  triangle_normals                  ))                  o3d                  .                  visualization                  .                  draw_geometries                  ([                  mesh                  ])                
Calculating normal and rendering information technology. [[ 0.79164373 -0.53951444  0.28674793]  [ 0.8319824  -0.53303008  0.15389681]  [ 0.83488162 -0.09250101  0.54260136]  ...  [ 0.16269924 -0.76215917 -0.6266118 ]  [ 0.52755226 -0.83707495 -0.14489352]  [ 0.56778973 -0.76467734 -0.30476777]]                

../../_images/tutorial_geometry_mesh_8_1.png

Information technology uses compute_vertex_normals and paint_uniform_color which are member functions of mesh .

Ingather mesh¶

Nosotros remove half of the surface by direct operating on the triangle and triangle_normals data fields of the mesh. This is done via numpy.

                                    impress                  (                  "We brand a partial mesh of merely the first half triangles."                  )                  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                  ])                
Nosotros make a fractional mesh of simply the first half triangles. std::vector<Eigen::Vector3i> with 1440 elements. Utilize numpy.asarray() to access information.                

../../_images/tutorial_geometry_mesh_11_1.png

Paint mesh¶

paint_uniform_color paints the mesh with a uniform color. The color is in RGB infinite, [0, ane] range.

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

../../_images/tutorial_geometry_mesh_13_1.png

Mesh properties¶

A triangle mesh has several properties that can be tested with Open3D. One of import belongings is the manifold property, where we tin exam the triangle mesh if it is edge manifold is_edge_manifold and if information technology is is_vertex_manifold . A triangle mesh is edge manifold, if each edge is bounding either one or 2 triangles. The function is_edge_manifold has the bool parameter allow_boundary_edges that defines if boundary edges should exist allowed. Further, a triangle mesh is vertex manifold if the star of the vertex is edge-manifold and border-continued, eastward.1000., two or more faces continued simply by a vertex and not by an border.

Another property is the exam of self-intersection. The function is_self_intersecting returns True if in that location exists a triangle in the mesh that is intersecting some other mesh. A watertight mesh can be divers as a mesh that is edge manifold, vertex manifold and non self intersecting. The function is_watertight implements this bank check in Open3D.

We as well can test the triangle mesh, if it is orientable, i.e. the triangles tin be oriented in such a fashion that all normals point towards the outside. The respective role in Open3D is chosen is_orientable .

The lawmaking below tests a number of triangle meshes against those properties and visualizes the results. Non-manifold edges are shown in ruby-red, boundary edges in green, not-manifold vertices are visualized as greenish points, and self-intersecting triangles are shown in pink.

                                    def                  check_properties                  (                  proper 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                  =                  Fake                  )                  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                  }                  "                  )                  impress                  (                  f                  "  edge_manifold_boundary:                                    {                  edge_manifold_boundary                  }                  "                  )                  impress                  (                  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                  non                  edge_manifold_boundary                  :                  edges                  =                  mesh                  .                  get_non_manifold_edges                  (                  allow_boundary_edges                  =                  False                  )                  geoms                  .                  append                  (                  o3dtut                  .                  edges_to_lineset                  (                  mesh                  ,                  edges                  ,                  (                  0                  ,                  ane                  ,                  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                  .                  suspend                  (                  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                  )                  impress                  (                  "  # visualize self-intersecting triangles"                  )                  triangles                  =                  np                  .                  asarray                  (                  mesh                  .                  triangles                  )[                  intersecting_triangles                  ]                  edges                  =                  [                  np                  .                  vstack                  ((                  triangles                  [:,                  i                  ],                  triangles                  [:,                  j                  ]))                  for                  i                  ,                  j                  in                  [(                  0                  ,                  ane                  ),                  (                  1                  ,                  2                  ),                  (                  two                  ,                  0                  )]                  ]                  edges                  =                  np                  .                  hstack                  (                  edges                  )                  .                  T                  edges                  =                  o3d                  .                  utility                  .                  Vector2iVector                  (                  edges                  )                  geoms                  .                  append                  (                  o3dtut                  .                  edges_to_lineset                  (                  mesh                  ,                  edges                  ,                  (                  i                  ,                  0                  ,                  one                  )))                  o3d                  .                  visualization                  .                  draw_geometries                  (                  geoms                  ,                  mesh_show_back_face                  =                  True                  )                
                                    knot_mesh_data                  =                  o3d                  .                  information                  .                  KnotMesh                  ()                  knot_mesh                  =                  o3d                  .                  io                  .                  read_triangle_mesh                  (                  knot_mesh_data                  .                  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                  ())                
KnotMesh   edge_manifold:          True   edge_manifold_boundary: True   vertex_manifold:        True   self_intersecting:      False   watertight:             True   orientable:             True                

../../_images/tutorial_geometry_mesh_16_1.png

Mobius   edge_manifold:          True   edge_manifold_boundary: Fake   vertex_manifold:        True   self_intersecting:      Faux   watertight:             False   orientable:             False                

../../_images/tutorial_geometry_mesh_16_3.png

non-manifold edge   edge_manifold:          False   edge_manifold_boundary: Fake   vertex_manifold:        Truthful   self_intersecting:      False   watertight:             False   orientable:             True                

../../_images/tutorial_geometry_mesh_16_5.png

not-manifold vertex   edge_manifold:          Truthful   edge_manifold_boundary: True   vertex_manifold:        False   self_intersecting:      False   watertight:             False   orientable:             True                

../../_images/tutorial_geometry_mesh_16_7.png

open box   edge_manifold:          True   edge_manifold_boundary: False   vertex_manifold:        True   self_intersecting:      Fake   watertight:             False   orientable:             True                

../../_images/tutorial_geometry_mesh_16_9.png

intersecting_boxes   edge_manifold:          True   edge_manifold_boundary: True   vertex_manifold:        Truthful   self_intersecting:      Truthful   watertight:             False   orientable:             True   # visualize cocky-intersecting triangles                

../../_images/tutorial_geometry_mesh_16_11.png

Mesh filtering¶

Open3D contains a number of methods to filter meshes. In the following we bear witness the implemented filters to smoothen noisy triangle meshes.

Average filter¶

The simplest filter is the average filter. A given vertex \(v_i\) is given by the average of the adjacent vertices \(\mathcal{Northward}\)

\begin{equation} v_i = \frac{v_i + \sum_{n \in \mathcal{N}} v_n}{|Northward| + 1} \,. \cease{equation}

This filter can be used to denoise meshes every bit demonstrated in the code beneath. The parameter number_of_iterations in the function filter_smooth_simple defines the how often the filter is practical to the mesh.

                                        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 boilerplate 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                    =                    v                    )                    mesh_out                    .                    compute_vertex_normals                    ()                    o3d                    .                    visualization                    .                    draw_geometries                    ([                    mesh_out                    ])                  

../../_images/tutorial_geometry_mesh_19_1.png

filter with average with one iteration                  

../../_images/tutorial_geometry_mesh_19_3.png

filter with average with v iterations                  

../../_images/tutorial_geometry_mesh_19_5.png

Laplacian¶

Another of import mesh filter is the Laplacian divers equally

\brainstorm{equation} v_i = v_i \cdot \lambda \sum_{n \in N} w_n v_n - v_i \,, \end{equation}

where \(\lambda\) is the strength of the filter and \(w_n\) are normalized weights that relate to the distance of the neighboring vertices. The filter is implemented in filter_smooth_laplacian and has the parameters number_of_iterations and lambda .

                                        impress                    (                    '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 fifty iterations'                    )                    mesh_out                    =                    mesh_in                    .                    filter_smooth_laplacian                    (                    number_of_iterations                    =                    50                    )                    mesh_out                    .                    compute_vertex_normals                    ()                    o3d                    .                    visualization                    .                    draw_geometries                    ([                    mesh_out                    ])                  
filter with Laplacian with 10 iterations                  

../../_images/tutorial_geometry_mesh_21_1.png

filter with Laplacian with 50 iterations                  

../../_images/tutorial_geometry_mesh_21_3.png

Taubin filter¶

The problem with the boilerplate and Laplacian filter is that they lead to a shrinkage of the triangle mesh. [Taubin1995] showed that the application of two Laplacian filters with dissimilar \(\lambda\) parameters can forbid the mesh shrinkage. The filter is implemented in filter_smooth_taubin .

                                        print                    (                    'filter with Taubin with 10 iterations'                    )                    mesh_out                    =                    mesh_in                    .                    filter_smooth_taubin                    (                    number_of_iterations                    =                    ten                    )                    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                    ])                  
filter with Taubin with 10 iterations                  

../../_images/tutorial_geometry_mesh_23_1.png

filter with Taubin with 100 iterations                  

../../_images/tutorial_geometry_mesh_23_3.png

Sampling¶

Open3D includes functions to sample point clouds from a triangle mesh. The simplest method is sample_points_uniformly that uniformly samples points from the 3D surface based on the triangle surface area. The parameter number_of_points defines how many points are sampled from the triangle surface.

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

../../_images/tutorial_geometry_mesh_25_0.png

../../_images/tutorial_geometry_mesh_25_1.png

                                    bunny                  =                  o3d                  .                  information                  .                  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                  ])                

../../_images/tutorial_geometry_mesh_26_0.png

../../_images/tutorial_geometry_mesh_26_1.png

Uniform sampling tin yield clusters of points on the surface, while a method called Poisson deejay sampling can evenly distribute the points on the surface. The method sample_points_poisson_disk implements sample elimination. It starts with a sampled point cloud and removes points to satisfy the sampling criterion. The method supports ii options to provide the initial point cloud:

  1. Default via the parameter init_factor : The method first samples uniformly a point deject from the mesh with init_factor 10 number_of_points and uses this for the elimination.

  2. One can provide a signal cloud and pass it to the sample_points_poisson_disk method. Then, this betoken cloud is used for emptying.

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

../../_images/tutorial_geometry_mesh_28_0.png

../../_images/tutorial_geometry_mesh_28_1.png

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

../../_images/tutorial_geometry_mesh_29_0.png

../../_images/tutorial_geometry_mesh_29_1.png

Mesh subdivision¶

In mesh subdivision we divide each triangle into a number of smaller triangles. In the simplest instance, we compute the midpoint of each side per triangle and divide the triangle into four smaller triangles. This is implemented in the subdivide_midpoint function. The 3D surface and area stays the same, but the number of vertices and triangles increases. The parameter number_of_iterations defines how many times this process should exist repeated.

                                    mesh                  =                  o3d                  .                  geometry                  .                  TriangleMesh                  .                  create_box                  ()                  mesh                  .                  compute_vertex_normals                  ()                  impress                  (                  f                  'The mesh has                                    {                  len                  (                  mesh                  .                  vertices                  )                  }                                      vertices and                                    {                  len                  (                  mesh                  .                  triangles                  )                  }                                      triangles'                  )                  o3d                  .                  visualization                  .                  draw_geometries                  ([                  mesh                  ],                  zoom                  =                  0.8                  ,                  mesh_show_wireframe                  =                  True                  )                  mesh                  =                  mesh                  .                  subdivide_midpoint                  (                  number_of_iterations                  =                  ane                  )                  print                  (                  f                  'After subdivision it has                                    {                  len                  (                  mesh                  .                  vertices                  )                  }                                      vertices and                                    {                  len                  (                  mesh                  .                  triangles                  )                  }                                      triangles'                  )                  o3d                  .                  visualization                  .                  draw_geometries                  ([                  mesh                  ],                  zoom                  =                  0.eight                  ,                  mesh_show_wireframe                  =                  Truthful                  )                
The mesh has 8 vertices and 12 triangles                

../../_images/tutorial_geometry_mesh_31_1.png

Later subdivision it has 26 vertices and 48 triangles                

../../_images/tutorial_geometry_mesh_31_3.png

Open3D implements an additional subdivision method based on [Loop1987]. The method is based on a quartic box spline, which generates \(C^2\) continuous limit surfaces everywhere except at boggling vertices where they are \(C^one\) continuous. This leads to smoother corners.

                                    mesh                  =                  o3d                  .                  geometry                  .                  TriangleMesh                  .                  create_sphere                  ()                  mesh                  .                  compute_vertex_normals                  ()                  print                  (                  f                  'The mesh has                                    {                  len                  (                  mesh                  .                  vertices                  )                  }                                      vertices and                                    {                  len                  (                  mesh                  .                  triangles                  )                  }                                      triangles'                  )                  o3d                  .                  visualization                  .                  draw_geometries                  ([                  mesh                  ],                  zoom                  =                  0.8                  ,                  mesh_show_wireframe                  =                  True                  )                  mesh                  =                  mesh                  .                  subdivide_loop                  (                  number_of_iterations                  =                  2                  )                  print                  (                  f                  'After subdivision it has                                    {                  len                  (                  mesh                  .                  vertices                  )                  }                                      vertices and                                    {                  len                  (                  mesh                  .                  triangles                  )                  }                                      triangles'                  )                  o3d                  .                  visualization                  .                  draw_geometries                  ([                  mesh                  ],                  zoom                  =                  0.viii                  ,                  mesh_show_wireframe                  =                  Truthful                  )                
The mesh has 762 vertices and 1520 triangles                

../../_images/tutorial_geometry_mesh_33_1.png

Later subdivision it has 12162 vertices and 24320 triangles                

../../_images/tutorial_geometry_mesh_33_3.png

                                    knot_mesh                  =                  o3d                  .                  data                  .                  KnotMesh                  ()                  mesh                  =                  o3d                  .                  io                  .                  read_triangle_mesh                  (                  knot_mesh                  .                  path                  )                  mesh                  .                  compute_vertex_normals                  ()                  print                  (                  f                  'The mesh has                                    {                  len                  (                  mesh                  .                  vertices                  )                  }                                      vertices and                                    {                  len                  (                  mesh                  .                  triangles                  )                  }                                      triangles'                  )                  o3d                  .                  visualization                  .                  draw_geometries                  ([                  mesh                  ],                  zoom                  =                  0.viii                  ,                  mesh_show_wireframe                  =                  Truthful                  )                  mesh                  =                  mesh                  .                  subdivide_loop                  (                  number_of_iterations                  =                  1                  )                  print                  (                  f                  'After subdivision it has                                    {                  len                  (                  mesh                  .                  vertices                  )                  }                                      vertices and                                    {                  len                  (                  mesh                  .                  triangles                  )                  }                                      triangles'                  )                  o3d                  .                  visualization                  .                  draw_geometries                  ([                  mesh                  ],                  zoom                  =                  0.8                  ,                  mesh_show_wireframe                  =                  True                  )                
The mesh has 1440 vertices and 2880 triangles                

../../_images/tutorial_geometry_mesh_34_1.png

After subdivision it has 5760 vertices and 11520 triangles                

../../_images/tutorial_geometry_mesh_34_3.png

Mesh simplification¶

Sometimes we want to correspond a high-resolution mesh with fewer triangles and vertices, only the low-resolution mesh should notwithstanding be close to the high-resolution mesh. For this purpose Open3D implements a number of mesh simplification methods.

Vertex clustering¶

The vertex clustering method pools all vertices that autumn into a voxel of a given size to a single vertex. The method is implemented in simplify_vertex_clustering and has as parameters voxel_size that defines the size of the voxel grid and wrinkle that defines how the vertices are pooled. o3d.geometry.SimplificationContraction.Average computes a simple 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                    impress                    (                    f                    'voxel_size =                                        {                    voxel_size                    :                    eastward                    }                    '                    )                    mesh_smp                    =                    mesh_in                    .                    simplify_vertex_clustering                    (                    voxel_size                    =                    voxel_size                    ,                    wrinkle                    =                    o3d                    .                    geometry                    .                    SimplificationContraction                    .                    Boilerplate                    )                    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                    ())                    /                    sixteen                    print                    (                    f                    'voxel_size =                                        {                    voxel_size                    :                    e                    }                    '                    )                    mesh_smp                    =                    mesh_in                    .                    simplify_vertex_clustering                    (                    voxel_size                    =                    voxel_size                    ,                    wrinkle                    =                    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                    ])                  
Input mesh has 1440 vertices and 2880 triangles                  

../../_images/tutorial_geometry_mesh_37_1.png

voxel_size = 5.650959e+00 Simplified mesh has 1355 vertices and 2720 triangles                  

../../_images/tutorial_geometry_mesh_37_3.png

voxel_size = i.130192e+01 Simplified mesh has 860 vertices and 1773 triangles                  

../../_images/tutorial_geometry_mesh_37_5.png

Mesh decimation¶

Some other category of mesh simplification methods is mesh decimation that operates in incremental steps. We select a single triangle that minimizes an error metric and removes it. This is repeated until a required number of triangles is accomplished. Open3D implements simplify_quadric_decimation that minimizes error quadrics (distances to neighboring planes). The parameter target_number_of_triangles defines the stopping critera of the decimation algorithm.

                                        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 1440 vertices and 2880 triangles                  

../../_images/tutorial_geometry_mesh_39_1.png

Simplified mesh has 850 vertices and 1700 triangles                  

../../_images/tutorial_geometry_mesh_39_3.png

Connected components¶

The result of diverse reconstruction methods. Open3D implements a connected components algorithm cluster_connected_triangles that assigns each triangle to a cluster of connected triangles. Information technology returns for each triangle the index of the cluster in triangle_clusters , and per cluster the number of triangles in cluster_n_triangles and the expanse of the cluster in cluster_area .

This is useful in for instance RGBD Integration, which is not e'er a single triangle mesh, but a number of meshes. Some of the smaller parts are due to noise and we almost likely want to remove them.

The code below shows the application of cluster_connected_triangles and how it can exist used to remove spurious 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                  (                  centrality                  =                  0                  ),                  vert                  .                  max                  (                  axis                  =                  0                  )                  for                  _                  in                  range                  (                  30                  ):                  cube                  =                  o3d                  .                  geometry                  .                  TriangleMesh                  .                  create_box                  ()                  cube                  .                  scale                  (                  0.005                  ,                  centre                  =                  cube                  .                  get_center                  ())                  cube                  .                  interpret                  (                  (                  np                  .                  random                  .                  uniform                  (                  min_vert                  [                  0                  ],                  max_vert                  [                  0                  ]),                  np                  .                  random                  .                  compatible                  (                  min_vert                  [                  ane                  ],                  max_vert                  [                  one                  ]),                  np                  .                  random                  .                  uniform                  (                  min_vert                  [                  ii                  ],                  max_vert                  [                  2                  ]),                  ),                  relative                  =                  False                  ,                  )                  mesh                  +=                  cube                  mesh                  .                  compute_vertex_normals                  ()                  print                  (                  "Show input mesh"                  )                  o3d                  .                  visualization                  .                  draw_geometries                  ([                  mesh                  ])                
Generate data Prove input mesh                

../../_images/tutorial_geometry_mesh_41_1.png

                                    impress                  (                  "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                  )                
Cluster connected triangles [Open3D DEBUG] [ClusterConnectedTriangles] Compute triangle adjacency [Open3D DEBUG] [ClusterConnectedTriangles] Washed computing triangle adjacency [Open3D DEBUG] [ClusterConnectedTriangles] Washed clustering, #clusters=31                
                                    print                  (                  "Evidence 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                  ])                
Show mesh with pocket-size clusters removed                

../../_images/tutorial_geometry_mesh_43_1.png

                                    print                  (                  "Show largest cluster"                  )                  mesh_1                  =                  copy                  .                  deepcopy                  (                  mesh                  )                  largest_cluster_idx                  =                  cluster_n_triangles                  .                  argmax                  ()                  triangles_to_remove                  =                  triangle_clusters                  !=                  largest_cluster_idx                  mesh_1                  .                  remove_triangles_by_mask                  (                  triangles_to_remove                  )                  o3d                  .                  visualization                  .                  draw_geometries                  ([                  mesh_1                  ])                

../../_images/tutorial_geometry_mesh_44_1.png