The text pane is gone: recognised text is selected on the page image like
in a PDF viewer (drag in reading order, double-click a word, triple-click a
line, shift-click to extend, Ctrl+A), copied with Ctrl+C or the context
menu, and previewed in the status bar; on X11/Wayland it also goes to the
primary selection. Middle-drag or Space+drag pans.
OCR asks RapidOCR for per-character boxes and keeps each line's character
boundaries (Line.edges), with Line.locate/quad/word for hit testing and
drawing; lines whose boxes don't match the text fall back to even spacing.
Errors are logged to the terminal with their traceback, and Ctrl+C in the
terminal closes the app (restoring mouse mode), dismissing any open dialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tools/app.py (myscan/app.py, Qt/PySide6) shows the camera and the page as it
is stitched, Start/Stop or the scan button (F5), then reads the finished page
with RapidOCR (myscan/ocr.py; bundled multilingual models, offline). Lines are
outlined on the page by confidence and linked to an editable text pane that
can be copied or saved. Existing images can be opened or dropped, so the app
is useful without the mouse.
The scan loop moves from tools/scan.py to myscan/scanner.py, shared by both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
stitch.py and pagegraph.py mixed laser odometry, image registration,
frame correction, canvas handling, pose-graph maths and the two stitchers.
They are now modules of myscan/stitching, which imports nothing from the
device code:
geometry rigid transforms, pose vectors, warp_add
filters grayscale, high-pass, feather weights
correct Corrector: undistortion + flat field (and weighted variant)
odometry LaserOdometry: the laser model and the phase-correlation
override, shared by both stitchers
register ECC, phase correlation, SIFT matching, overlap agreement
canvas growable Canvas, layout, parallel render, normalize/compose
posegraph robust Gauss-Newton, largest connected component
loops keyframes and loop-closure candidates
pagegraph PageGraph, now orchestration only
incremental Stitcher, on the shared Canvas
The stitchers take a Corrector and an odometry object (anything with
predict()) instead of building them, so another device can supply its
own motion prior. live.py drops its own canvas and normalisation for the
shared ones; PageGraph's mosaic check uses the parallel renderer.
Output on scan_text is unchanged (global stitcher identical, incremental
within 1 grey level). stitch.py --live now applies lens correction too
(--no-undistort restores the old output).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PageGraph.add registers consecutive frames on half-resolution high-pass
images (ECC without pre-blur, phase correlation on a half-size window) and
builds the high-pass with the illumination blur done on a 4x smaller copy;
full-resolution high-pass images are made on demand for keyframes only. The
flat field is pre-warped into undistorted coordinates, so a frame is
remapped as uint8 and corrected with one multiply. add() goes from ~13-20
to ~3-7 ms per frame with the same registration.
image() draws the page on one thread per row strip of the canvas, with the
flat-field gain and feather weight folded into one multiply. compose crops
before normalising and takes the white point from a 1/16 subsample.
_agree computes its moments with OpenCV, which releases the GIL; its
decisions are unchanged on all 1425 checks of a 2525-frame scan.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scan.py --view serves a page (myscan/webview.py, standard library only) with
MJPEG streams of the latest camera frame and the page as it is stitched,
Start/Stop buttons, PNG/JPEG download of the finished page and a screenshot
of the current view.
myscan/live.py keeps a downscaled preview: new frames are pasted at their
current best pose, and a background thread re-solves the pose graph every
1.5 s (throttled to at most ~50% duty) and redraws the preview off the
registration thread. PageGraph.solve can now run on a prefix of the frames
while add() continues, keeping loop closures and features between solves,
so the final solve after the stop press is much shorter. Frame PNGs are
saved on their own thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
calib/eeprom_3000.bin is the 0x3000 header plus the signed HardwareData blob
(19417 bytes) read from the mouse, mapped field by field in EEPROM.md. The rest
of calib/ stays ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
- ScanSession: scan-mode context manager with bright-pixel auto-exposure and
sensor colour gains; tools/scan.py scans on the scan button with live stitching
- Read-only EEPROM access and calib.py parser (laser geometry, intrinsics,
colour gains, black/white flat-field samples); flat-field correction in Stitcher
- OV7740 sensor access via the MCU proxy; colour gains from EEPROM fix green cast
- Stitcher: colour support, high-passed registration (robust on blank paper)
- RE notes on the Mac app's colour, exposure, stitching and button handling
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UVC extension-unit control (mouse/scan mode, LEDs, exposure), JPEG header
fix and embedded laser record decoding, session recorder, and a stitcher
that combines laser dead reckoning with phase correlation and ECC refinement.
FINDINGS.md holds the reverse-engineered protocol and laser calibration.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>