如何用Open3D实现实心体素化并将.obj转为NumPy数组?
解决方案:基于Open3D CUDA加速的实心体素化
问题分析
你当前的代码仅生成了网格表面的空心体素,因为VoxelGrid.create_from_triangle_mesh只会标记与网格三角面相交的体素。要实现实心体素填充,需要批量判断每个体素是否处于网格内部,同时利用Open3D的CUDA加速能力避免循环操作。
前置准备
确保安装带CUDA支持的Open3D版本:
pip install open3d-cuda
完整代码实现
import open3d as o3d import numpy as np # 1. 加载并预处理网格(确保闭合,避免填充错误) mesh = o3d.io.read_triangle_mesh('path/to/file.obj') mesh.compute_vertex_normals() # 修复网格缺陷(可选但推荐) if not mesh.is_closed(): mesh = mesh.remove_duplicated_vertices() mesh = mesh.remove_degenerate_triangles() mesh = mesh.fill_holes() # 2. 定义体素参数与网格边界 voxel_size = 0.01 bbox = mesh.get_axis_aligned_bounding_box() min_bound, max_bound = bbox.min_bound, bbox.max_bound # 计算体素网格尺寸 grid_size = ((max_bound - min_bound) / voxel_size).astype(int) + 1 # 生成所有体素的中心坐标(批量生成,无循环) x = np.linspace(min_bound[0] + voxel_size/2, max_bound[0] - voxel_size/2, grid_size[0]) y = np.linspace(min_bound[1] + voxel_size/2, max_bound[1] - voxel_size/2, grid_size[1]) z = np.linspace(min_bound[2] + voxel_size/2, max_bound[2] - voxel_size/2, grid_size[2]) xx, yy, zz = np.meshgrid(x, y, z, indexing='ij') voxel_centers = np.stack([xx.ravel(), yy.ravel(), zz.ravel()], axis=1) # 3. CUDA加速的内部点判断(射线投射法) # 创建CUDA射线投射场景 scene = o3d.t.geometry.RaycastingScene() mesh_tensor = o3d.t.geometry.TriangleMesh.from_legacy(mesh) scene.add_triangles(mesh_tensor) # 生成批量射线:体素中心为起点,沿X轴正方向发射 rays = np.concatenate([voxel_centers, np.tile([1.0, 0.0, 0.0], (voxel_centers.shape[0], 1))], axis=1) rays_tensor = o3d.core.Tensor(rays, dtype=o3d.core.Dtype.Float32) # 执行射线投射,统计交点数量 cast_result = scene.cast_rays(rays_tensor) hit_counts = cast_result['primitive_ids'].shape[1] - np.sum(cast_result['primitive_ids'] == -1, axis=1) is_inside = hit_counts % 2 == 1 # 奇偶规则判断内部点 # 4. 生成实心体素数组并保存 voxel_array = np.zeros(grid_size, dtype=np.uint16) voxel_array[xx.ravel(), yy.ravel(), zz.ravel()] = is_inside.astype(np.uint16) * 1 # 保存为.npy np.save('path/to/file_solid.npy', voxel_array) # 可选:保存为nii.gz(医学CT格式) import nibabel as nib # 调整坐标系以匹配医学图像规范(按需修改) voxel_nii = np.transpose(voxel_array, (2, 1, 0)) affine = np.diag([voxel_size, voxel_size, voxel_size, 1]) nib.save(nib.Nifti1Image(voxel_nii, affine), 'path/to/file_solid.nii.gz')
关键说明
- CUDA加速核心:使用
o3d.t.geometry.RaycastingScene实现批量射线投射,所有计算在GPU上完成,避免Python循环的性能瓶颈。 - 内部点判断逻辑:利用射线与网格交点数量的奇偶性(奇数表示点在封闭网格内部),这是计算机图形学中判断点是否在多边形/网格内部的经典方法。
- 网格预处理:修复网格的孔洞、重复顶点等缺陷,确保内部填充的准确性。
内容的提问来源于stack exchange,提问作者adamW
相关产品推荐
相关产品推荐

