Files
myscan/README.md
T
erikandClaude Opus 5.5 2ef567580a Decide text orientation per page instead of per line
RapidOCR's angle classifier judges each cut-out line on its own and now
and then turns a perfectly upright line upside down, which then reads as
garbage ("Jo a ds ne t i d s t d no" for a line ending in "or" on the
latest scan). It is off now. All lines on a page face the same way, so a
page that reads poorly (upright text scores 0.97-1.0 per character,
upside-down text 0.55-0.8) is read again rotated 180 degrees and the
better reading kept; that also covers pages turned 90 degrees. The app
shows an upside-down page turned the right way up.

Upright pages read about 25% faster without the per-line classifier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:44:40 +00:00

49 lines
4.1 KiB
Markdown

# 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), 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). 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 and saved 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 and Save text (.txt) take 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](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`
**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).