Nodes/camera-comfyUI/SplitSplatsByMask
ComfyUI Node

SplitSplatsByMask

Split splats by a 2D mask — dynamic stuff out, static world in

By Alexankharin·Created about a year ago·Updated about a month ago· 36
SplitSplatsByMask
  • splats
  • mask
  • camera_matrix
  • inside_splats
  • outside_splats
projection
horizontal_fov90.00
threshold0.50
deviceauto

If you've ever had a splat cloud where a walking person smears across your otherwise-clean scene, this is the node that fixes it. SplitSplatsByMask takes a Gaussian splat cloud and a 2D mask, projects every splat's center into that mask's camera view, and hands you back two clouds: the splats that land inside the mask and the ones outside. It's the dynamic-vs-static split in this pack's video_to_4d_world workflow - the step where the moving stuff gets peeled off the frozen background so each can be treated differently downstream.

The mechanism is refreshingly direct. Each splat's 3D center gets transformed by the camera_matrix input (a world-to-camera matrix; leave it empty and the splats are assumed to already be in the camera frame), projected through your choice of projection - PINHOLE, FISHEYE, or EQUIRECTANGULAR - using horizontal_fov, then compared against the mask. Mask value at that pixel above threshold (default 0.5) means "inside." Splats that end up behind the camera are unceremoniously dumped into the outside pile, which is usually exactly what you want anyway.

The inputs that actually matter:

  • mask - [H,W] or [1,H,W], in the view the split happens in. For the 4D pipeline this comes from MotionMaskFromDepth, where 1.0 means "moving."
  • horizontal_fov - must match the FOV the mask was rendered/estimated at, or the projection lands in the wrong place.
  • threshold - your inside/outside boundary. Crank it toward 1.0 for a stricter, smaller "inside" region.
  • camera_matrix - world-to-camera for the mask's view. Only needed if your splats are in world space and the mask came from a specific camera.

Outputs are inside_splats and outside_splats, both GSPLAT. In the reference 4D workflow, inside_splats (the dynamic stuff) feeds BuildSplats4D, and outside_splats becomes the static world you render and walk around in.

One real trap: the mask, the FOV, and the camera matrix must describe the same view. Grab the FOV from whatever rendered the mask, not from memory - a 60° vs 90° mismatch won't crash anything, it'll just split the cloud in a place that makes no sense. And if the mask resolution is wildly different from what the projection expects, treat it as suspicious; there's no shape check, so you get silent nonsense instead of an error.

Install it with the rest of the pack - ComfyUI Manager, search camera-comfyUI (registry id camera-comfyui), Install, restart. Manager runs the pack's requirements.txt plus install.py for you. If you'd rather do it by hand:

git clone https://github.com/Alexankharin/camera-comfyUI.git custom_nodes/camera-comfyUI
cd custom_nodes/camera-comfyUI && python install.py

The heavier bits (gsplat, the SHARP submodule) only matter for the splat-generating nodes - SplitSplatsByMask itself is pure projection math on tensors, so it loads even when the fancy GPU stuff is missing. That's the pack's design: every heavy dependency is optional and only disables the nodes that need it.

Keep in mind the whole 4D splatting pipeline is research-grade territory - flicker and ghosting are normal on a first pass. This node won't fix a bad mask; it just applies the split honestly. Give it a clean motion mask and it's quietly one of the most satisfying nodes in the pack.

CategoryCamera/GSplat4D

Inputs (7)

NameTypeDefaultDescription
splatsGSPLAT
maskMASKMask [H,W] (or [1,H,W]) in the camera view.
projectionCOMBO3 options: PINHOLE, FISHEYE, EQUIRECTANGULAR
horizontal_fovFLOAT90.001–360
thresholdFLOAT0.500–1
camera_matrixoptMAT_4X4World-to-camera matrix of the mask's view. Default: identity (splats already in camera frame).
deviceoptCOMBOauto3 options: auto, cpu, cuda

Outputs (2)

NameTypeDescription
inside_splatsGSPLAT
outside_splatsGSPLAT