erikandClaude Opus 5.5 5f9ecca302 Make only a scan's unscanned area transparent, not every white pixel
The page view made every pure-white pixel transparent. The white point
puts the paper's 97th percentile at 245, so the brightest paper clips
to 255 and showed as dark speckles; opened images with a white
background turned dark entirely; and the live preview (unscanned area
already dark) only got the speckles. Now only fresh scans are masked,
and only the white regions connected to the border, which is where the
stitcher's uncovered area is after cropping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:55:29 +00:00

myscan

Linux driver tools for the Hama myScan (00052343) scanner mouse, a Dacuda design (USB 274f:c001: UVC MJPEG camera + HID mouse).

  • 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/calib.py — per-device calibration from the mouse's EEPROM: colour gains, flat field, lens undistortion
  • myscan/stitching/ — page stitching, independent of the scanner hardware (no device imports), so it can be used elsewhere: from myscan.stitching import Corrector, PageGraph. Its modules, lowest level first: geometry (rigid transforms, poses), filters (grayscale, high-pass, feather weights), correct (Corrector: lens undistortion + flat field), odometry (LaserOdometry, the myScan's laser motion prior; another device can pass its own object with predict()), register (ECC, phase correlation, SIFT matching, overlap check), canvas (weighted canvases, page composition, parallel rendering), posegraph (robust pose-graph optimisation), loops (loop-closure candidates), and the two stitchers built from them: pagegraph (PageGraph: assembles the page from all frames at once with loop closures between passes; used by scan.py and stitch.py) and incremental (Stitcher: frame by frame, stitch.py --live)
  • tools/scan.py — scan with the button: python tools/scan.py (press to start, press again to stop). --view [PORT] serves a live view at http://<host>:8000/: the camera, the page as it is stitched (re-solved every 1.5 s, current position outlined), Start/Stop buttons, PNG/JPEG download of the finished page and a Screenshot button (camera + page + status as one PNG)
  • tools/app.py — desktop app (Qt/PySide6; Linux, Windows, macOS): live camera and page view while scanning (Start/Stop or the scan button, F5). Under the camera a dial shows how the mouse is turned on the page since the scan started (a big "A", coloured by tracking), its direction and speed (arrow), and the live readings: speed, distance, offset from the start, area covered, tracking (share of recent frames matched by image rather than the lasers alone), the lasers' surface quality, exposure, paper brightness and frame rate. Then OCR of the finished page with RapidOCR (myscan/ocr.py; multilingual PP-OCRv6 models bundled in the wheel, works offline; an upside-down page is detected and turned). Level (Ctrl+L, on by default) shows the page rotated so its lines are horizontal, using the text direction found by OCR; switch it off to see the page as stitched. Save image saves what is shown. Recognised lines are outlined on the page by confidence, and the text is selected right on the page like in a PDF viewer: drag (in reading order across lines), double-click a word, triple-click a line, shift-click to extend, Ctrl+A; Ctrl+C or right-click copies. With Paragraphs on (Ctrl+J, the default), copied text has each paragraph on one line: line breaks inside a paragraph become spaces and words hyphenated across lines are rejoined; a line ends a paragraph if the next line is indented or its first word would have fitted on it (works for justified and ragged text), and rows with several columns are left alone. Right-click offers the other form. Middle-drag or Space+drag pans, the wheel zooms. Copy all takes the whole page. python tools/app.py [image] also opens and reads existing images (Open, or drag and drop), which is all it does on machines without the mouse
  • myscan/scanner.py — the scan loop shared by scan.py and the app (finds the mouse's /dev/videoN itself)
  • myscan/live.py, myscan/webview.py — the live preview and its browser viewer (standard library only)
  • 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 for the protocol and calibration notes, and EEPROM.md for the calibration blob.

Setup: python -m venv .venv && .venv/bin/pip install -r requirements.txt

Calibration cache: calib/eeprom_3000.bin in this repo is the EEPROM dump of one specific mouse. myscan/calib.py uses that file whenever it exists and only reads the mouse when it is missing. With a different mouse, delete it first (rm calib/eeprom_3000.bin), so the next scan reads and caches your own unit's calibration (about 15 s).

S
Description
Vibecoded driver and picture stitched for the Dacuda Scanner Mose (HAMA myScan)
Readme
1 MiB
Languages
Python 100%