Nodes/Reframe 360/Reframe 360 Camera
ComfyUI Node

Reframe 360 Camera

Point a real camera around inside a 360° image and pull the shot out

By senyilmaz·Created 2 months ago·Updated 2 months ago· 8
Reframe 360 Camera
  • equirectangular
  • image
  • camera_info
sensorFull Frame (36 x 24)
sensor_fitauto
sensor_width_custom36.00
sensor_height_custom24.00
focal_length18.0
lens_projectionrectilinear
yaw0.0
pitch0.0
roll0.0
perspective_warp0.000
aspect_preset16:9
output_width1920
output_height1080
interpolationbicubic
preview_max_width2048

You have a 360° image and you want… a shot - a normal-looking perspective frame at the exact focal length and sensor size a real camera would use. That's this node. It's a virtual camera sitting inside the panorama's sphere - pan, tilt, roll, change the lens in millimetres, pick a fisheye projection - and out comes a proper perspective frame, plus a text readout of exactly what the camera was doing.

Why bother? pytorch360convert has done equirect→perspective for years and does it well, but it gives you no camera to grab and no lens in millimetres - just rectilinear. This is the camera layer. If you're pulling plates out of CC0 HDRIs, matching a real VFX lens, or framing a background before you composite, this is the one you reach for.

How it works

The panorama is mapped to the inside of a unit sphere. Your camera sits inside it and casts rays; wherever a ray hits the sphere is what that pixel sees. Sensor size and focal length pick the ray angle off the optical axis, yaw/pitch/roll orient the camera, and one grid_sample at the end does the actual resample. Since it's a single tensor op, batches come free - feed it video frames and every frame costs the same as a still.

The inputs that actually bite

equirectangular is your IMAGE in (a 2:1 panorama - it warns if the ratio is off). Past that, three groups:

  • Lens. sensor is a stack of real presets, from Full Frame down to Super16 and the iPhone main sensor, plus Custom with sensor_width_custom / sensor_height_custom. Focal length is meaningless without it: 18mm on Super16 is not 18mm on full frame. lens_projection matters when you go wide - rectilinear keeps straight lines straight but its math heads to infinity past ~150° FOV, so below about 12mm on full frame it's fisheye time. Stereographic gives the "little planet" look; equisolid is what most real fisheye lenses are (Blender's default); equidistant is the scientific one.
  • Camera. yaw, pitch, roll pan, tilt and dutch the shot. Positive pitch looks up - the C4D/Blender/Nuke convention, opposite of some tools. perspective_warp slides the camera along its own Z axis as a fraction of sphere radius, but read the tooltip: it's a warp, not a dolly. No parallax, nothing occludes, and straight lines bow. Great on sky, clouds and foliage; terrible on architecture. There's no sphere-diameter control, because only the offset-to-radius ratio changes the image.
  • Output. aspect_preset (16:9, 2.39:1 scope, 9:16 vertical, 4:5 social…) drives output_height from output_width - the height widget hides when a preset is active. sensor_fit decides which sensor dimension survives when the output aspect doesn't match it (same idea as Blender). interpolation defaults to bicubic, which is right.

Outputs

  • image - the perspective frame. Wire it to Save Image, upscale, inpaint, whatever.
  • camera_info - a STRING with focal length, sensor, projection, FOV, effective sensor, orientation, warp, and frame count. That's the "read exact numbers off the sliders" promise, ready for a text display node.

The preview

Drag to pan, shift-drag to roll, wheel to zoom the lens, double-click to reset orientation. It's a WebGL2 fragment shader, a direct GLSL port of the same projection.py maths, so it renders the fisheye projections and the warp honestly. Needs WebGL2; without it the sliders still work, you just lose the live view. One quirk: the preview box height is derived from your aspect ratio - drag the node taller and it snaps back, because any other height would letterbox or stretch. The panorama is cached server-side after the first run and fetched over HTTP, so the preview survives a page refresh; preview_max_width caps how big that cache gets.

Install

Shared with the pack - ComfyUI Manager (search "Reframe 360") or:

cd ComfyUI/custom_nodes
git clone https://github.com/senyilmaz/ComfyUI-Reframe360
pip install -r ComfyUI-Reframe360/requirements.txt

Restart after. Dependencies are just torch, pillow, requests, numpy - no model downloads. (The README's own clone command has a YOURNAME typo; use the repo above.)

Troubleshooting

  • Input isn't 2:1? It warns and renders distorted anyway. Resize or crop first.
  • Straight lines are bowing - that's perspective_warp doing its thing, or rectilinear pushed below ~12mm full frame. Set warp to 0.
  • Preview won't load - old browser or WebGL2 disabled; the sliders still work.
  • A saved graph fails validation with something like roll: could not convert 'rectilinear' to float - a workflow serialized widget values by position. New saves store values by name, and anything impossible is repaired to a default on load.
CategoryReframe360

Inputs (16)

NameTypeDefaultDescription
equirectangularIMAGE
sensorCOMBOFull Frame (36 x 24)11 options: Full Frame (36 x 24), Super35 (24.89 x 18.66), ARRI ALEXA 35 (27.99 x 19.22), ARRI ALEXA LF (36.70 x 25.54), RED KOMODO 6K (27.03 x 14.26), APS-C (23.6 x 15.6), +5
sensor_fitCOMBOauto3 options: auto, horizontal, vertical
sensor_width_customFLOAT36.000.1–200
sensor_height_customFLOAT24.000.1–200
focal_lengthFLOAT18.01–300Millimetres. Meaningless without the sensor size above.
lens_projectionCOMBOrectilinearRectilinear keeps straight lines straight but falls apart below ~12mm on full frame. Use a fisheye projection for ultra-wide.
yawFLOAT0.0-180–180
pitchFLOAT0.0-90–90
rollFLOAT0.0-180–180
perspective_warpFLOAT0.000-0.95–0.95Slides the camera along its OWN Z axis, as a fraction of the sphere radius. Bends straight lines, gives no parallax. A warp, not a dolly.
aspect_presetCOMBO16:98 options: Custom, 16:9, 2.39:1 (scope), 1.85:1 (flat), 4:3, 1:1, +2
output_widthINT192016–16384
output_heightINT108016–16384
interpolationCOMBObicubic3 options: bicubic, bilinear, nearest
preview_max_widthoptINT2048256–8192

Outputs (2)

NameTypeDescription
imageIMAGE
camera_infoSTRING