LatLong Extract Projection
Cut a flat camera view out of your 360
- image
- patch
- coverage
- projection_context
The most useful thing about a panorama is that you can pull a normal photograph out of it. Point a virtual camera at any yaw/pitch/roll and what you get is a rectilinear image - the projection every diffusion model, ControlNet and upscaler on your disk actually understands.
That's this node. It's the front half of the crop-and-stitch pattern people know from inpaint crop nodes, except the crop is defined in angles rather than pixels, and the stitch target is a sphere. Extract a view, do whatever you want to it in flat image land - re-render it at higher resolution, inpaint a person out, run a detailer on it - then hand it and its context to LatLong Reinsert Projection to put it back. Because the context is a small metadata record rather than a copy of the panorama, the whole loop is cheap.
A parallel worth noting: the Flux.2 Klein 360 workflow shipped a "take a photo" node that pulls a normal still out of a panorama, because looking around isn't always the deliverable. This is the same move with the projection maths exposed.
How it works
The camera faces world +X at zero angles, with image-right along +Y and image-down along -Z. fov is horizontal, and vertical field of view follows from your width/height ratio with square pixels - so a 1024×1024 extraction at fov 90 is a 90° × 90° window, while 1024×512 at the same FOV is 90° wide and about 53° tall. That square-pixel relationship is what makes the round trip reversible, so don't squash the patch before reinserting.
Internally it builds the perspective sampling maps for your panorama's dimensions once and reuses them across the batch, then samples with bilinear interpolation. The three outputs are the important part:
- patch - the extracted rectilinear IMAGE.
- coverage - a MASK. Note the size: it's shaped like the panorama, not the patch, and 1 marks where the projection lands. The pack's mask convention is 1 = selected/inpaint, but this socket is explicitly named coverage, so read it as "the part of the sphere this patch will overwrite" - useful for seeing how much of a sphere a 60° view actually covers (less than you think), or for masking other panorama-space operations.
- projection_context - a versioned record of yaw, pitch, roll, fov, patch dimensions and source panorama dimensions, plus the coordinate basis as a string. It does not embed the panorama.
Unlike the pack's original transform nodes, this path doesn't clip values on the way through.
Inputs you'll touch
image is the panorama batch. yaw/pitch/roll aim the camera; yaw 0 is longitude 0, positive pitch tilts down (the pack's rotation is right-handed around +Y, so it's the opposite of the "look up" intuition - flip the sign and you're fine, this trips up everyone once). fov (1–179, default 90) is the widest knob: lower numbers give you a tighter, more detailed view. width/height (1–8192 each) set the patch resolution - and since you'll probably upscale that patch later, extract it a bit larger than you think you need rather than upscaling hard.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/cedarconnor/comfyui-LatLong
cd comfyui-LatLong
python -m pip install -r requirements.txt
Manager users can search "ComfyUI LatLong". ComfyUI's Python (embedded on portable builds), Python 3.10+. Dependencies are numpy, opencv-python, scipy, torch and Pillow - no model downloads. Restart; it's under LatLong/Outpaint.
Common issues
The fov and aspect numbers are the trap. Extract at 1024×1024, crop it to 1024×900 while editing, then reinsert, and you get a hard error - the patch aspect has to match the saved projection. That's deliberate, and it's the guardrail that stops you misaligning a sphere.
Extraction is bilinear by design, so a very tight FOV near the pole will show resampling softness. Extract wide and let the model work; the pole of an ERP simply doesn't have the pixels for a crisp 20° window.
And keep the panorama that goes to Reinsert identical to the one you extracted from - same dimensions, same pixels, or at least the same geometry if you regenerated it. The context only knows where the camera was, not what it was looking at.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| yaw | FLOAT | 0.00 | — |
| pitch | FLOAT | 0.00 | — |
| roll | FLOAT | 0.00 | — |
| fov | FLOAT | 90.001–179 | — |
| width | INT | 10241–8192 | — |
| height | INT | 10241–8192 | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| patch | IMAGE | — |
| coverage | MASK | — |
| projection_context | PROJECTION_CONTEXT | — |