Documentation: - The GUI is stated as the primary interface on every platform; the earlier claim that Linux is CLI-first is gone. - §2.1 becomes "Installing Rectify" with macOS, Linux, and Windows subsections, each opening with the public clone of this repository and the per-session launch commands. - §3.1's Linux section reaches the depth the Windows one already had: python3-venv (stock Debian/Ubuntu fails `python3 -m venv` without it), libxcb packages, QT_QPA_PLATFORM=xcb as the Wayland fallback, a PYTHONPATH wrapper for launching from any directory, and a .desktop entry. States plainly that Linux has no packaged deliverable. - §2.15 lists all three settings paths; §2.3 covers the platform-specific Open dialog and HEIC thumbnails; Finder/Mac-only phrasing generalized. - README gains a matching Linux section, and its "pre-built executables" section no longer promises Linux and Windows binaries that were never published. Keyboard notation: - Both documents print the macOS symbols throughout, with one substitution rule (Ctrl for ⌘, Alt for ⌥) stated in the guide's §2 preamble and under each shortcut table. - Every combination is joined with a hyphen on all platforms — ⌘-O, Ctrl-O, ⇧-⌘-Z, ⌥-⇧ — rather than Apple's tight ⌘O, whose glyphs have side bearings too small to separate them from the next character. - shortcuts.py carries this in SECTIONS and _mac_translate; gui.py's tooltip modifier follows. Display labels only: the QKeySequence bindings keep Qt's "Ctrl+X" syntax and are untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
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:
- Linux: See Run from source on Linux below
- macOS: Works with Homebrew or python.org Python (Apple silicon and Intel).
For day-to-day use, install
Rectify.dmginstead of running from source - Windows: See Run from source on Windows below
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.shonce; 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.10–3.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++ 2015–2022 Redistributable (x64), which Qt needs. It is already present on most Windows installations.pyis not recognized — the Python launcher was not installed; usepython -m venv .venvinstead, 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:
- Opening a HEIC/HEIF photo from an iPhone.
- Display scaling at 125% and 150% — menu, panel, and control sizing.
- 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.
- Saving to a folder whose path contains spaces.
- Building an executable with
pyinstaller rectify.spec(see Building a standalone executable) and launching the resultingdist\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.10–10.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 3–80%). 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
_rectifiedappended (e.g.hotel.png→hotel_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
.dmgand dragRectify.appto your Applications folder - Double-click
Rectify.appto launch — it opens the GUI with a file dialog - You can also drag an image file onto the
Rectify.appicon in Finder or the Dock to open it directly
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--onedirfor faster development builds.rectify.spec— PyInstaller spec file with the build configuration, including hidden imports for PySide6, macOS.appbundle 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
libxcblibraries on the target system. - macOS: The spec file includes
BUNDLEconfiguration for a.appbundle. Code signing may be needed for distribution outside of direct sharing. - Windows: Use
pyinstaller rectify.specfrom a command prompt. The spec setsconsole=Falseto suppress the console window.
See doc/Rectify_user_guide.md for full documentation including usage examples and a programmer's guide.