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>
62 lines
2.7 KiB
Python
62 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Scan with the scan button, like the original app: press it to start, glide the
|
|
mouse over the page, press it again to stop. Frames are registered while you scan;
|
|
after the stop press the page is assembled from all of them at once (see
|
|
myscan/stitching/pagegraph.py) and saved as <outdir>/scan-YYYYmmdd-HHMMSS.png. The mouse goes
|
|
back to mouse mode between scans (and on Ctrl-C). The loop itself is myscan/scanner.py.
|
|
|
|
With --view, a browser viewer (myscan/webview.py) shows the camera and the page as it
|
|
is being stitched, and has Start/Stop buttons that work like the scan button."""
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
|
|
from myscan.session import quiet_libjpeg # noqa: E402
|
|
|
|
quiet_libjpeg()
|
|
|
|
from myscan.scanner import Scanner # noqa: E402
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("-o", "--outdir", default="scans")
|
|
ap.add_argument("--once", action="store_true", help="exit after one page")
|
|
ap.add_argument("--now", action="store_true", help="start the first scan immediately")
|
|
ap.add_argument("--video", help="capture device (default: find the myScan, else /dev/video4)")
|
|
ap.add_argument("--target", type=float, default=200, help="auto-exposure target for paper (95th pct of brightest channel)")
|
|
ap.add_argument("--exposure", type=int, help="fixed E6 value instead of auto-exposure")
|
|
ap.add_argument("--gray", action="store_true")
|
|
ap.add_argument("--no-wb", action="store_true", help="skip white-patch balancing")
|
|
ap.add_argument("--no-flat", action="store_true", help="skip the EEPROM flat-field correction")
|
|
ap.add_argument("--no-undistort", action="store_true", help="skip the EEPROM lens correction")
|
|
ap.add_argument("--save-frames", action="store_true", help="also keep raw frames + records.csv per scan")
|
|
ap.add_argument("--max-time", type=float, default=180, help="stop a scan after this many seconds")
|
|
ap.add_argument("--view", type=int, nargs="?", const=8000, metavar="PORT",
|
|
help="serve a live browser view on PORT (default 8000)")
|
|
a = ap.parse_args()
|
|
viewer = None
|
|
if a.view is not None:
|
|
from myscan.webview import Viewer
|
|
viewer = Viewer(a.view)
|
|
print("live view: " + " ".join(viewer.urls()), flush=True)
|
|
scanner = Scanner(a, viewer)
|
|
first = True
|
|
try:
|
|
while True:
|
|
if not (first and a.now):
|
|
print("press the scan button to start (Ctrl-C to quit)", flush=True)
|
|
scanner.wait_press()
|
|
first = False
|
|
scanner.scan()
|
|
if a.once:
|
|
break
|
|
except KeyboardInterrupt:
|
|
print("\nbye")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|