Global page assembly with loop closures and lens undistortion

The frame-by-frame stitcher accumulated drift, so later passes landed rotated
and offset, doubling text. myscan/pagegraph.py instead solves all frame poses
together: ECC odometry between consecutive frames (bridging single corrupt
frames), SIFT+RANSAC loop closures between overlapping passes, and a robust
Gauss-Newton pose graph. Loop matches must agree over the whole overlap, which
rejects matches on repeated text. Off-page frames and frames that disagree
with the mosaic are left out of the render.

calib.Undistort applies the EEPROM lens model (OpenCV k1..k3, p1, p2 plus
the tilt homography, as decoded in EEPROM.md); up to 8.8 px at the corners.

scan.py registers frames on a worker thread so capture no longer drops
frames, then assembles the page after the stop press (~11 s for a full
page). stitch.py uses the new assembly by default; --live keeps the old one.

EEPROM.md documents the full calibration blob layout, checksum and signature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
erik
2026-10-02 13:06:45 +00:00
co-authored by Claude Opus 5.5
parent e70d56d957
commit 026aa6c894
8 changed files with 781 additions and 36 deletions
+7 -2
View File
@@ -5,12 +5,17 @@ Linux driver tools for the Hama myScan (00052343) scanner mouse, a Dacuda design
- `myscan/xu.py` — UVC extension-unit access: switch between mouse and scan mode, LEDs, exposure
- `myscan/frame.py` — fix the lying JPEG header and decode the per-frame sensor record (laser dx/dy)
- `myscan/stitch.py` — assemble frames into a page image (laser dead reckoning + image registration)
- `myscan/calib.py` — per-device calibration from the mouse's EEPROM: colour gains, flat field, lens undistortion
- `myscan/pagegraph.py` — assemble a page from all frames at once: frame-to-frame registration, loop closures
between passes, pose-graph optimisation (used by `scan.py` and `stitch.py`)
- `myscan/stitch.py` — the older live, frame-by-frame stitcher (`stitch.py --live`) and shared helpers
- `tools/scan.py` — scan with the button: `python tools/scan.py` (press to start, press again to stop)
- `tools/record.py` — record a scan session: `python tools/record.py -d 25 -o scan_text`
- `tools/stitch.py` — stitch a recording: `python tools/stitch.py scan_text` -> `scan_text/page.png`
- `tools/probe.py` — log raw frames and HID reports side by side
- `tools/scantest.py` — minimal scan-mode test: grab frames, decode records, restore mouse mode
See [FINDINGS.md](FINDINGS.md) for the protocol and calibration notes.
See [FINDINGS.md](FINDINGS.md) for the protocol and calibration notes, and [EEPROM.md](EEPROM.md) for the
calibration blob.
Setup: `python -m venv .venv && .venv/bin/pip install -r requirements.txt`