A high-confidence, pixel-preserving stitcher for vertically scrolling screenshots.
It is designed for mobile apps and web pages where the top and bottom UI stay fixed while the body scrolls. The tool detects overlap between adjacent screenshots, removes repeated content, chooses safe seams, optionally removes transient scrollbars, and emits a JSON audit trail showing where every output region came from.
Naive screenshot stitching often fails on repetitive interfaces: transaction rows, chat messages, tables, feeds, and settings screens can look structurally similar. A visually plausible but wrong seam can silently duplicate or omit content.
This project prioritizes fidelity and verification over merely producing a long image.
- Preserves source pixels; it does not regenerate text or UI
- Automatic fixed header/footer detection
- FFT-based normalized cross-correlation for efficient coarse matching
- Multi-band edge descriptors to retain horizontal structure
- Full-resolution coarse-to-fine overlap refinement
- Duplicate screenshot detection
- Ambiguity scoring with minimum confidence margins
- Content-aware seam selection in low-information rows
- Optional transient scrollbar detection and inpainting
- Refuses to resize mismatched screenshot widths
- JSON provenance report for every stitched region
- Fails closed on low-confidence joins unless explicitly overridden
Python 3.10+ is recommended.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\\Scripts\\activate
pip install -r requirements.txtOr install the project in editable mode:
pip install -e .Provide screenshots in top-to-bottom scroll order:
python stitch_scroll.py \
shot1.jpeg shot2.jpeg shot3.jpeg shot4.jpeg \
-o combined.png \
--report combined.jsonNatural-sort filenames before processing:
python stitch_scroll.py screenshots/*.jpeg \
--sort-name \
-o combined.pngFor stricter validation:
python stitch_scroll.py screenshots/*.jpeg \
--sort-name \
-o combined.png \
--min-similarity 0.90 \
--min-margin 0.015 \
--max-seam-mae 12Override automatic fixed-UI detection when necessary:
python stitch_scroll.py screenshots/*.jpeg \
-o combined.png \
--header 850 \
--footer 320Keep scrollbars instead of removing detected transient indicators:
python stitch_scroll.py screenshots/*.jpeg \
-o combined.png \
--keep-scrollbarsA fully synthetic, non-personal dataset is included in demo/. It contains four overlapping mobile screenshots, an expected long-image result, a contact sheet, known overlap metadata, and a deterministic generator.
Try it directly:
python stitch_scroll.py \
demo/screenshot_01.png \
demo/screenshot_02.png \
demo/screenshot_03.png \
demo/screenshot_04.png \
-o demo/actual_stitched.png \
--report demo/actual_report.jsonSee demo/README.md for the demo layout and regeneration instructions.
Load + normalize orientation
↓
Build low-resolution previews
↓
Detect fixed top/bottom UI
↓
Remove adjacent duplicate frames
↓
Build multi-band edge descriptors
↓
FFT coarse overlap search
↓
Full-resolution refinement
↓
Confidence + ambiguity validation
↓
Content-aware seam selection
↓
Stitch original pixels
↓
Optional scrollbar cleanup
↓
PNG output + JSON provenance report
The default behavior is intentionally conservative. A join is rejected when any of the following is true:
- similarity is below the configured minimum;
- the best match is not sufficiently better than another distinct candidate;
- the chosen seam has excessive pixel disagreement;
- the overlap is too small;
- source segment geometry is invalid; or
- source screenshot widths differ.
--allow-low-confidence exists for manual review workflows, but should not be the default for financial, legal, archival, or evidentiary screenshots.
The JSON report includes:
- source inputs;
- removed duplicate frames;
- detected fixed UI dimensions;
- scroll displacement and overlap for every join;
- match similarity and ambiguity margin;
- seam position and seam error;
- detected scrollbar components;
- source-to-output provenance ranges; and
- runtime matching settings.
This makes the resulting long screenshot reproducible and inspectable instead of a black-box composite.
Do not commit sensitive source screenshots or generated audit reports unless you intend to store that data in the repository. .gitignore excludes common input/output directories and generated reports by default.
Current release: v0.1.0
The repository includes automated tests plus a fully synthetic regression/demo dataset with repetitive scrolling records and known overlap geometry.

