0


『OPEN3D』1.6 Voxelization体素化

  1. 在点云处理的内容中,简单介绍了open3d中对点云下采样使用了体素的操作,这里对体素化进行详细的介绍。

点云和三角面片(triangle meshes)表达的数据是无序的几何结构;而体素则是另一种表达三维数据的几何结构,体素类似于图片中的像素,具有规则性。因此,open3d中提供了VoxelGrid几何类型用于对体素的表达。

1 从triangle meshes中创建体素

  1. 方法create_from_triangle_mesh可以从mesh中创建体素,任何一个mesh与体素相交,则该体素置为1(存在);否则置为0(不存在)。该方法包含一个参数voxel_size用于设置体素的分辨率。
  1. import copy
  2. import open3d as o3d
  3. import numpy as np
  4. if __name__ == "__main__":
  5. # bunny = o3d.data.BunnyMesh()
  6. armadillo_data = o3d.data.ArmadilloMesh()
  7. mesh = o3d.io.read_triangle_mesh(armadillo_data.path)
  8. # 计算顶点的法向量
  9. mesh.compute_vertex_normals()
  10. # Fit to unit cube.
  11. mesh.scale(1 / np.max(mesh.get_max_bound() - mesh.get_min_bound()),
  12. center=mesh.get_center())
  13. print('Displaying input mesh ...')
  14. # o3d.visualization.draw_geometries([mesh])
  15. """
  16. create_from_triangle_mesh
  17. param:
  18. voxel_size:设置每个体素的长宽高为0.5
  19. 返回值类型为o3d.geometry.VoxelGrid
  20. """
  21. mesh_for_voxelGrid: o3d.geometry.TriangleMesh = copy.deepcopy(mesh)
  22. mesh_for_voxelGrid.translate([1, 0, 0])
  23. voxel_grid: o3d.geometry.VoxelGrid = o3d.geometry.VoxelGrid.create_from_triangle_mesh(
  24. mesh_for_voxelGrid, voxel_size=0.05)
  25. print('Displaying voxel grid ...')
  26. o3d.visualization.draw_geometries([mesh,voxel_grid])

2 从点云中创建体素

  1. 使用方法create_from_point_cloud可以实现从点云中创建voxelgrid,一个voxel被占用的话,则至少该voxel中存在一个点云。voxel的颜色则是对该voxel中所有点云的颜色做平均;参数voxel_size设置voxelgrid的分辨率。
  1. import open3d as o3d
  2. import numpy as np
  3. if __name__ == "__main__":
  4. N = 3000
  5. armadillo_data = o3d.data.ArmadilloMesh()
  6. pcd = o3d.io.read_triangle_mesh(
  7. armadillo_data.path).sample_points_poisson_disk(N)
  8. # Fit to unit cube.
  9. pcd.scale(1 / np.max(pcd.get_max_bound() - pcd.get_min_bound()),
  10. center=pcd.get_center())
  11. pcd.colors = o3d.utility.Vector3dVector(np.random.uniform(0, 1,
  12. size=(N, 3)))
  13. print('Displaying input point cloud ...')
  14. o3d.visualization.draw_geometries([pcd])
  15. print('Displaying voxel grid ...')
  16. voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd,
  17. voxel_size=0.05)
  18. o3d.visualization.draw_geometries([voxel_grid])

3 体素包含测试(Inclusion test)

  1. voxel grid可以用于测试一个点云是否被体素所包含;方法check_if_included接受一个(n,3)的array;并返回array中每个点是否在voxelgrid中。
  1. import copy
  2. import open3d as o3d
  3. import numpy as np
  4. if __name__ == "__main__":
  5. N = 3000
  6. armadillo_data = o3d.data.ArmadilloMesh()
  7. pcd = o3d.io.read_triangle_mesh(
  8. armadillo_data.path).sample_points_poisson_disk(N)
  9. # Fit to unit cube.
  10. pcd.scale(1 / np.max(pcd.get_max_bound() - pcd.get_min_bound()),
  11. center=pcd.get_center())
  12. pcd.colors = o3d.utility.Vector3dVector(np.random.uniform(0, 1,
  13. size=(N, 3)))
  14. # print('Displaying input point cloud ...')
  15. # o3d.visualization.draw_geometries([pcd])
  16. pcd_for_voxelgrid = copy.deepcopy(pcd)
  17. pcd_for_voxelgrid.translate([1, 0, 0])
  18. print('Displaying voxel grid ...')
  19. voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd_for_voxelgrid,
  20. voxel_size=0.05)
  21. # o3d.visualization.draw_geometries([pcd, voxel_grid])
  22. queries = np.asarray(pcd.points)
  23. output = voxel_grid.check_if_included(o3d.utility.Vector3dVector(queries))
  24. print(output[:10])
  25. queries = np.asarray(pcd_for_voxelgrid.points)
  26. output = voxel_grid.check_if_included(o3d.utility.Vector3dVector(queries))
  27. print(output[:10])
  28. """
  29. 输出结果
  30. Displaying voxel grid ...
  31. [False, False, False, False, False, False, False, False, False, False]
  32. [True, True, True, True, True, True, True, True, True, True]
  33. """

4 Voxel carving

  1. 上述两种方法创建的voxelGrid只在点云或mesh占用该voxel时,才会将该voxel设置为被占用的状态;因此会出现物体的中心在voxelGrid为空洞的情况,只有表面的voxelGrid被占据;但是也可以从多个深度图(depth maps)或者轮廓图(silhouettes)中雕刻出体素网格;在open3d中提供了该实现分别为carve_depth_mapcarve_silhouette

下面已depth map为示例进行展示

  1. # ----------------------------------------------------------------------------
  2. # - Open3D: www.open3d.org -
  3. # ----------------------------------------------------------------------------
  4. # The MIT License (MIT)
  5. #
  6. # Copyright (c) 2018-2021 www.open3d.org
  7. #
  8. # Permission is hereby granted, free of charge, to any person obtaining a copy
  9. # of this software and associated documentation files (the "Software"), to deal
  10. # in the Software without restriction, including without limitation the rights
  11. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. # copies of the Software, and to permit persons to whom the Software is
  13. # furnished to do so, subject to the following conditions:
  14. #
  15. # The above copyright notice and this permission notice shall be included in
  16. # all copies or substantial portions of the Software.
  17. #
  18. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23. # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  24. # IN THE SOFTWARE.
  25. # ----------------------------------------------------------------------------
  26. import open3d as o3d
  27. import numpy as np
  28. def xyz_spherical(xyz):
  29. x = xyz[0]
  30. y = xyz[1]
  31. z = xyz[2]
  32. # 计算得到球体的半径
  33. r = np.sqrt(x * x + y * y + z * z)
  34. # 半径与x轴的夹角
  35. r_x = np.arccos(y / r)
  36. # 半径在y轴的夹角
  37. r_y = np.arctan2(z, x)
  38. return [r, r_x, r_y]
  39. def get_rotation_matrix(r_x, r_y):
  40. rot_x = np.asarray([[1, 0, 0], [0, np.cos(r_x), -np.sin(r_x)],
  41. [0, np.sin(r_x), np.cos(r_x)]])
  42. rot_y = np.asarray([[np.cos(r_y), 0, np.sin(r_y)], [0, 1, 0],
  43. [-np.sin(r_y), 0, np.cos(r_y)]])
  44. return rot_y.dot(rot_x)
  45. def get_extrinsic(xyz):
  46. rvec = xyz_spherical(xyz)
  47. # 计算该相机位姿下的旋转矩阵和平移向量并拼接成T矩阵
  48. r = get_rotation_matrix(rvec[1], rvec[2])
  49. t = np.asarray([0, 0, 2]).transpose()
  50. trans = np.eye(4)
  51. trans[:3, :3] = r
  52. trans[:3, 3] = t
  53. return trans
  54. def preprocess(model):
  55. min_bound = model.get_min_bound()
  56. max_bound = model.get_max_bound()
  57. center = min_bound + (max_bound - min_bound) / 2.0
  58. scale = np.linalg.norm(max_bound - min_bound) / 2.0
  59. vertices = np.asarray(model.vertices)
  60. vertices -= center
  61. model.vertices = o3d.utility.Vector3dVector(vertices / scale)
  62. return model
  63. def voxel_carving(mesh, cubic_size, voxel_resolution, w=300, h=300):
  64. # 计算mesh的顶点法向量
  65. mesh.compute_vertex_normals()
  66. # 创建球体
  67. camera_sphere = o3d.geometry.TriangleMesh.create_sphere(radius=1.0,
  68. resolution=10)
  69. # o3d.visualization.draw_geometries([camera_sphere], mesh_show_back_face=True)
  70. # Setup dense voxel grid.
  71. voxel_carving = o3d.geometry.VoxelGrid.create_dense(
  72. width=cubic_size,
  73. height=cubic_size,
  74. depth=cubic_size,
  75. voxel_size=cubic_size / voxel_resolution,
  76. origin=[-cubic_size / 2.0, -cubic_size / 2.0, -cubic_size / 2.0],
  77. color=[1.0, 0.7, 0.0])
  78. # Rescale geometry.
  79. camera_sphere = preprocess(camera_sphere)
  80. mesh = preprocess(mesh)
  81. # Setup visualizer to render depthmaps.
  82. vis = o3d.visualization.Visualizer()
  83. vis.create_window(width=w, height=h, visible=False)
  84. vis.add_geometry(mesh)
  85. vis.get_render_option().mesh_show_back_face = True
  86. ctr = vis.get_view_control()
  87. param = ctr.convert_to_pinhole_camera_parameters()
  88. # Carve voxel grid.
  89. centers_pts = np.zeros((len(camera_sphere.vertices), 3))
  90. for cid, xyz in enumerate(camera_sphere.vertices):
  91. # Get new camera pose.
  92. trans = get_extrinsic(xyz)
  93. param.extrinsic = trans
  94. c = np.linalg.inv(trans).dot(np.asarray([0, 0, 0, 1]).transpose())
  95. centers_pts[cid, :] = c[:3]
  96. # 转换相机的参数到成open3d中的相机内外参
  97. ctr.convert_from_pinhole_camera_parameters(param)
  98. # Capture depth image and make a point cloud.
  99. vis.poll_events()
  100. vis.update_renderer()
  101. # 根据当前的位姿来进行渲染拍摄得到深度图
  102. depth = vis.capture_depth_float_buffer(False)
  103. # Depth map carving method.
  104. voxel_carving.carve_depth_map(o3d.geometry.Image(depth), param)
  105. print("Carve view %03d/%03d" % (cid + 1, len(camera_sphere.vertices)))
  106. vis.destroy_window()
  107. return voxel_carving
  108. """
  109. 流程:
  110. 1 先创建一个固定大小的稠密(dense)voxleGrid对象
  111. 2 创建一个球形用于虚拟相机的位姿来拍摄拍摄深度图
  112. 3 根据拍摄得到的深度图与相机位姿使用carve_depth_map融合到dense voxelGrid中
  113. """
  114. if __name__ == "__main__":
  115. armadillo_data = o3d.data.ArmadilloMesh()
  116. mesh = o3d.io.read_triangle_mesh(armadillo_data.path)
  117. cubic_size = 2.0
  118. voxel_resolution = 128.0
  119. carved_voxels = voxel_carving(mesh, cubic_size, voxel_resolution)
  120. print("Carved voxels ...")
  121. print(carved_voxels)
  122. o3d.visualization.draw_geometries([carved_voxels])

生成的voxelGird内部也是被填充的,可以自行方法查看


本文转载自: https://blog.csdn.net/qq_41366026/article/details/129636355
版权归原作者 NNNNNathan 所有, 如有侵权,请联系我们删除。

“『OPEN3D』1.6 Voxelization体素化”的评论:

还没有评论