ComfyUI Node

Auto: Upright

Make the Building Stand Up Straight — or Leave the Photo Alone

By jetthuangai·Created 6 months ago·Updated a day ago· 6
Auto: Upright
  • image
  • image
  • confidence
  • matrix
◄modeauto►
◄strength1.00►
◄cropnone►
◄max_angle25.0►

Tilt the camera up at a building and the verticals lean toward each other like they're in a hurry to meet. Every phone camera hides this behind a crop; every real-estate photo lives or dies by it. Auto: Upright (JHPixelProAutoUpright) does what Lightroom's Upright panel does - measure the straight lines in the photo, level the horizon or make the verticals parallel - and, crucially, it refuses to guess when the evidence isn't there.

That refusal is the design. Portraits, trees, cloudscapes, night shots: no reliable long straight lines, so the node returns your image untouched with confidence 0. A wrong perspective correction is much more offensive than a crooked horizon, and this node knows it.

The mechanism, briefly

Line segments come out of OpenCV's LSD on a grey proxy capped at 1024 px on the long side.

For level, the roll angle is a length-weighted median deviation of the near-horizontal and near-vertical segments, and the image is rotated about its centre. That's it - a rotation.

For vertical and full, it goes further: vanishing points are found by RANSAC over pairs of segments, then refined as the smallest eigenvector of a weighted sum of outer products of the lines. The camera is then virtually rotated so the vertical vanishing point goes to infinity - i.e. verticals become parallel - with the warp H = K·R·K⁻¹, focal length derived from the two vanishing points when they're orthogonal, else assumed to be about a 36 mm lens. full does the same to the main horizontal vanishing point, which is the "facade seen straight on" correction.

The safety gate before any of that: level needs at least 3 lines with enough total length and 60% of that length in agreement; a vanishing point needs at least 5 lines. Lines leaning more than 15° aren't treated as walls. Fail any of it and you get the original image and confidence 0.

Inputs and outputs

image, then mode: auto (parallel verticals when buildings are detected, else a level horizon), level (rotation only), vertical, full.

strength (0–1) is the fraction of correction applied. Around 0.7 is my default for tall buildings - a fully corrected look-up shot of a skyscraper looks wrong, like a CAD elevation, because the eye expects a little convergence.

crop (none / valid) decides what happens to the black corners. none keeps everything and leaves empty corners; valid zooms in until no empty corner remains. For final output, valid.

max_angle (0–45°, default 25) caps how steep a correction it will make - a very steep look-up shot gets straightened part of the way rather than stretched into plastic.

Outputs: image (same size), confidence (0–1, lowest of the batch; 0 = unchanged), and matrix (JSON list of the 3×3 pixel homographies, one per image).

Wire confidence into a text preview

This is the node's whole API for trusting it beyond first glance. Feed it to a Show Text / text preview node and leave it on screen while you batch. 0 means nothing happened; 0.3 means it found enough to act on something marginal. Once you've seen the values for your kind of photo, you'll know what threshold to eyeball before you ship a batch.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/jetthuangai/ComfyUI-JH-PixelPro.git
cd ComfyUI-JH-PixelPro
pip install -r requirements.txt   # the Python that runs ComfyUI

Or ComfyUI Manager → search ComfyUI-JH-PixelPro → Install → restart. Under Add Node → ComfyUI-JH-PixelPro → geometry. Deps: kornia, mediapipe, opencv-python-headless, scipy - all in requirements.txt, PyTorch from ComfyUI. It's one of the cheapest nodes in the pack: 0.1–0.3 s per image, under 0.3 GB VRAM, and the geometry itself runs in float64 numpy with the warp on torch/Kornia.

Where people get burned

  • No correction at all. Usually too few long lines - or the verticals lean more than 15°, at which point they're not treated as walls. Try mode = level, or fix the tilt by hand.
  • Curved lines near the frame edge. That's barrel distortion, a different problem. Fix it with Lens Distortion first, then straighten, or Upright will be trying to fit vanishing points to curves.
  • Black corners in the output. crop = none does that. Use valid.
  • Run it before colour work. Auto Tone measures the final framing, so straighten first, then tone, then grade.

One connection worth making: if the horizon is level but the building still leans, level won't help - that's perspective, so reach for vertical. And if you're fixing a whole hand-held series to the same framing, Auto Align is the node you want.

The pack is Apache-2.0, maintained by JH (@jetthuangai), at 1.10.0 with 64 nodes and docs in English and Vietnamese. It's almost entirely un-discussed on Reddit - a shame for a geometry node this well-behaved, because "it silently does nothing when it isn't sure" is exactly the property nobody blogs about and everybody wants.

CategoryComfyUI-JH-PixelPro/geometry

Inputs (5)

NameTypeDefaultDescription
imageIMAGEPhoto to straighten.
modeCOMBOautoauto = parallel verticals when the photo has buildings, else a level horizon. level = rotate only. vertical = parallel verticals. full = verticals and the main horizontal lines too.
strengthFLOAT1.000–1Fraction of the correction. Around 0.7 keeps a little natural perspective in tall buildings.
cropCOMBOnonenone = keep everything, corners may be black. valid = zoom in until no empty corners remain.
max_angleoptFLOAT25.00–45Largest camera turn in degrees; bigger corrections stop there.

Outputs (3)

NameTypeDescription
imageIMAGEThe straightened image (same size).
confidenceFLOATHow sure the correction is, 0..1 (lowest of a batch). 0 = image left unchanged.
matrixSTRINGJSON list of the 3x3 pixel homographies applied, one per image.