Nodes/ComfyUI-SplatKit/Persp to ERP Warp
ComfyUI Node

Persp to ERP Warp

Turn one phone photo into an equirect canvas a pano model can finish

By mickmumpitz·Created about a month ago·Updated about 19 hours ago· 17
Persp to ERP Warp
  • image
  • control_video
  • control_mask
  • inpaint_mask
  • erp_guide
width1440
height720
length5
h_fov_deg90
yaw_deg0
pitch_deg0
mask_feather8
supersample2.0
grid_spacing_deg10
guide_opacity0.55
fill_color#000000

SplatKit starts from a 360° panorama, but most of us don't have one lying around - we have a phone photo. Persp to ERP Warp is the front end that bridges the gap (it's the "Image→pano" stage in workflow 0_generate_360_panorama-upscale.json): it takes a normal perspective photo and warps it onto an equirectangular (ERP) canvas, marking everything outside the photo's footprint as an inpainting mask. You then hand control_video + inpaint_mask to an image-to-pano generator (Krea 2 Turbo, or Qwen-Image-Edit with a 360 LoRA), it invents the rest of the sphere, and you have a panorama to feed the rest of SplatKit. So: this node makes your single 360° input instead of a 360° camera.

The warp itself is area-prefiltered - the source is heavily minified into its ERP footprint, so supersample (default 2) kills aliasing/moire before a bicubic warp, and you'd go 3–4 for very high-res sources. Placement is yaw_deg/pitch_deg (0 = forward/horizon). h_fov_deg matters most: match your camera (phone main ~70, wide ~90, ultrawide ~110–120), since vertical FOV follows from the image aspect. Keep width = 2 × height - the pano LoRA expects a 2:1 canvas.

Inputs and outputs that matter

Required: image, width/height (1440×720 default), length (frames in the still batch - 1 is a single ERP still, 5 gives the video model a little temporal extent), h_fov_deg.

The optional list has two worth knowing:

  • mask_feather (default 8 px) - softens the known/hole boundary so an inpainting sampler blends instead of leaving a hard seam.
  • fill_color (default black) - tints the unknown region on control_video only; the masks are unchanged so inpainting still regenerates it. If a model reacts badly to black init, try #808080 mid-grey.

Outputs: control_video (the ERP canvas), control_mask (where the photo lands, for conditioning), inpaint_mask (a MASK for the unknown region - what an inpainting sampler actually uses), and erp_guide - a technical equirect reference chart (lat/lon grid, degree labels, floor/ceiling perspective curves, and a dotted FOV box showing exactly where your image sits) that you can feed a generator as a pure geometry hint for the 360 layout. Handy when the model keeps drawing walls at the wrong angle.

Install & gotchas

cd ComfyUI/custom_nodes
git clone https://github.com/mickmumpitz/ComfyUI-SplatKit
python_embeded\python.exe -m pip install -r ComfyUI-SplatKit/requirements.txt

Restart, and this node needs no model downloads - the heavy MoGe/SphereSfM stuff only triggers when you reach the actual SplatKit stages. Two traps: a wrong h_fov_deg visibly warps the photo's perspective, and if your source was taken at a significant roll, straighten it before warping (workflow 0 includes a roll-180° seam fix on the output pano side for a reason). If you have no idea what your lens FOV is, run Estimate FOV first and wire its h_fov_deg in - that's exactly the pairing it was built for.

CategorySplatKit

Inputs (12)

NameTypeDefaultDescription
imageIMAGE
widthINT144064–8192ERP canvas width. Keep 2:1 (width = 2*height) for the pano LoRA.
heightINT72032–4096
lengthINT51–257Frames in the still batch (N = 1+4k). 1 = single ERP still (fastest); 5 gives the video model a little temporal extent.
h_fov_degFLOAT9020–170HORIZONTAL field of view of the source photo, in degrees. Vertical FOV follows from the image aspect (square pixels). Match your camera: phone main ~70, wide ~90, ultrawide ~110-120.
yaw_degoptFLOAT0-180–180Horizontal placement of the view center (0 = forward/center col).
pitch_degoptFLOAT0-90–90Vertical placement of the view center (0 = horizon/mid row).
mask_featheroptINT80–128Feather (px) on the inpaint_mask edge. Softens the known/hole boundary so an inpainting sampler blends instead of leaving a hard seam. 0 = hard edge.
supersampleoptFLOAT2.01–4Anti-alias quality. The source is heavily minified into the ERP footprint; this area-prefilters it to footprint x this factor before a bicubic warp, killing aliasing/moire. 2 is a good default, 3-4 for very high-res sources, 1 = off (old bilinear-ish).
grid_spacing_degoptINT105–90Lat/lon grid spacing (deg) on the 'erp_guide' chart. The guide is a TECHNICAL equirect reference (no photo): lat/lon grid + degree labels, floor/ceiling perspective curves, and a dotted FOV box marking exactly where the image sits. Feed it to a generator as a pure geometry hint for the 360 layout.
guide_opacityoptFLOAT0.550–10 = draw only the lat/lon grid + FOV box; >0 = also draw the floor + ceiling perspective curves (how a ground/ceiling plane distorts into equirect).
fill_coloroptSTRING#000000Colour of the UNKNOWN region on the ERP canvas (everything outside the photo's footprint). Hex '#RRGGBB' / '#RGB', or 'r,g,b' with 0-255 components. Default black. This only tints control_video -- the masks are unchanged, so an inpainting sampler still regenerates the region regardless. Handy when a model reacts badly to black init (try '#808080' mid-grey).

Outputs (4)

NameTypeDescription
control_videoIMAGE
control_maskIMAGE
inpaint_maskMASK
erp_guideIMAGE