Repository
ComfyUI-EquirectProjector
ComfyUI custom nodes for 360° equirectangular outpainting and seam inpainting
- Input
- Description
- image
- Source rectilinear IMAGE (or IMAGE batch = video)
- hfov_deg
- Horizontal field-of-view of the source frame, degrees
- equirect_width / equirect_height
- Output canvas size (2:1 recommended)
Specs
- InputDescription
- imageSource rectilinear IMAGE (or IMAGE batch = video)
- hfov_degHorizontal field-of-view of the source frame, degrees
- equirect_width / equirect_heightOutput canvas size (2:1 recommended)
- yaw_deg / pitch_degWhere to place the view on the sphere
- shapepincushion (curved footprint) · inscribed_rect (largest axis-aligned rect fully inside) · bounding_rect (fill bbox, extrapolating corner gaps from nearest projected pixels)
- fill_valueScalar [0,1] for outside-content pixels
- feather_pxSoft-edge the content/mask boundary
- strip_letterbox / letterbox_thresholdAuto-crop black bars before projection
- crop_to_239Crop input to 2.39:1 before projection (default on )
- crop_aligntop / center / bottom — vertical anchor for the 2.39 crop
- InputDescription
- imageEquirect IMAGE
- seam_width_pxWidth of the center stripe, pixels
- fill_r / fill_g / fill_bStripe color [0,1]
- feather_pxSoft edge on the mask
- InputDescription
- inpainted_imageVAE-decoded post-inpaint frame (still in shifted coords)
- clean_shifted_imageOutput 2 of Prep — the clean reference
- seam_maskOutput 3 of Prep
- color_match_modeoff · mean_shift · mean_std · boundary_gradient (default)
- match_band_pxWidth of the sampling strip on each side of the mask (default 16)
- composite_feather_pxExtra feather only at composite boundary (default 8)
README
ComfyUI-EquirectProjector
ComfyUI custom nodes for working with 360° equirectangular (ERP) imagery and
video. Built primarily for the LTX2.3 VR-Outpaint model; may work with
other ERP-aware diffusion / video models too.
- Rectilinear → Equirect — forward gnomonic projection for outpainting a
normal perspective shot into a full 360° panorama. - Equirect Seam Inpaint Prep / Export — rolls the horizontal seam into the
middle of the frame so you can inpaint across it, then rolls it back. - Equirect Seam Inpaint Compose — post-decode cleanup: boundary-anchored
local colour-match + feathered composite back onto the clean shifted base.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Burgstall-labs/ComfyUI-EquirectProjector
cd ComfyUI-EquirectProjector
pip install -r requirements.txt
Restart ComfyUI. Nodes appear under the 360/projection category.
scipy is optional — it's used for a fast Euclidean distance transform in the
bounding_rect fill path of Rectilinear → Equirect. Without it the pack
falls back to a slower pure-PyTorch dilation.
Nodes
Rectilinear → Equirect (360 outpaint prep)
Projects a perspective image / video onto a 2:1 equirectangular canvas at a
chosen (yaw, pitch), producing the distorted / padded ERP image plus an
outpaint mask marking the region the model should generate.
| Input | Description |
|---|---|
image |
Source rectilinear IMAGE (or IMAGE batch = video) |
hfov_deg |
Horizontal field-of-view of the source frame, degrees |
equirect_width / equirect_height |
Output canvas size (2:1 recommended) |
yaw_deg / pitch_deg |
Where to place the view on the sphere |
shape |
pincushion (curved footprint) · inscribed_rect (largest axis-aligned rect fully inside) · bounding_rect (fill bbox, extrapolating corner gaps from nearest projected pixels) |
fill_value |
Scalar [0,1] for outside-content pixels |
feather_px |
Soft-edge the content/mask boundary |
strip_letterbox / letterbox_threshold |
Auto-crop black bars before projection |
crop_to_239 |
Crop input to 2.39:1 before projection (default on) |
crop_align |
top / center / bottom — vertical anchor for the 2.39 crop |
Outputs (equirect_image, outpaint_mask).
Defaults target a 2.39:1 @ 100° hfov rectilinear source (cinemascope
spherical) — which is what the LTX2.3 VR-Outpaint model was trained on.
Feed in any aspect ratio; the node letterbox-strips and center-crops to 2.39:1
automatically. Disable crop_to_239 if you're using a different model that
expects another aspect.
Equirect Seam Inpaint Prep
Shifts an equirect image by 50% of its width so the wrap-around seam
(columns 0 / W-1 in the original) lands in the middle of the frame, then
overlays a solid-color stripe of configurable width. The stripe and its mask
are intended as the inpainting region.
| Input | Description |
|---|---|
image |
Equirect IMAGE |
seam_width_px |
Width of the center stripe, pixels |
fill_r / fill_g / fill_b |
Stripe color [0,1] |
feather_px |
Soft edge on the mask |
Outputs (shifted_image, shifted_clean_image, seam_mask):
shifted_image— the rolled frame with the fill stripe painted over the
seam. Use this as the input to your VAE-encode + inpaint branch.shifted_clean_image— the rolled frame without the stripe (pure
translation, same pixels as the input just roll-shifted). Use this as the
clean base inEquirect Seam Inpaint Composeor stockImageCompositeMasked
to avoid the VAE round-trip polluting non-masked pixels.seam_mask— binary / feathered stripe mask.
Equirect Seam Inpaint Export
Rolls the pixels back by -W/2, putting the original middle back in the
middle and the seam back at the edges. Pair with Prep at the start and
Export at the end of the seam-inpainting branch.
Input: IMAGE. Output: IMAGE.
Equirect Seam Inpaint Compose (color-match + composite)
Post-decode cleanup node: pastes the inpainted stripe into the clean shifted
base, after optionally colour-matching it to the boundary pixels on each
side of the mask. Fixes two common inference artefacts:
- VAE encode → sample → decode introduces tiny colour drift in unmasked
regions; this node replaces the unmasked region with the original pixels
bit-exact. - The inpaint model sometimes outputs a stripe that is a touch brighter /
darker / colour-cast than its neighbours. Local strip-sampled colour match
corrects that without pulling global histograms around.
| Input | Description |
|---|---|
inpainted_image |
VAE-decoded post-inpaint frame (still in shifted coords) |
clean_shifted_image |
Output 2 of Prep — the clean reference |
seam_mask |
Output 3 of Prep |
color_match_mode |
off · mean_shift · mean_std · boundary_gradient (default) |
match_band_px |
Width of the sampling strip on each side of the mask (default 16) |
composite_feather_px |
Extra feather only at composite boundary (default 8) |
Modes:
mean_shift— uniform per-channel offset so the inpaint's boundary mean
matches the neighbour mean. Safest, never distorts texture.mean_std— also scales per-channel std (Reinhard-style).boundary_gradient— measures the left and right boundary offsets
separately and interpolates a smooth per-column correction across the
stripe. Best when the two sides of the frame have different colour casts
(e.g. sun on one side, shade on the other).
Output: IMAGE.
Seam-inpainting workflow
image ─ Prep ─── shifted_image ────── VAEEncode + Inpaint + VAEDecode ──┐
├── shifted_clean_image ─────────────────────────────────────── Compose ─ Export ─ final
└── seam_mask ──────────────────────────────────────────────────┘
Prep → Export with nothing in between is an identity — a visible change
only appears when the inpaint branch actually modifies the stripe region.
The pair is also useful for building paired training data for ERP seam-repair
LoRAs / IC-LoRAs: prep gives the "seam in the middle" input, and the clean
equirect source is the target.
Notes
- All nodes operate on ComfyUI's standard
(B, H, W, C)IMAGE tensors,
floatin[0, 1], and preserve batch dimension (works on video IMAGE
batches). - For the seam nodes, image width doesn't need to be even — the export uses
the negative-shift inverse so round-trip is exact for anyW. Rectilinear → EquirectusesF.grid_samplewith bilinear filtering and
runs on the same device as the input tensor.
License
MIT.