Working with Occupancy Grids

While isoext is designed for signed distance fields (SDFs), you can also extract surfaces from binary occupancy grids—voxel grids where each cell is either occupied (1) or empty (0).

This is useful when:

  • You have voxelized data from 3D scanning or segmentation

  • Your data comes from a boolean CSG operation

  • You want to convert a mesh to voxels and back

import torch
import isoext
from isoext.sdf import SphereSDF
from _viz import show_mesh
Running cmake --build & --install in /home/gcnick/Documents/code/isoext/build/cp312-abi3-linux_x86_64

Creating an Occupancy Grid

Let’s create a binary occupancy grid from a sphere SDF. Values > 0 are outside, so we threshold to get a binary mask:

# Create a grid and compute sphere SDF
grid = isoext.UniformGrid([64, 64, 64])
sdf_values = SphereSDF(radius=0.7)(grid.get_points())

# Convert to binary occupancy: 0 = outside, 1 = inside
occupancy = (sdf_values < 0.0).float()

print(f"Occupancy values: {occupancy.unique().tolist()}")
Occupancy values: [0.0, 1.0]

Direct Extraction (Jagged Result)

You can run marching cubes directly on the binary grid using level=0.5 (the boundary between 0 and 1). However, the result will be jagged because there’s no gradient information—every surface point lands exactly on a voxel face:

grid.set_values(occupancy)
v_jagged, f_jagged = isoext.marching_cubes(grid, level=0.5)

print(f"Jagged mesh: {v_jagged.shape[0]} vertices, {f_jagged.shape[0]} faces")
show_mesh(v_jagged, f_jagged, smooth_shading=False)
Jagged mesh: 9168 vertices, 18332 faces
_images/bd24f9f9c698cc13aff5c45d446b57d21ac84f60ae9c7e9ce531f2984c6cb6af.png

Smoothed Extraction

To get a smoother result, apply Gaussian smoothing before extraction. This blurs the sharp 0/1 transitions into gradual gradients, allowing marching cubes to interpolate vertex positions:

# Smooth the occupancy grid
smoothed = isoext.gaussian_smooth(occupancy, sigma=5.0)
grid.set_values(smoothed)
v_smooth, f_smooth = isoext.marching_cubes(grid, level=0.5)

print(f"Smooth mesh: {v_smooth.shape[0]} vertices, {f_smooth.shape[0]} faces")
show_mesh(v_smooth, f_smooth)
Smooth mesh: 8232 vertices, 16460 faces
_images/f3d331ae5475c5a003bf8768cfcd015bad7368905e0158ced8dfbdbd29450ea2.png