Skip to content

Commit 5487fc8

Browse files
m9hclaude
andcommitted
Switch BM7 rasterization from containment to voxelization
trimesh.contains() with 175K triangles × 1.3M points was timing out (>3600s). Now uses trimesh.voxelized().fill() which is O(n_triangles) and completes in ~15s. Vectorized numpy index mapping instead of loops. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d129ec6 commit 5487fc8

1 file changed

Lines changed: 32 additions & 12 deletions

File tree

benchmarks/itrusst_bm7.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,40 @@ def rasterize_skull(outer_mesh, inner_mesh, dx_mm: float = 1.0,
107107
# Rasterize using trimesh containment checks
108108
log.info("Rasterizing skull to %s grid (dx=%.1f mm)...", shape, dx_mm)
109109

110-
# Build query points (subsample if too large)
111-
xx, yy, zz = np.meshgrid(*[v for _, _, v in axes], indexing="ij")
112-
points = np.column_stack([xx.ravel(), yy.ravel(), zz.ravel()])
113-
114-
log.info(" Checking %d points against outer mesh...", len(points))
115-
inside_outer = outer_mesh.contains(points)
116-
log.info(" Checking %d points against inner mesh...", len(points))
117-
inside_inner = inner_mesh.contains(points)
110+
# Use trimesh voxelization — much faster than per-point containment
111+
pitch = dx_mm
112+
log.info(" Voxelizing outer mesh (pitch=%.1f mm)...", pitch)
113+
outer_filled = outer_mesh.voxelized(pitch).fill()
114+
log.info(" Voxelizing inner mesh...")
115+
inner_filled = inner_mesh.voxelized(pitch).fill()
118116

119117
labels = np.zeros(shape, dtype=np.int32)
120-
labels_flat = labels.ravel()
121-
labels_flat[inside_outer & ~inside_inner] = 2 # skull
122-
labels_flat[inside_inner] = 4 # brain
123-
labels = labels_flat.reshape(shape)
118+
119+
def _map_voxels_to_grid(voxel_grid, grid_mins, grid_shape, dx):
120+
"""Map trimesh VoxelGrid boolean matrix to our coordinate grid."""
121+
matrix = voxel_grid.matrix
122+
origin = voxel_grid.transform[:3, 3]
123+
# Voxel indices where the mesh is filled
124+
filled_ijk = np.argwhere(matrix) # (N, 3)
125+
# Convert to world coordinates
126+
world = origin + filled_ijk * dx
127+
# Convert to our grid indices
128+
grid_ijk = np.round((world - grid_mins) / dx).astype(int)
129+
# Clip to valid range
130+
valid = np.all((grid_ijk >= 0) & (grid_ijk < grid_shape), axis=1)
131+
return grid_ijk[valid]
132+
133+
inner_idx = _map_voxels_to_grid(inner_filled, mins, shape, dx_mm)
134+
outer_idx = _map_voxels_to_grid(outer_filled, mins, shape, dx_mm)
135+
136+
# Brain = inside inner mesh
137+
if len(inner_idx) > 0:
138+
labels[inner_idx[:, 0], inner_idx[:, 1], inner_idx[:, 2]] = 4
139+
# Skull = inside outer but not inside inner
140+
if len(outer_idx) > 0:
141+
outer_mask = labels[outer_idx[:, 0], outer_idx[:, 1], outer_idx[:, 2]] != 4
142+
skull_idx = outer_idx[outer_mask]
143+
labels[skull_idx[:, 0], skull_idx[:, 1], skull_idx[:, 2]] = 2
124144

125145
n_water = np.sum(labels == 0)
126146
n_skull = np.sum(labels == 2)

0 commit comments

Comments
 (0)