Triangle Mesh#

triangle_mesh_connected_components.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9import numpy as np
10import copy
11
12if __name__ == "__main__":
13    bunny = o3d.data.BunnyMesh()
14    mesh = o3d.io.read_triangle_mesh(bunny.path)
15    mesh.compute_vertex_normals()
16
17    mesh = mesh.subdivide_midpoint(number_of_iterations=2)
18    vert = np.asarray(mesh.vertices)
19    min_vert, max_vert = vert.min(axis=0), vert.max(axis=0)
20    for _ in range(30):
21        cube = o3d.geometry.TriangleMesh.create_box()
22        cube.scale(0.005, center=cube.get_center())
23        cube.translate(
24            (
25                np.random.uniform(min_vert[0], max_vert[0]),
26                np.random.uniform(min_vert[1], max_vert[1]),
27                np.random.uniform(min_vert[2], max_vert[2]),
28            ),
29            relative=False,
30        )
31        mesh += cube
32    mesh.compute_vertex_normals()
33    print("Displaying input mesh ...")
34    o3d.visualization.draw([mesh])
35
36    print("Clustering connected triangles ...")
37    with o3d.utility.VerbosityContextManager(
38            o3d.utility.VerbosityLevel.Debug) as cm:
39        triangle_clusters, cluster_n_triangles, cluster_area = (
40            mesh.cluster_connected_triangles())
41    triangle_clusters = np.asarray(triangle_clusters)
42    cluster_n_triangles = np.asarray(cluster_n_triangles)
43    cluster_area = np.asarray(cluster_area)
44
45    print("Displaying mesh with small clusters removed ...")
46    mesh_0 = copy.deepcopy(mesh)
47    triangles_to_remove = cluster_n_triangles[triangle_clusters] < 100
48    mesh_0.remove_triangles_by_mask(triangles_to_remove)
49    o3d.visualization.draw([mesh_0])

triangle_mesh_cropping.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9import numpy as np
10import copy
11
12if __name__ == "__main__":
13    knot_mesh = o3d.data.KnotMesh()
14    mesh = o3d.io.read_triangle_mesh(knot_mesh.path)
15    mesh.compute_vertex_normals()
16    print("Displaying original mesh ...")
17    o3d.visualization.draw([mesh])
18
19    print("Displaying mesh of only the first half triangles ...")
20    mesh_cropped = copy.deepcopy(mesh)
21    mesh_cropped.triangles = o3d.utility.Vector3iVector(
22        np.asarray(mesh_cropped.triangles)[:len(mesh_cropped.triangles) //
23                                           2, :])
24    mesh_cropped.triangle_normals = o3d.utility.Vector3dVector(
25        np.asarray(mesh_cropped.triangle_normals)
26        [:len(mesh_cropped.triangle_normals) // 2, :])
27    print(mesh_cropped.triangles)
28    o3d.visualization.draw([mesh_cropped])

triangle_mesh_deformation.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import numpy as np
 9import open3d as o3d
10import time
11import os
12import sys
13
14pyexample_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
15sys.path.append(pyexample_path)
16
17import open3d_example as o3dex
18
19
20def problem0():
21    mesh = o3dex.get_plane_mesh(height=1, width=1)
22    mesh = mesh.subdivide_midpoint(3)
23    vertices = np.asarray(mesh.vertices)
24    static_ids = [
25        1, 46, 47, 48, 16, 51, 49, 50, 6, 31, 33, 32, 11, 26, 27, 25, 0, 64, 65,
26        20, 66, 68, 67, 7, 69, 71, 70, 22, 72, 74, 73, 3, 15, 44, 43, 45, 5, 41,
27        40, 42, 13, 39, 37, 38, 2, 56, 55, 19, 61, 60, 59, 8, 76, 75, 77, 23
28    ]
29    static_positions = []
30    for id in static_ids:
31        static_positions.append(vertices[id])
32    handle_ids = [4]
33    handle_positions = [vertices[4] + np.array((0, 0, 0.4))]
34
35    return mesh, static_ids + handle_ids, static_positions + handle_positions
36
37
38def problem1():
39    mesh = o3dex.get_plane_mesh(height=1, width=1)
40    mesh = mesh.subdivide_midpoint(3)
41    vertices = np.asarray(mesh.vertices)
42    static_ids = [
43        1, 46, 15, 43, 5, 40, 13, 38, 2, 56, 37, 39, 42, 41, 45, 44, 48, 47
44    ]
45    static_positions = []
46    for id in static_ids:
47        static_positions.append(vertices[id])
48    handle_ids = [21]
49    handle_positions = [vertices[21] + np.array((0, 0, 0.4))]
50
51    return mesh, static_ids + handle_ids, static_positions + handle_positions
52
53
54def problem2():
55    armadillo_data = o3d.data.ArmadilloMesh()
56    mesh = o3d.io.read_triangle_mesh(armadillo_data.path)
57    vertices = np.asarray(mesh.vertices)
58    static_ids = [idx for idx in np.where(vertices[:, 1] < -30)[0]]
59    static_positions = []
60    for id in static_ids:
61        static_positions.append(vertices[id])
62    handle_ids = [2490]
63    handle_positions = [vertices[2490] + np.array((-40, -40, -40))]
64
65    return mesh, static_ids + handle_ids, static_positions + handle_positions
66
67
68if __name__ == "__main__":
69    o3d.utility.set_verbosity_level(o3d.utility.Debug)
70
71    for mesh, constraint_ids, constraint_pos in [
72            problem0(), problem1(), problem2()
73    ]:
74        constraint_ids = np.array(constraint_ids, dtype=np.int32)
75        constraint_pos = o3d.utility.Vector3dVector(constraint_pos)
76        tic = time.time()
77        mesh_prime = mesh.deform_as_rigid_as_possible(
78            o3d.utility.IntVector(constraint_ids), constraint_pos, max_iter=50)
79        print("deform took {}[s]".format(time.time() - tic))
80        mesh_prime.compute_vertex_normals()
81
82        mesh.paint_uniform_color((1, 0, 0))
83        handles = o3d.geometry.PointCloud()
84        handles.points = constraint_pos
85        handles.paint_uniform_color((0, 1, 0))
86        o3d.visualization.draw_geometries([mesh, mesh_prime, handles])
87
88    o3d.utility.set_verbosity_level(o3d.utility.Info)

triangle_mesh_filtering_average.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8from numpy.random.mtrand import laplace
 9import open3d as o3d
10import numpy as np
11
12
13def average_filtering():
14    # Create noisy mesh.
15    knot_mesh = o3d.data.KnotMesh()
16    mesh_in = o3d.io.read_triangle_mesh(knot_mesh.path)
17    vertices = np.asarray(mesh_in.vertices)
18    noise = 5
19    vertices += np.random.uniform(0, noise, size=vertices.shape)
20    mesh_in.vertices = o3d.utility.Vector3dVector(vertices)
21    mesh_in.compute_vertex_normals()
22    print("Displaying input mesh ...")
23    o3d.visualization.draw_geometries([mesh_in])
24
25    print("Displaying output of average mesh filter after 1 iteration ...")
26    mesh_out = mesh_in.filter_smooth_simple(number_of_iterations=1)
27    mesh_out.compute_vertex_normals()
28    o3d.visualization.draw_geometries([mesh_out])
29
30    print("Displaying output of average mesh filter after 5 iteration ...")
31    mesh_out = mesh_in.filter_smooth_simple(number_of_iterations=5)
32    mesh_out.compute_vertex_normals()
33    o3d.visualization.draw_geometries([mesh_out])
34
35
36def laplace_filtering():
37    # Create noisy mesh.
38    knot_mesh = o3d.data.KnotMesh()
39    mesh_in = o3d.io.read_triangle_mesh(knot_mesh.path)
40    vertices = np.asarray(mesh_in.vertices)
41    noise = 5
42    vertices += np.random.uniform(0, noise, size=vertices.shape)
43    mesh_in.vertices = o3d.utility.Vector3dVector(vertices)
44    mesh_in.compute_vertex_normals()
45    print("Displaying input mesh ...")
46    o3d.visualization.draw_geometries([mesh_in])
47
48    print("Displaying output of Laplace mesh filter after 10 iteration ...")
49    mesh_out = mesh_in.filter_smooth_laplacian(number_of_iterations=10)
50    mesh_out.compute_vertex_normals()
51    o3d.visualization.draw_geometries([mesh_out])
52
53    print("Displaying output of Laplace mesh filter after 50 iteration ...")
54    mesh_out = mesh_in.filter_smooth_laplacian(number_of_iterations=50)
55    mesh_out.compute_vertex_normals()
56    o3d.visualization.draw_geometries([mesh_out])
57
58
59def taubin_filtering():
60    # Create noisy mesh.
61    knot_mesh = o3d.data.KnotMesh()
62    mesh_in = o3d.io.read_triangle_mesh(knot_mesh.path)
63    vertices = np.asarray(mesh_in.vertices)
64    noise = 5
65    vertices += np.random.uniform(0, noise, size=vertices.shape)
66    mesh_in.vertices = o3d.utility.Vector3dVector(vertices)
67    mesh_in.compute_vertex_normals()
68    print("Displaying input mesh ...")
69    o3d.visualization.draw_geometries([mesh_in])
70
71    print("Displaying output of Taubin mesh filter after 10 iteration ...")
72    mesh_out = mesh_in.filter_smooth_taubin(number_of_iterations=10)
73    mesh_out.compute_vertex_normals()
74    o3d.visualization.draw_geometries([mesh_out])
75
76    print("Displaying output of Taubin mesh filter after 100 iteration ...")
77    mesh_out = mesh_in.filter_smooth_taubin(number_of_iterations=100)
78    mesh_out.compute_vertex_normals()
79    o3d.visualization.draw_geometries([mesh_out])
80
81
82if __name__ == "__main__":
83    average_filtering()
84    laplace_filtering()
85    taubin_filtering()

triangle_mesh_from_point_cloud_alpha_shapes.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9
10if __name__ == "__main__":
11    bunny = o3d.data.BunnyMesh()
12    mesh = o3d.io.read_triangle_mesh(bunny.path)
13    mesh.compute_vertex_normals()
14
15    pcd = mesh.sample_points_poisson_disk(750)
16    print("Displaying input pointcloud ...")
17    o3d.visualization.draw_geometries([pcd])
18    alpha = 0.03
19    print(f"alpha={alpha:.3f}")
20    print('Running alpha shapes surface reconstruction ...')
21    mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_alpha_shape(
22        pcd, alpha)
23    mesh.compute_triangle_normals(normalized=True)
24    print("Displaying reconstructed mesh ...")
25    o3d.visualization.draw_geometries([mesh], mesh_show_back_face=True)

triangle_mesh_from_point_cloud_ball_pivoting.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9
10if __name__ == "__main__":
11    bunny = o3d.data.BunnyMesh()
12    gt_mesh = o3d.io.read_triangle_mesh(bunny.path)
13    gt_mesh.compute_vertex_normals()
14
15    pcd = gt_mesh.sample_points_poisson_disk(3000)
16    print("Displaying input pointcloud ...")
17    o3d.visualization.draw([pcd], point_size=5)
18
19    radii = [0.005, 0.01, 0.02, 0.04]
20    print('Running ball pivoting surface reconstruction ...')
21    rec_mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_ball_pivoting(
22        pcd, o3d.utility.DoubleVector(radii))
23    print("Displaying reconstructed mesh ...")
24    o3d.visualization.draw([rec_mesh])

triangle_mesh_from_point_cloud_poisson.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9import numpy as np
10
11if __name__ == "__main__":
12    eagle = o3d.data.EaglePointCloud()
13    pcd = o3d.io.read_point_cloud(eagle.path)
14    R = pcd.get_rotation_matrix_from_xyz((np.pi, -np.pi / 4, 0))
15    pcd.rotate(R, center=(0, 0, 0))
16    print('Displaying input pointcloud ...')
17    o3d.visualization.draw([pcd])
18
19    print('Running Poisson surface reconstruction ...')
20    mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
21        pcd, depth=9)
22    print('Displaying reconstructed mesh ...')
23    o3d.visualization.draw([mesh])

triangle_mesh_normal_estimation.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9import numpy as np
10
11if __name__ == "__main__":
12    knot_mesh = o3d.data.KnotMesh()
13    mesh = o3d.io.read_triangle_mesh(knot_mesh.path)
14    print("Displaying mesh without normals ...")
15    # Invalidate existing normals.
16    mesh.triangle_normals = o3d.utility.Vector3dVector(np.zeros((1, 3)))
17    print("normals: \n", np.asarray(mesh.triangle_normals))
18    o3d.visualization.draw([mesh])
19
20    print("Computing normals and rendering it ...")
21    mesh.compute_vertex_normals()
22    print("normals: \n", np.asarray(mesh.triangle_normals))
23    o3d.visualization.draw([mesh])

triangle_mesh_project_to_albedo.py#

  1# ----------------------------------------------------------------------------
  2# -                        Open3D: www.open3d.org                            -
  3# ----------------------------------------------------------------------------
  4# Copyright (c) 2018-2024 www.open3d.org
  5# SPDX-License-Identifier: MIT
  6# ----------------------------------------------------------------------------
  7"""This example demonstrates project_image_to_albedo. Use create_dataset mode to
  8render images of a 3D mesh or model from different viewpoints.
  9albedo_from_dataset mode then uses the calibrated images to re-create the albedo
 10texture for the mesh.
 11"""
 12import argparse
 13from pathlib import Path
 14import subprocess as sp
 15import time
 16import numpy as np
 17import open3d as o3d
 18from open3d.visualization import gui, rendering, O3DVisualizer
 19from open3d.core import Tensor
 20
 21
 22def download_smithsonian_baluster_vase():
 23    """Download the Smithsonian Baluster Vase 3D model."""
 24    vase_url = 'https://3d-api.si.edu/content/document/3d_package:d8c62634-4ebc-11ea-b77f-2e728ce88125/resources/F1980.190%E2%80%93194_baluster_vase-150k-4096.glb'
 25    import urllib.request
 26
 27    def show_progress(block_num, block_size, total_size):
 28        total_size = total_size >> 20 if total_size > 0 else "??"  # Convert to MB if known
 29        print(
 30            "Downloading F1980_baluster_vase.glb... "
 31            f"{(block_num * block_size) >>20}MB / {total_size}MB",
 32            end="\r")
 33
 34    urllib.request.urlretrieve(vase_url,
 35                               filename="F1980_baluster_vase.glb",
 36                               reporthook=show_progress)
 37    print("\nDownload complete.")
 38
 39
 40def create_dataset(meshfile, n_images=10, movie=False, vary_exposure=False):
 41    """Render images of a 3D mesh from different viewpoints, covering the
 42    northern hemisphere. These form a synthetic dataset to test the
 43    project_images_to_albedo function.
 44    """
 45    # Adjust these parameters to properly frame your model.
 46    # Window system pixel scaling (e.g. 1 for normal, 2 for HiDPI / retina display)
 47    SCALING = 2
 48    width, height = 1024, 1024  # image width, height
 49    focal_length = 512
 50    d_camera_obj = 0.3  # distance from camera to object
 51    K = np.array([[focal_length, 0, width / 2], [0, focal_length, height / 2],
 52                  [0, 0, 1]])
 53    t = np.array([0, 0, d_camera_obj])  # origin / object in camera ref frame
 54
 55    model = o3d.io.read_triangle_model(meshfile)
 56    # DefaultLit shader will produce non-uniform images with specular
 57    # highlights, etc. These should be avoided to accurately capture the diffuse
 58    # albedo
 59    unlit = rendering.MaterialRecord()
 60    unlit.shader = "unlit"
 61
 62    def triangle_wave(n, period=1):
 63        """Triangle wave function between [0,1] with given period."""
 64        return abs(n % period - period / 2) / (period / 2)
 65
 66    def rotate_camera_and_shoot(o3dvis):
 67        Rts = []
 68        images = []
 69        o3dvis.scene.scene.enable_sun_light(False)
 70        print("Rendering images: ", end='', flush=True)
 71        n_0 = 2 * n_images // 3
 72        n_1 = n_images - n_0 - 1
 73        for n in range(n_images):
 74            Rt = np.eye(4)
 75            Rt[:3, 3] = t
 76            if n < n_0:
 77                theta = n * (2 * np.pi) / n_0
 78                Rt[:3, :
 79                   3] = o3d.geometry.Geometry3D.get_rotation_matrix_from_zyx(
 80                       [np.pi, theta, 0])
 81            elif n < n_images - 1:
 82                theta = (n - n_0) * (2 * np.pi) / n_1
 83                Rt[:3, :
 84                   3] = o3d.geometry.Geometry3D.get_rotation_matrix_from_xyz(
 85                       [np.pi / 4, theta, np.pi])
 86            else:  # one image from the top
 87                Rt[:3, :
 88                   3] = o3d.geometry.Geometry3D.get_rotation_matrix_from_zyx(
 89                       [np.pi, 0, -np.pi / 2])
 90            Rts.append(Rt)
 91            o3dvis.setup_camera(K, Rt, width, height)
 92            # Vary IBL intensity as a poxy for exposure value. IBL ranges from
 93            # [0,150000]. We vary it between 20000 and 100000.
 94            if vary_exposure:
 95                o3dvis.set_ibl_intensity(20000 +
 96                                         80000 * triangle_wave(n, n_images / 4))
 97            o3dvis.post_redraw()
 98            o3dvis.export_current_image(f"render-{n:02}.jpg")
 99            images.append(f"render-{n:02}.jpg")
100            print('.', end='', flush=True)
101        np.savez("cameras.npz",
102                 width=width,
103                 height=height,
104                 K=K,
105                 Rts=Rts,
106                 images=images)
107        # Now create a movie from the saved images by calling ffmpeg with
108        # subprocess
109        if movie:
110            print("\nCreating movie...", end='', flush=True)
111            sp.run([
112                "ffmpeg", "-framerate", f"{n_images/6}", "-pattern_type",
113                "glob", "-i", "render-*.jpg", "-y", meshfile.stem + ".mp4"
114            ],
115                   check=True)
116        o3dvis.close()
117        print("\nDone.")
118
119    print("If the object is properly framed in the GUI window, click on the "
120          "'Save Images' action in the menu.")
121    o3d.visualization.draw([{
122        'geometry': model,
123        'name': meshfile.name,
124        'material': unlit
125    }],
126                           show_ui=False,
127                           width=int(width / SCALING),
128                           height=int(height / SCALING),
129                           actions=[("Save Images", rotate_camera_and_shoot)])
130
131
132def albedo_from_images(meshfile, calib_data_file, albedo_contrast=1.25):
133
134    model = o3d.io.read_triangle_model(meshfile)
135    tmeshes = o3d.t.geometry.TriangleMesh.from_triangle_mesh_model(model)
136    tmeshes = list(tmeshes.values())
137    calib = np.load(calib_data_file)
138    Ks = list(Tensor(calib["K"]) for _ in range(len(calib["Rts"])))
139    Rts = list(Tensor(Rt) for Rt in calib["Rts"])
140    images = list(o3d.t.io.read_image(imfile) for imfile in calib["images"])
141    calib.close()
142    start = time.time()
143    with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug):
144        albedo = tmeshes[0].project_images_to_albedo(images, Ks, Rts, 1024,
145                                                     True)
146    albedo = albedo.linear_transform(scale=albedo_contrast)  # brighten albedo
147    tmeshes[0].material.texture_maps["albedo"] = albedo
148    print(f"project_images_to_albedo ran in {time.time()-start:.2f}s")
149    o3d.t.io.write_image("albedo.png", albedo)
150    o3d.t.io.write_triangle_mesh(meshfile.stem + "_albedo.glb", tmeshes[0])
151    cam_vis = list({
152        "name":
153            f"camera-{i:02}",
154        "geometry":
155            o3d.geometry.LineSet.create_camera_visualization(
156                images[0].columns, images[0].rows, K.numpy(), Rt.numpy(), 0.1)
157    } for i, (K, Rt) in enumerate(zip(Ks, Rts)))
158    o3d.visualization.draw(cam_vis + [{
159        "name": meshfile.name,
160        "geometry": tmeshes[0]
161    }],
162                           show_ui=True)
163
164
165if __name__ == "__main__":
166
167    parser = argparse.ArgumentParser(description=__doc__)
168    parser.add_argument("action",
169                        choices=('create_dataset', 'albedo_from_images'))
170    parser.add_argument("--meshfile",
171                        type=Path,
172                        default=".",
173                        help="Path to mesh file.")
174    parser.add_argument("--n-images",
175                        type=int,
176                        default=10,
177                        help="Number of images to render.")
178    parser.add_argument("--download_sample_model",
179                        help="Download a sample 3D model for this example.",
180                        action="store_true")
181    parser.add_argument(
182        "--movie",
183        action="store_true",
184        help=
185        "Create movie from rendered images with ffmpeg. ffmpeg must be installed and in path."
186    )
187    args = parser.parse_args()
188
189    if args.action == "create_dataset":
190        if args.download_sample_model:
191            download_smithsonian_baluster_vase()
192            args.meshfile = "F1980_baluster_vase.glb"
193        if args.meshfile == Path("."):
194            parser.error("Please provide a path to a mesh file, or use "
195                         "--download_sample_model.")
196        if args.n_images < 10:
197            parser.error("Atleast 10 images should be used!")
198        create_dataset(args.meshfile,
199                       n_images=args.n_images,
200                       movie=args.movie,
201                       vary_exposure=True)
202    else:
203        albedo_from_images(args.meshfile, "cameras.npz")

triangle_mesh_properties.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9import numpy as np
10import os
11import sys
12
13pyexample_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
14sys.path.append(pyexample_path)
15
16import open3d_example as o3dex
17
18
19def check_properties(name, mesh):
20    mesh.compute_vertex_normals()
21
22    edge_manifold = mesh.is_edge_manifold(allow_boundary_edges=True)
23    edge_manifold_boundary = mesh.is_edge_manifold(allow_boundary_edges=False)
24    vertex_manifold = mesh.is_vertex_manifold()
25    self_intersecting = mesh.is_self_intersecting()
26    watertight = mesh.is_watertight()
27    orientable = mesh.is_orientable()
28
29    print(name)
30    print(f"  edge_manifold:          {edge_manifold}")
31    print(f"  edge_manifold_boundary: {edge_manifold_boundary}")
32    print(f"  vertex_manifold:        {vertex_manifold}")
33    print(f"  self_intersecting:      {self_intersecting}")
34    print(f"  watertight:             {watertight}")
35    print(f"  orientable:             {orientable}")
36
37    geoms = [mesh]
38    if not edge_manifold:
39        edges = mesh.get_non_manifold_edges(allow_boundary_edges=True)
40        geoms.append(o3dex.edges_to_lineset(mesh, edges, (1, 0, 0)))
41    if not edge_manifold_boundary:
42        edges = mesh.get_non_manifold_edges(allow_boundary_edges=False)
43        geoms.append(o3dex.edges_to_lineset(mesh, edges, (0, 1, 0)))
44    if not vertex_manifold:
45        verts = np.asarray(mesh.get_non_manifold_vertices())
46        pcl = o3d.geometry.PointCloud(
47            points=o3d.utility.Vector3dVector(np.asarray(mesh.vertices)[verts]))
48        pcl.paint_uniform_color((0, 0, 1))
49        geoms.append(pcl)
50    if self_intersecting:
51        intersecting_triangles = np.asarray(
52            mesh.get_self_intersecting_triangles())
53        intersecting_triangles = intersecting_triangles[0:1]
54        intersecting_triangles = np.unique(intersecting_triangles)
55        print("  # visualize self-intersecting triangles")
56        triangles = np.asarray(mesh.triangles)[intersecting_triangles]
57        edges = [
58            np.vstack((triangles[:, i], triangles[:, j]))
59            for i, j in [(0, 1), (1, 2), (2, 0)]
60        ]
61        edges = np.hstack(edges).T
62        edges = o3d.utility.Vector2iVector(edges)
63        geoms.append(o3dex.edges_to_lineset(mesh, edges, (1, 0, 1)))
64    o3d.visualization.draw_geometries(geoms, mesh_show_back_face=True)
65
66
67if __name__ == "__main__":
68    knot_mesh = o3d.data.KnotMesh()
69    mesh = o3d.io.read_triangle_mesh(knot_mesh.path)
70    check_properties('KnotMesh', mesh)
71    check_properties('Mobius',
72                     o3d.geometry.TriangleMesh.create_mobius(twists=1))
73    check_properties("non-manifold edge", o3dex.get_non_manifold_edge_mesh())
74    check_properties("non-manifold vertex",
75                     o3dex.get_non_manifold_vertex_mesh())
76    check_properties("open box", o3dex.get_open_box_mesh())
77    check_properties("intersecting_boxes", o3dex.get_intersecting_boxes_mesh())

triangle_mesh_sampling.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9
10if __name__ == "__main__":
11    bunny = o3d.data.BunnyMesh()
12    mesh = o3d.io.read_triangle_mesh(bunny.path)
13    mesh.compute_vertex_normals()
14
15    print("Displaying input mesh ...")
16    o3d.visualization.draw([mesh])
17
18    print("Displaying pointcloud using uniform sampling ...")
19    pcd = mesh.sample_points_uniformly(number_of_points=1000)
20    o3d.visualization.draw([pcd], point_size=5)
21
22    print("Displaying pointcloud using Poisson disk sampling ...")
23    pcd = mesh.sample_points_poisson_disk(number_of_points=1000, init_factor=5)
24    o3d.visualization.draw([pcd], point_size=5)

triangle_mesh_simplification_decimation.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9
10if __name__ == "__main__":
11    bunny = o3d.data.BunnyMesh()
12    mesh_in = o3d.io.read_triangle_mesh(bunny.path)
13    mesh_in.compute_vertex_normals()
14
15    print("Before Simplification: ", mesh_in)
16    o3d.visualization.draw_geometries([mesh_in])
17
18    mesh_smp = mesh_in.simplify_quadric_decimation(
19        target_number_of_triangles=6500)
20    print("After Simplification target number of triangles = 6500:\n", mesh_smp)
21    o3d.visualization.draw_geometries([mesh_smp])
22
23    mesh_smp = mesh_in.simplify_quadric_decimation(
24        target_number_of_triangles=1700)
25    print("After Simplification target number of triangles = 1700:\n", mesh_smp)
26    o3d.visualization.draw_geometries([mesh_smp])

triangle_mesh_simplification_vertex_clustering.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9
10if __name__ == "__main__":
11    bunny = o3d.data.BunnyMesh()
12    mesh_in = o3d.io.read_triangle_mesh(bunny.path)
13    mesh_in.compute_vertex_normals()
14
15    print("Before Simplification: ", mesh_in)
16    o3d.visualization.draw_geometries([mesh_in])
17
18    voxel_size = max(mesh_in.get_max_bound() - mesh_in.get_min_bound()) / 32
19    mesh_smp = mesh_in.simplify_vertex_clustering(
20        voxel_size=voxel_size,
21        contraction=o3d.geometry.SimplificationContraction.Average)
22    print("After Simplification with voxel size =", voxel_size, ":\n", mesh_smp)
23    o3d.visualization.draw_geometries([mesh_smp])
24
25    voxel_size = max(mesh_in.get_max_bound() - mesh_in.get_min_bound()) / 16
26    mesh_smp = mesh_in.simplify_vertex_clustering(
27        voxel_size=voxel_size,
28        contraction=o3d.geometry.SimplificationContraction.Average)
29    print("After Simplification with voxel size =", voxel_size, ":\n", mesh_smp)
30    o3d.visualization.draw_geometries([mesh_smp])

triangle_mesh_subdivision.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9
10if __name__ == "__main__":
11    knot_mesh = o3d.data.KnotMesh()
12    mesh = o3d.io.read_triangle_mesh(knot_mesh.path)
13    mesh.compute_vertex_normals()
14    print("Before Subdivision: ", mesh)
15    print("Displaying input mesh ...")
16    o3d.visualization.draw_geometries([mesh], mesh_show_wireframe=True)
17    mesh = mesh.subdivide_loop(number_of_iterations=1)
18    print("After Subdivision: ", mesh)
19    print("Displaying subdivided mesh ...")
20    o3d.visualization.draw_geometries([mesh], mesh_show_wireframe=True)

triangle_mesh_transformation.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9import numpy as np
10import copy
11
12
13def translate():
14    mesh = o3d.geometry.TriangleMesh.create_coordinate_frame()
15    mesh_tx = copy.deepcopy(mesh).translate((1.3, 0, 0))
16    mesh_ty = copy.deepcopy(mesh).translate((0, 1.3, 0))
17    print('Displaying original and translated geometries ...')
18    o3d.visualization.draw([{
19        "name": "Original Geometry",
20        "geometry": mesh
21    }, {
22        "name": "Translated (in X) Geometry",
23        "geometry": mesh_tx
24    }, {
25        "name": "Translated (in Y) Geometry",
26        "geometry": mesh_ty
27    }],
28                           show_ui=True)
29
30
31def rotate():
32    mesh = o3d.geometry.TriangleMesh.create_coordinate_frame()
33    mesh_r = copy.deepcopy(mesh)
34    R = mesh.get_rotation_matrix_from_xyz((np.pi / 2, 0, np.pi / 4))
35    mesh_r.rotate(R, center=(0, 0, 0))
36    print('Displaying original and rotated geometries ...')
37    o3d.visualization.draw([{
38        "name": "Original Geometry",
39        "geometry": mesh
40    }, {
41        "name": "Rotated Geometry",
42        "geometry": mesh_r
43    }],
44                           show_ui=True)
45
46
47def scale():
48    mesh = o3d.geometry.TriangleMesh.create_coordinate_frame()
49    mesh_s = copy.deepcopy(mesh).translate((2, 0, 0))
50    mesh_s.scale(0.5, center=mesh_s.get_center())
51    print('Displaying original and scaled geometries ...')
52    o3d.visualization.draw([{
53        "name": "Original Geometry",
54        "geometry": mesh
55    }, {
56        "name": "Scaled Geometry",
57        "geometry": mesh_s
58    }],
59                           show_ui=True)
60
61
62def transform():
63    mesh = o3d.geometry.TriangleMesh.create_coordinate_frame()
64    T = np.eye(4)
65    T[:3, :3] = mesh.get_rotation_matrix_from_xyz((0, np.pi / 3, np.pi / 2))
66    T[0, 3] = 1
67    T[1, 3] = 1.3
68    print(T)
69    mesh_t = copy.deepcopy(mesh).transform(T)
70    print('Displaying original and transformed geometries ...')
71    o3d.visualization.draw([{
72        "name": "Original Geometry",
73        "geometry": mesh
74    }, {
75        "name": "Transformed Geometry",
76        "geometry": mesh_t
77    }],
78                           show_ui=True)
79
80
81if __name__ == "__main__":
82
83    translate()
84    rotate()
85    scale()
86    transform()

triangle_mesh_with_numpy.py#

 1# ----------------------------------------------------------------------------
 2# -                        Open3D: www.open3d.org                            -
 3# ----------------------------------------------------------------------------
 4# Copyright (c) 2018-2024 www.open3d.org
 5# SPDX-License-Identifier: MIT
 6# ----------------------------------------------------------------------------
 7
 8import open3d as o3d
 9import numpy as np
10
11if __name__ == "__main__":
12    # Read a mesh and get its data as numpy arrays.
13    knot_mesh = o3d.data.KnotMesh()
14    mesh = o3d.io.read_triangle_mesh(knot_mesh.path)
15    mesh.paint_uniform_color([0.5, 0.1, 0.3])
16    print('Vertices:')
17    print(np.asarray(mesh.vertices))
18    print('Vertex Colors:')
19    print(np.asarray(mesh.vertex_colors))
20    print('Vertex Normals:')
21    print(np.asarray(mesh.vertex_normals))
22    print('Triangles:')
23    print(np.asarray(mesh.triangles))
24    print('Triangle Normals:')
25    print(np.asarray(mesh.triangle_normals))
26    print("Displaying mesh ...")
27    print(mesh)
28    o3d.visualization.draw([mesh])
29
30    # Create a mesh using numpy arrays with random colors.
31    N = 5
32    vertices = o3d.utility.Vector3dVector(
33        np.array([[0, 0, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1], [0.5, 0.5, 0.5]]))
34    triangles = o3d.utility.Vector3iVector(
35        np.array([[0, 1, 2], [0, 2, 3], [0, 4, 1], [1, 4, 2], [2, 4, 3],
36                  [3, 4, 0]]))
37    mesh_np = o3d.geometry.TriangleMesh(vertices, triangles)
38    mesh_np.vertex_colors = o3d.utility.Vector3dVector(
39        np.random.uniform(0, 1, size=(N, 3)))
40    mesh_np.compute_vertex_normals()
41    print(np.asarray(mesh_np.triangle_normals))
42    print("Displaying mesh made using numpy ...")
43    o3d.visualization.draw_geometries([mesh_np])