TRELLIS.2 Sparse Structure Cleanup ⚡
Filling the Holes TRELLIS.2 Leaves in Its Own Voxels
- voxel
- voxel
The sparse structure stage gives you an occupancy grid, and occupancy grids come out of flow models with problems: pinholes through a wall, a stray island floating beside the leg, a shell so thick the shape stage has to chew through voxels that will never be surface. The owner's sidecar runs a cleanup pass right after VAE Decode Structure and before the shape cascade. ComfyUI core doesn't, because core's Trellis2ShapeStage takes the decoded occupancy as-is. This node is that missing pass.
The mechanism, honestly
Input is a VOXEL - the output of Trellis2 VAE Decode Structure. The node thresholds it at 0.5 into a boolean grid, counts how many enclosed holes there are (connected-component labelling of the inverse, minus anything touching the border), then applies one of three fills:
remove_small_holes(default) - the 2D-style operation applied slice by slice, area thresholdhole_structure². Cheap, conservative, and what the sidecar actually runs.morphological_closing- binary closing with ahole_structure-cubed kernel,hole_iterationspasses. Seals gaps and thin cracks rather than just enclosed voids. Aggressive; it fattens the shape slightly.flood_fill- closing, then fill enclosed holes, then keep only the largest connected component. This is the nuclear option, and it's the one that removes stray floating blobs.
After filling, keep_only_shell runs two 3×3×3 erosions and drops everything that survives - i.e. voxels two or more layers inside the surface. Thin parts stay. What's left is a shell, which is what the shape stage is supposed to be upsamping into detail rather than volume.
The console logs how many holes were filled and how many remain per batch item, which is the only feedback you get that the pass did anything. Worth watching the first time you run it.
The knobs
fill_holes- on by default, matching the sidecar's TRELLIS.2 runs. Off passes the voxel through completely unchanged - and note that meanskeep_only_shellgets skipped too, since the early return happens before shell extraction. If you want the shell trim without hole filling, you don't get it.hole_structure- 1 to 9. Area threshold forremove_small_holes(structure squared) or kernel size for closing. Upstream runs 1, which is the most conservative setting possible; raise it if you're seeing persistent pinholes.hole_iterations- 1 to 8. Only meaningful for the two morphological algorithms.keep_only_shell- on by default. Off keeps the filled solid, which is slower downstream and rarely what you want.
Output is a single voxel, same type in and out, so it drops in inline with no rewiring beyond inserting it. voxel in, voxel out, then on to the shape stage.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/ChrisColeTech/ComfyUI-GGUF-Loader
pip install --upgrade gguf
Or Manager → "ComfyUI-GGUF-Loader" → restart. The dependencies here are scipy.ndimage and, for remove_small_holes specifically, skimage.morphology - scikit-image is in the pack's requirements.txt, so a requirements-based install covers it. If you installed with just pip install --upgrade gguf, scikit-image may be missing and this node will fail on import of the algorithm rather than at registration:
pip install scikit-image
Where it helps, and where it doesn't
It helps with the classic failure of the sparse stage: a structure that's mostly right but porous. Holes become infill at the shape stage, and infill becomes lumps and blisters in the final mesh. Filling first, then cascading, gives the shape model a cleaner prior to work from.
It hurts when the structure is thin and correct. morphological_closing with a big kernel will close two fingers together into a mitten. flood_fill will delete a legitimately separate component - a handle modelled slightly detached, an accessory beside the body. If you're getting lost parts, try remove_small_holes with hole_structure at 1 and keep_only_shell on, which is exactly the sidecar's default and the safest thing to do.
It won't fix a bad mask. If the subject separation was wrong, the structure will be wrong in ways no voxel morphology repairs. Fix the matte.
One quirk worth knowing: the algorithm throws on an unknown hole_fill_algorithm string, but everything else about this node is permissive - 2D operations applied slice by slice, which on an oblique surface can leave structure that a true 3D fill wouldn't. It's the sidecar's behaviour, faithfully reproduced, and the sidecar's output is good enough that the fidelity is the point.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| voxel | VOXEL | Trellis2 VAE Decode Structure output. | |
| fill_holes | BOOLEAN | true | The sidecar's TRELLIS.2 runs: on. Off passes the voxel through unchanged. |
| hole_fill_algorithm | COMBO | remove_small_holes | 3 options: remove_small_holes, morphological_closing, flood_fill |
| hole_structure | INT | 11–9 | Hole area (remove_small_holes: structure^2) / closing kernel size. |
| hole_iterations | INT | 11–8 | — |
| keep_only_shell | BOOLEAN | true | Drop voxels >= 2 layers inside the surface (applies with fill_holes, as in the sidecar). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| voxel | VOXEL | — |