Files
rectify/README.md
Andy Kopra 5e3a43d6ad docs: deliver the user guide as a formatted PDF
The guide now ships as `doc/Rectify_user_guide.pdf` alongside the Markdown
it is rendered from. The PDF is the one to read; the Markdown remains the
source and is authoritative if the two ever disagree.

It is committed rather than built here because rendering it needs a font
store and a headless browser, which no one should have to install in order
to read a guide. The same PDF is downloadable on its own from
https://andykopra.com/Rectify_user_guide.pdf — the macOS disk image holds
the application only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 01:06:35 +02:00

21 KiB
Raw Permalink Blame History

Rectify

Perspective correction for paintings and rectangular objects. Takes a photo of a painting on a wall (taken at an angle) and produces a head-on, undistorted rectangular image.

Quick start

# From source (Linux/macOS)
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python -m rectify --gui photo.jpg

On macOS there is also a ready-to-run signed app — see Installation methods.

Documentation

The full user and programmer's guide ships in two forms, both in doc/:

The Markdown is the source and is authoritative. If the two ever disagree, the Markdown is right and the PDF is behind it. The PDF is committed to the repository rather than built here because rendering it needs a font store and a headless browser, which no one should have to install in order to read a guide.

Setup from source

The repository is public — no account, login, or permission is needed to clone it. It can also be browsed, and downloaded as a ZIP, at https://git.andykopra.com/ack/rectify/.

git clone https://git.andykopra.com/ack/rectify.git
cd rectify
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

The GUI is the primary interface on every platform; the CLI drives the same engine without a window, for scripting and batch work.

Platform notes:

Run from source on Linux

Linux is the platform Rectify is developed on, so it is the best-tested of the three. There is no packaged download the way macOS has its .dmg — you run it from source. Development happens on Ubuntu with GNOME; any current distribution with Python 3.10+, git, and a graphical desktop works, and only the apt command lines below are Ubuntu-specific.

Start with the public clone in Setup from source above — git clone https://git.andykopra.com/ack/rectify.git, no account needed.

Debian/Ubuntu need one package before the venv step — without it, python3 -m venv fails with ensurepip is not available:

sudo apt install python3-venv python3-pip

Then follow Setup from source above and launch:

source .venv/bin/activate
python -m rectify --gui photo.jpg

If something goes wrong:

  • PySide6 fails to start — install the X11/XCB libraries Qt needs: sudo apt install libxcb-xinerama0 libxcb-cursor0
  • Window sizing, cursors, or tooltips look wrong (or the app won't start) in a Wayland session — force the X11 path with QT_QPA_PLATFORM=xcb python -m rectify --gui. Worth trying first for any display oddity, especially on NVIDIA drivers
  • HEIC files show a generic icon in the Open dialog — thumbnails come from your desktop, not from Rectify (which reads them fine either way). Run scripts/install_heic_thumbnailer.sh once; it self-skips if your system already handles HEIC

Launching from anywhere. The venv's own interpreter can run Rectify directly, so a one-line wrapper (saved as ~/bin/rectify, chmod +x) avoids activating the venv each time and keeps relative filenames working:

#!/bin/sh
exec env PYTHONPATH="$HOME/rectify" "$HOME/rectify/.venv/bin/python" -m rectify "$@"

For a clickable icon, point a ~/.local/share/applications/rectify.desktop entry at that wrapper with Exec=/home/YOUR_USER/bin/rectify --gui %f. Per-user settings are written to ~/.local/share/<username>/Rectify/settings.json.

Run from source on Windows

Every dependency ships a Windows wheel — PySide6 bundles Qt and pillow-heif bundles libheif — so pip install is the whole build step. No compiler and no system libraries are required, and the Open dialog is the standard Explorer dialog, with image thumbnails.

Prerequisites: Python 3.103.13 from python.org — tick "Add python.exe to PATH" in the installer. git is optional; the public repository can be downloaded as a ZIP from https://git.andykopra.com/ack/rectify/ instead. Either way, no account or permission is needed.

PowerShell:

git clone https://git.andykopra.com/ack/rectify.git
cd rectify
py -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python -m rectify --gui

If PowerShell refuses to run the activation script, allow it for that window only:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

Command Prompt is identical except for the activation line:

.venv\Scripts\activate.bat

Usage is the same as on the other platforms — see Usage below, and python -m rectify --help for the CLI flags. Per-user settings are written to %APPDATA%\<username>\Rectify\settings.json.

If something goes wrong:

  • ImportError: DLL load failed while importing QtCore — install the Microsoft Visual C++ 20152022 Redistributable (x64), which Qt needs. It is already present on most Windows installations.
  • py is not recognized — the Python launcher was not installed; use python -m venv .venv instead, or re-run the installer with "Add python.exe to PATH" ticked.
  • Blurry or oversized UI on a scaled display — Rectify scales its own fonts; report the display scaling percentage along with a screenshot.

Reporting back on a Windows trial. Windows is not yet a regularly tested platform, so the following are the points most likely to differ. Please note how each behaves:

  1. Opening a HEIC/HEIF photo from an iPhone.
  2. Display scaling at 125% and 150% — menu, panel, and control sizing.
  3. The keyboard shortcuts — press Shift-Alt for the overlay. On Windows, Ctrl and Alt stand in for the ⌘ and ⌥ used in the shortcut table below.
  4. Saving to a folder whose path contains spaces.
  5. Building an executable with pyinstaller rectify.spec (see Building a standalone executable) and launching the resulting dist\rectify.exe.

Usage

GUI (primary interface)

python -m rectify --gui                    # launch empty, open file later
python -m rectify --gui photo.jpg          # launch with an image
python -m rectify --gui /path/to/photos/   # launch with file dialog in that directory

The GUI provides:

  • Input formats — JPEG, PNG, TIFF, BMP, WebP, and HEIC/HEIF (iPhone photos). HEICs are decoded to their SDR base image and converted from Display P3 to sRGB, so colors are correct; any HDR gain map is ignored (the right behavior for SDR documentation). Output is saved in the standard formats below
  • Two-panel view — original image with detected quad (left), rectified result (right)
  • Zoom and pan — mouse wheel to zoom (anchors under cursor), right-click to reset zoom, drag outside the quad to pan when zoomed in
  • Automatic detection — opens the image, picks the best strategy (grayscale or saturation) and sensitivity by sweeping all combinations, and presents the result. No knobs to set in the GUI; for fine-tuning the detected quad, use the manual editing gestures below
  • Aspect ratio correction — recovers the true width/height of a photographed rectangle from EXIF focal length and perspective geometry; manual override via a labelled checkbox plus value slider. The value's styling is a cue: a gray italic value means no action is needed from you (the correction is off, or a reliable value was recovered automatically from the photo's camera data); a black value means the photo has no usable camera data (e.g. a screenshot) so you should set the ratio by eye; a red value means the recovered ratio is wider/taller than the slider's 0.1010.0 range, so it's pinned at the limit and the proportions can't be fully reached. Applies to Extract and Transform → Quad output; hidden in Transform → Lines mode, which has its own Stretch control instead (below)
  • Bow correction — centre-origin slider (200…200 px) that straightens edges which bow in the extracted output (typical mild residual lens distortion). Corner-anchored: corners stay fixed and mid-edge content is moved. The value is a radial distance in output pixels — positive straightens inward-bowed (pincushion) edges by pushing content outward, negative straightens outward-bowed (barrel) edges by pulling content inward (converted internally to a curvature coefficient against the output's half-diagonal). Drag the slider, use the spin box, or type a value to find the minimum magnitude that straightens the edges. Per-image: cached so switching between images preserves each image's bow value. Extract mode only
  • Color correct — recover accurate color when a neutral reference is in the frame. Tick Color correct, then choose a reference mode — Gray or White (neither is preselected; pick the one that matches your reference). Click the swatch, then click the reference area in the left image; Rectify averages a small square around that point — its side length in pixels is the Sample size (1 = just the clicked pixel; default 10 = a 10×10 block) — and white-balances the output so the patch becomes neutral. Gray mode (a gray card, or any surface you trust as neutral gray) couples color and exposure: the Reflectance value is the target tone the patch is mapped to, in the photographer's unit — default 18% (standard middle-gray card); set it to your card's rating (e.g. 12%) or adjust to taste (adjustable 380%). White mode (a white sheet or other white reference) decouples them: it neutralizes the color cast while keeping the patch's own brightness — so white is not forced to maximum and anything brighter (a highlight, a lamp) keeps its headroom — and a centered Brightness slider (0 = unchanged) then raises or lowers the whole result independently. The swatch shows the sampled color and turns red if the spot is too bright or too dark to use; a 2-pixel-wide red border marks the sampled region, with the averaged pixels just inside it. Sample the card outside the artwork — it's read from the source image but the correction is applied to the result, in every mode. The measured gray is sticky: shoot one reference frame with a gray card, pick it once, then for other images taken under the same light just tick Color correct and they reuse that gray automatically (no need for a card in every shot). Reusing a gray takes a frozen snapshot — re-picking the reference later won't silently change images that already borrowed it. Left-click the swatch to pick a gray on the current image; right-click it to re-apply the last-used gray (handy when an image already has its own value you want to replace). An image you picked on keeps its own measurement and shows the red marker; one using a borrowed gray shows the color in the swatch but no marker
  • Corner dragging — click and drag corners; arrow keys for 1-pixel nudge
  • Edge dragging — click near an edge and drag to move it perpendicular to itself; arrow keys for fine nudging
  • ⌘-Left-Click — moves the nearest movable point (a quad corner in Extract mode, a keystone-line arrow handle in Lines mode) to the click position. Designed for precision adjustment while zoomed in; the cursor becomes a crosshair whenever ⌘ is held so you know the gesture is armed
  • ⌥-Left-Click ×2 — draws a red antialiased "alternate-line" annotation between two clicked points. Useful for sketching what auto-detection should produce in screenshots and bug reports. A dashed rubber-band tracks the cursor between the first and second click; multiple annotations accumulate; Esc clears them all (and any temporary marks in general); a new image load also clears.
  • Alignment indicator — edges turn blue when their endpoints are vertically or horizontally aligned (same x or y value); the same blue/green indicator applies to keystone-line pairs in Lines mode
  • Subregion selection — click in the center of the quad and drag to reposition; scroll wheel while dragging to resize. Use with Peel in to extract individual subjects from multi-subject photos
  • Peel stack — Peel in/out to strip successive frame layers; left panel always shows the original with mapped-back overlay
  • Full-image perspective correction — correct the entire image's perspective using a reference quad (e.g., a window or known rectangle), like a view camera tilt/shift. The Transform-mode Crop checkbox switches between a filled full canvas (default) and an auto-cropped rectangle
  • Keystone correction — remove vertical and/or horizontal keystone distortion using line pairs. Draw one or two pairs of lines on features known to be parallel (e.g., building edges, door frames); the correction makes them parallel in the output. Arrow-shaped handles distinguish lines from the quad overlay
  • Stretch (Transform → Lines) — keystone correction straightens converging lines but can't recover how wide the result should be relative to its height (the lines carry no scale, and there's no reference rectangle as in Quad mode). The Stretch slider is a by-eye correction for that residual width-to-height relationship: 1.00 leaves the width unchanged, above 1.00 widens, below narrows. Shown only in Lines mode; remembered per image
  • Saving — one Save button with an Increment checkbox. The default output name is the source image's name with _rectified appended (e.g. hotel.pnghotel_rectified.png), so a save never overwrites the input. With Increment off, Save opens a dialog (pre-filled with that default) where the extension you type picks the format (png/jpg/jpeg/tiff/tif/webp/bmp; an unsupported type is rejected with the supported list). With Increment on, Save writes the next auto-numbered file (hotel_rectified_1, hotel_rectified_2, …) with one click — no dialog. Both modes share the last-used folder and type; folder, type, and the Increment setting persist across sessions
  • Undo/redo — ⌘-Z / ⌘-Shift-Z for corner adjustments (keyboard only)
  • Drag and drop — drop an image file onto the window
  • Before/after — hold Space to compare
  • Tooltips — hover any control for a short explanation in a readable boxed popup; toolbar buttons also show their keyboard shortcut. Fully translated in all three interface languages
  • Settings persistence — all preferences (including your last save folder, file type, and Increment mode), window layout, and per-image state (corners, keystone lines, bow value, Stretch value, color-correction sample, plus any manual override of the aspect ratio) cached for every image you've touched. Switch between images with ⌘-↑/↓ while preparing a batch; come back to any image and your tuning is intact. Saved on close and restored on next launch

Keyboard shortcuts

Shortcut Action
⌘-O Open image
⌘-S Save (opens the dialog, or writes the next auto-numbered file when Increment is on)
⌘-D Reset (re-detect from scratch)
⌘-R Re-open the current file (re-read pixels from disk; per-image cache preserves corners, keystone pairs, bow)
⌘-↓ / ⌘-↑ Load next / previous image in the current directory (wraps around)
⌘-Z Undo
⌘-Shift-Z Redo
+ or = Peel in
- Peel out
Space (hold) Before/after comparison
Arrow keys Nudge selected corner, edge, or whole quad
Mouse wheel Zoom (or adjust element with Shift or left-button held)
Shift-wheel Adjust quad element under cursor (corner, edge, or whole quad)
⌘-Left-click Snap the nearest point (corner or line endpoint) to the click position
⌥-Left-click ×2 Draw a red alternate-line annotation between two clicks
Esc Clear temporary marks (annotations, etc.)
Right-click Reset zoom on clicked panel
0 Reset zoom on both panels
Shift-⌥ (hold) Show keyboard-shortcut overlay (centered popup)

Keys are written with the macOS symbols (Command) and (Option). On Linux and Windows, read Ctrl for ⌘ and Alt for ⌥ — ⌘-O is Ctrl-O, ⌘-click is Ctrl-click, Shift-⌥ is Shift-Alt. That substitution is the whole difference; no shortcut exists on one system and not another. Two macOS-only details: ⌃-click — the physical Control key — is just how a one-button mouse delivers a right-click, while ⌘-click is the snap gesture; and combinations containing Shift are shown in Apple's order on screen (⇧-⌘-Z, ⌥-⇧), the same keys as the ⌘-Shift-Z and Shift-⌥ above. Rectify labels its own tooltips and overlay for the system you are on.

The same shortcut table is printed by rectify -k (also --keyboard), so you can read it without opening the GUI.

Command line

python -m rectify photo.jpg -o rectified.jpg
python -m rectify photo.jpg -o rectified.jpg --strategy saturation -s 0.7
python -m rectify photo.jpg -o rectified.jpg --peel 1

# Full-image perspective correction
python -m rectify photo.jpg -o corrected.jpg --full-image
python -m rectify photo.jpg -o corrected.jpg --full-image --full-image-crop
python -m rectify photo.jpg -o corrected.jpg --full-image --fill-color "#808080"

# Debug logging (writes detection details to a log file)
python -m rectify --gui photo.jpg --debug
python -m rectify --gui photo.jpg --debug my_debug.log

# Incremental output (auto-numbered)
python -m rectify photo.jpg --dir output/ --prefix museum --ext jpg

Detection strategies

The GUI always runs the automatic two-pass sweep — both detection strategies, both ends of the sensitivity range, best result wins. The CLI exposes manual overrides via --strategy {auto, grayscale, saturation}, -s/--sensitivity, and the individual Canny parameters (--blur, --canny-low, --canny-high, --min-area, --epsilon).

  • Grayscale — Edge detection on luminance. Best when subject and background differ in brightness.
  • Saturation — Edge detection on HSV saturation channel (no blur). Best when brightness is similar but color richness differs (e.g., tiles on brick).
  • Auto (default) — Evaluates both using a two-pass sensitivity sweep (coarse then fine), picks the best. Scores candidates by rectangularity, margin from image edges, and perspective plausibility (vanishing-point orthogonality). Prefers larger regions initially; smaller on peel-in.

Installation methods

Pre-built application

macOS is the only platform with a published build: a signed, notarized Rectify.dmg that needs no Python installation.

  • Open the .dmg and drag Rectify.app to your Applications folder
  • Double-click Rectify.app to launch — it opens the GUI with a file dialog
  • You can also drag an image file onto the Rectify.app icon in Finder or the Dock to open it directly

The disk image holds the application and nothing else, so the guide is a separate download: https://andykopra.com/Rectify_user_guide.pdf

Linux and Windows run from source (above). You can build a standalone executable yourself if you want one — see Building a standalone executable — but it is not required, and running from source is the normal path on both.

Building a standalone executable

To create a distributable executable from source, two files are provided:

  • build.sh — Shell script that installs PyInstaller (if needed) and runs the build. Supports --onedir for faster development builds.
  • rectify.spec — PyInstaller spec file with the build configuration, including hidden imports for PySide6, macOS .app bundle settings, and image file type associations.
pip install pyinstaller
./build.sh               # Linux/macOS: single-file executable
./build.sh --onedir      # Linux/macOS: directory build (faster startup)
pyinstaller rectify.spec # Windows (from command prompt)

The signed, notarized macOS .dmg is produced by the maintainer — it needs installer artwork and an Apple Developer ID, so it is not something this source distribution can build. Download it from the releases page instead.

The output appears in dist/. On macOS, rectify.spec also creates a Rectify.app bundle with the bundle identifier com.andykopra.rectify.

Platform-specific build notes:

  • Linux: The resulting binary is platform-specific (not cross-platform). It may require libxcb libraries on the target system.
  • macOS: The spec file includes BUNDLE configuration for a .app bundle. Code signing may be needed for distribution outside of direct sharing.
  • Windows: Use pyinstaller rectify.spec from a command prompt. The spec sets console=False to suppress the console window.

See doc/Rectify_user_guide.pdf for full documentation including usage examples and a programmer's guide — or the Markdown it is rendered from, as Documentation describes.