Nodes/camera-comfyUI/VideoToFusedSplats
ComfyUI Node

VideoToFusedSplats

Fuse a whole video into one world-frame gaussian splat cloud

By Alexankharin·Created about a year ago·Updated about a month ago· 36
VideoToFusedSplats
  • frames
  • trajectory
  • static_mask
  • depths
  • splats
horizontal_fov60.00
checkpoint<download default>
keyframe_stride8
stitch_voxel_size0.010
stitch_modesmart
deviceauto

VideoToFusedSplats is where a video stops being frames and becomes a place. It runs SHARP - Apple's single-image-to-gaussian-splat model - on keyframes of your clip, transforms each keyframe's splat cloud from its own camera frame into a shared world frame, and fuses them into one GSPLAT you can render from any camera. This is the "static world" half of the pack's video_to_4d_world workflow: the frozen scene you can walk around in, distinct from the dynamic objects handled separately.

The mechanism, in order: pick keyframes (keyframe_stride, default 8 - SHARP runs on every Nth frame), lift each one to splats, then optionally do two cleanups before fusing. If you feed depths (metric depth, [T,H,W]), each keyframe's splats get scale-aligned to it via a robust median disparity ratio - this is what makes the cloud metric and prevents per-keyframe drift. If you feed a static_mask ([T,H,W], 1 = static/keep), any splat whose source pixel is below 0.5 gets dropped - that's how moving people and cars get scrubbed out of the world. Then each cloud is rotated into world space by the inverse of its camera pose, and all of it is fused with a voxel-style reduction controlled by stitch_voxel_size and stitch_mode.

The inputs that matter:

  • frames - [T,H,W,3].
  • trajectory - world-to-camera poses [T,4,4] (a single [4,4] broadcasts). Feed it VideoPoseEstimator's trajectory output directly.
  • horizontal_fov - default 60; use the FOV from VideoPoseEstimator, not the default, or the fusion won't line up.
  • checkpoint - the SHARP .pt. It can download the default model for you; pick a local checkpoint from your input folder if you have one.
  • keyframe_stride - 8 is a sane default. Lower = more keyframes = slower but denser; raise it if a pass takes forever.
  • stitch_voxel_size / stitch_mode - fusion controls. smart is the default and usually the right call; average, discard, and keep are there when smart is smearing.

Optional depths, static_mask, and device round it out. One output: splats (GSPLAT), which wires straight into RenderSplat, SavePlySplat, SplatPolish, or SplitSplatsByMask.

Gotchas, and these are the ones that actually bite:

  • Skip the double ray-depth correction. Feed it VideoPoseEstimator's already-ray-corrected depth, or you'll bow the geometry at wide FOVs.
  • Dynamic pixels will ghost without a static_mask. A moving person fused into the world becomes a smear across all keyframes. Run MotionMaskFromDepth and pass its output in.
  • Wrong FOV = splats in the wrong place. This node trusts your horizontal_fov; a mismatched one fuses clouds that don't overlap, and the result looks like a broken hologram.
  • SHARP and gsplat are required. The node errors if the SHARP submodule isn't available. install.py (run automatically by ComfyUI Manager) initializes the submodules/ml-sharpt checkout and pip-installs gsplat, whose CUDA kernels JIT-compile on first use - expect a long first run.

Install the pack via ComfyUI Manager (search camera-comfyUI, Install, restart), or:

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

Expect a GPU with real VRAM to be happy (8 GB works at reduced settings; more is better), and expect this node to be the slow one in the graph. But when it works - and it does work - you get a world-frame splat scene from a phone video, and that's the whole reason this pack exists.

CategoryCamera/GSplat

Inputs (10)

NameTypeDefaultDescription
framesIMAGEVideo frames [T,H,W,3].
trajectoryTENSOR[T,4,4] world-to-camera matrix per frame (a single [4,4] is broadcast).
horizontal_fovFLOAT60.001–179
checkpointCOMBO<download default>SHARP .pt checkpoint from the input folder, or download the default model.
keyframe_strideINT81–1000Run SHARP on every Nth frame.
stitch_voxel_sizeFLOAT0.0100–10
stitch_modeCOMBOsmart4 options: smart, average, discard, keep
static_maskoptMASK[T,H,W], 1 = static/keep pixel. Splats whose source pixel has mask < 0.5 are dropped.
depthsoptTENSOR[T,H,W] metric depths. SHARP splats are scale-aligned per keyframe via a robust median disparity ratio.
deviceoptCOMBOauto3 options: auto, cpu, cuda

Outputs (1)

NameTypeDescription
splatsGSPLAT