PaperPod

License: MIT

Offline overhead video → named PDFs for Paperless. Motion windows, perspective crop, OCR — nothing leaves your machine.

Public mirror: github.com/Gitilia/PaperPod

How it works

  1. Record one continuous overhead video, placing documents under the camera one at a time (optionally saying out loud what each one is).
  2. PaperPod finds "stable windows" where nothing is moving, detects the document outline, and produces a perspective-corrected crop. Detection scores candidate contours by shape (filled, convex, plausible aspect ratio) rather than just picking the largest one, and runs on a resolution-independent downscale so it works the same on FHD and 4K phone video. Best-frame selection prefers hands-free frames first (skin detection over the document region), sharpness second — so a finger holding the page flat doesn't end up in the scan when a clean frame exists elsewhere in the event.
  3. The crop is auto-rotated upright (0/90/180/270) — no need to place documents facing a particular way.
  4. Tesseract OCR (run against an enhanced crop) extracts a vendor/date/total for receipts, a form code + organization name for recognized Canadian tax slips (T4, T4A, T5008, ...), a printed document title (Transaction History, Invoice, Direct Deposit, ...), and a printed "PAGE X OF Y" marker where present. Enhancement always runs for OCR (and optionally on the saved crop when document.enhance is on): illumination normalize → CLAHE → gamma darkening for pale grey / dot-matrix ink (e.g. bank branch statements). Crops narrower than ~1800px also get a 1.5× cubic upscale first so thin ribbon strokes have enough pixels; native ~2k webcam captures (roughly 2000px-wide crops) skip that step, since OCR results there are identical with or without the upscale.
  5. Consecutive captures whose page markers advance under the same page total are grouped into one multi-page PDF (a bank statement's 17 pages become one PDF, not 17). Duplicate captures of the same page keep the better read.
  6. (Upcoming) Spoken descriptions (faster-whisper) as an alternate naming source; a review step to rename/merge/split before export.

Status

Module Purpose Status
capture/ Frame sampling + audio extraction from video files Built
vision/ Motion detection, document contours, perspective crop, hands-free best-frame selection Built
events/ State machine: placed / page-flipped / cleared, pod grouping Planned (page-marker grouping in pipeline.py covers paginated documents)
transcribe/ Local speech-to-text (faster-whisper) Planned
ocr/ OCR naming (Tesseract): vendor, date, total, doc title, page markers Built
naming/ Final filename assembly + summary CSV Built
pdf/ PDF assembly (img2pdf), multi-page Built — no consume-folder staging yet
review_cli/ Pre-export review (rename/merge/split) Planned

Known limitation: multi-page grouping relies on a printed "PAGE X OF Y" marker (bank statements, most system-generated letters). Multi-page documents without printed page numbers still export as one PDF per page until the motion-based events/ state machine exists.

Setup

Requires Python 3.11+ and ffmpeg (brew install ffmpeg).

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Usage

# Generate a synthetic test video (no camera needed)
python sample_data/make_sample_video.py

# Inspect a video
python -m paperpod probe sample_data/videos/synthetic_sample.mp4

# Detect stable windows + document candidates
python -m paperpod detect sample_data/videos/synthetic_sample.mp4

# Detect, OCR-name, and export each document as its own PDF
python -m paperpod export sample_data/videos/receipts_sample.mp4

# Extract the audio track (16 kHz mono WAV, whisper-ready)
python -m paperpod extract-audio my_recording.mp4

detect writes to output/<video-name>/:

  • crops/window_NNN.png — perspective-corrected document candidates
  • frames/window_NNN_full.png — the full best frame per window (debugging)
  • report.json — video metadata, motion events, stable windows, detections
  • motion_scores.csv — per-sample motion scores, for tuning motion.threshold

export runs detect and then groups, names, and writes:

  • pdf/<name>.pdf — one PDF per document (multi-page when printed "PAGE X OF Y" markers chain consecutive captures together), named YYYY-MM-DD_vendor[_doc-title].pdf from OCR-extracted date/vendor/title (or <year>_<form_code>_<org_name> for recognized tax slips). When the document's own date can't be read, the scan date is used instead — files always sort chronologically, nothing is named UNSORTED anymore (export_summary.csv records source_of_name: scan_date for those)
  • export_summary.csv — timestamp, pod_id, page_count, page_windows, source_of_name (ocr/llm/scan_date/none), OCR vendor/form_code/date/total/confidence, doc title, detection confidence, rotation applied, final filename

Before naming, each crop is auto-rotated to be right-side-up (Tesseract's built-in orientation detection, falling back to a 4-way OCR-confidence sweep when a crop has too little text for that to work) — you don't need to worry about which way documents face when placing them.

Nothing is copied into a Paperless-ngx consume directory yet — review the pdf/ folder yourself before moving files anywhere.

Tuning

All thresholds live in config.yaml (motion sensitivity, stable window duration, contour size/shape filters, Canny thresholds, detection downscale width, auto-rotate/enhance toggles, speech matching window, output paths). Plot motion_scores.csv to pick a motion.threshold that separates your camera's noise floor from real hand movement.

Recording tips (from real-world testing): shoot in 4K, keep hands out of frame once a document is placed, and use a plain dark, matte (non-glossy) surface under documents — busy wood grain or shiny surfaces make contour detection and OCR meaningfully harder. Document detection and orientation correction are tuned against real phone-camera footage (receipts on a wood table and multi-page CRA tax slips), not just synthetic test videos.

Tests

python -m pytest

Tests are self-contained: they synthesize frames and tiny videos on the fly, no sample assets required.

License

MIT — see LICENSE.

S
Description
Local privacy-first tool — overhead video of documents → named PDFs for Paperless-ngx
Readme MIT
222 KiB
Languages
Python 98.3%
Shell 1.7%