Auto: Upright
Make the Building Stand Up Straight — or Leave the Photo Alone
- image
- image
- confidence
- matrix
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 = nonedoes that. Usevalid. - 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.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | Photo to straighten. | |
| mode | COMBO | auto | auto = 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. |
| strength | FLOAT | 1.000–1 | Fraction of the correction. Around 0.7 keeps a little natural perspective in tall buildings. |
| crop | COMBO | none | none = keep everything, corners may be black. valid = zoom in until no empty corners remain. |
| max_angleopt | FLOAT | 25.00–45 | Largest camera turn in degrees; bigger corrections stop there. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | The straightened image (same size). |
| confidence | FLOAT | How sure the correction is, 0..1 (lowest of a batch). 0 = image left unchanged. |
| matrix | STRING | JSON list of the 3x3 pixel homographies applied, one per image. |