Distance Queries 距离查询
Open3D 中的类RaycastingScene提供了一组距离查询,可用于将三角形网格转换为隐式函数、查询到表面的距离或确定点是否位于网格内。
在本教程中,我们将展示如何生成这些查询,以及如何从几何处理和 3D 机器学习中使用的网格生成隐式表示。
Converting a mesh to an implicit representation
使用三角网格初始化RaycastingScene
# Load mesh and convert to open3d.t.geometry.TriangleMesh armadillo_data = o3d.data.ArmadilloMesh() mesh = o3d.io.read_triangle_mesh(armadillo_data.path) mesh = o3d.t.geometry.TriangleMesh.from_legacy(mesh) # Create a scene and add the triangle mesh scene = o3d.t.geometry.RaycastingScene() _ = scene.add_triangles(mesh) # we do not need the geometry ID for mesh
计算单个点的距离和占用
RaycastingScene 直接提供用于计算从点到网格表面的无符号和有符号距离的函数。它还提供了一个函数来计算查询点的占用率。
query_point = o3d.core.Tensor([[10, 10, 10]], dtype=o3d.core.Dtype.Float32) # Compute distance of the query point from the surface unsigned_distance = scene.compute_distance(query_point) signed_distance = scene.compute_signed_distance(query_point) occupancy = scene.compute_occupancy(query_point) print("unsigned distance", unsigned_distance.numpy()) print("signed_distance", signed_distance.numpy()) print("occupancy", occupancy.numpy())
虽然始终可以计算无符号距离,但符号距离和占用率仅在网格是水密且内部和外部明确定义的情况下才有效。
如果查询点位于网格内,则有符号距离为负。对于网格外的点,占用率为 0,对于网格内的点,占用率为 1。
计算多个点和网格的距离
RaycastingScene允许一次进行多个查询。例如,我们可以通过一个[N,3]张量表示N个查询点,这些查询点可用于随机采样一个体积,以训练机器学习中的隐式神经表示。
min_bound = mesh.vertex['positions'].min(0).numpy() max_bound = mesh.vertex['positions'].max(0).numpy() N = 256 query_points = np.random.uniform(low=min_bound, high=max_bound, size=[N, 3]).astype(np.float32) # Compute the signed distance for N random points signed_distance = scene.compute_signed_distance(query_points)
RaycastingScene允许使用任意数量的前导维度来组织查询点。要查询格网(grid)的有符号距离,我们可以执行以下操作
xyz_range = np.linspace(min_bound, max_bound, num=32) # query_points is a [32,32,32,3] array .. query_points = np.stack(np.meshgrid(*xyz_range.T), axis=-1).astype(np.float32) # signed distance is a [32,32,32] array signed_distance = scene.compute_signed_distance(query_points) # We can visualize a slice of the distance field directly with matplotlib plt.imshow(signed_distance.numpy()[:, :, 15])
同样的过程也适用于 RaycastingScene.compute_distance和RaycastingScene.compute_occupancy ,可用于生成无符号距离和占用字段。
Computing distances with closest point queries 使用最近点查询计算距离
距离函数建立在compute_closest_points()函数之上。在这一部分中,我们将重新实现有符号距离,并演示如何利用函数compute_closest_points()返回的其他信息。
初始化
我们首先使用两个三角形网格初始化RaycastingScene。两个网格都是水密的,我们将放置它们,使它们之间没有交集。
cube = o3d.t.geometry.TriangleMesh.from_legacy( o3d.geometry.TriangleMesh.create_box().translate([-1.2, -1.2, 0])) sphere = o3d.t.geometry.TriangleMesh.from_legacy( o3d.geometry.TriangleMesh.create_sphere(0.5).translate([0.7, 0.8, 0])) scene = o3d.t.geometry.RaycastingScene() # Add triangle meshes and remember ids mesh_ids = {} mesh_ids[scene.add_triangles(cube)] = 'cube' mesh_ids[scene.add_triangles(sphere)] = 'sphere'
计算表面上最近的点
RaycastingScene.compute_closest_points()可以计算表面上相对于查询点的最近点。
query_point = o3d.core.Tensor([[0, 0, 0]], dtype=o3d.core.Dtype.Float32) # We compute the closest point on the surface for the point at position [0,0,0]. ans = scene.compute_closest_points(query_point) # Compute_closest_points provides the point on the surface, the geometry id, # and the primitive id. # The dictionary keys are #. points #. geometry_ids #. primitive_ids print('The closest point on the surface is', ans['points'].numpy()) print('The closest point is on the surface of the', mesh_ids[ans['geometry_ids'][0].item()]) print('The closest point belongs to triangle', ans['primitive_ids'][0].item())
为了计算点是在内部还是外部,我们可以从查询点开始投射光线,并计算交集的数量。
rays = np.concatenate( [query_point.numpy(), np.ones(query_point.shape, dtype=np.float32)], axis=-1) intersection_counts = scene.count_intersections(rays).numpy() # A point is inside if the number of intersections with the scene is even # This sssumes that inside and outside is we ll defined for the scene. is_inside = intersection_counts % 2 == 1
我们可以将其组合成一个函数,以创建一个返回附加信息的特殊有符号距离函数:
def compute_signed_distance_and_closest_goemetry(query_points: np.ndarray): closest_points = scene.compute_closest_points(query_points) distance = np.linalg.norm(query_points - closest_points['points'].numpy(), axis=-1) rays = np.concatenate([query_points, np.ones_like(query_points)], axis=-1) intersection_counts = scene.count_intersections(rays).numpy() is_inside = intersection_counts % 2 == 1 distance[is_inside] *= -1 return distance, closest_points['geometry_ids'].numpy()
我们可以使用该函数创建具有距离和几何 ID 信息的网格
# compute range xyz_range = np.linspace([-2, -2, -2], [2, 2, 2], num=32) # query_points is a [32,32,32,3] array .. query_points = np.stack(np.meshgrid(*xyz_range.T), axis=-1).astype(np.float32) sdf, closest_geom = compute_signed_distance_and_closest_goemetry(query_points) # We can visualize a slice of the grids directly with matplotlib fig, axes = plt.subplots(1, 2) axes[0].imshow(sdf[:, :, 16]) axes[1].imshow(closest_geom[:, :, 16])