"""Dev CLI to test a recognizer against an image file — no HTTP, fast feedback. uv run python -m vision_service.cli path/to/car.jpg uv run python -m vision_service.cli car.jpg --recognizer stub # contract only uv run python -m vision_service.cli car.jpg --ocr cct-s-v2-global-model Defaults to the `fast_alpr` recognizer (the point of this tool). Prints the parsed plate result as JSON. If the `alpr` extra isn't installed it says so and exits non-zero rather than silently using the stub. See wiki/entities/opencv-anpr-service.md. """ from __future__ import annotations import argparse import sys from pathlib import Path from .recognizer import build_recognizer from .settings import Settings def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(prog="vision-recognize", description="Run a recognizer on an image.") parser.add_argument("image", type=Path, help="path to an image file (JPEG/PNG) with a plate") parser.add_argument( "--recognizer", choices=["fast_alpr", "stub"], default="fast_alpr", help="which recognizer to use (default: fast_alpr)", ) parser.add_argument("--detector", default=None, help="override the fast-alpr detector model name") parser.add_argument("--ocr", default=None, help="override the fast-alpr OCR model name") args = parser.parse_args(argv) if not args.image.is_file(): print(f"error: no such file: {args.image}", file=sys.stderr) return 2 settings = Settings(recognizer=args.recognizer) if args.detector: settings.detector_model = args.detector if args.ocr: settings.ocr_model = args.ocr rec = build_recognizer(settings) if not rec.ready: err = getattr(rec, "error", "unavailable") print( f"error: recognizer '{args.recognizer}' not ready: {err}\n" "hint: install the models with uv sync --extra alpr", file=sys.stderr, ) return 1 image_bytes = args.image.read_bytes() result = rec.analyze(image_bytes) # Pydantic v2: model_dump_json gives a clean, stable rendering. print(result.model_dump_json(indent=2)) if result.plate is None: print("\n(no plate detected)", file=sys.stderr) else: flag = " [LOW CONFIDENCE]" if result.low_confidence else "" print( f"\n→ {result.plate.text} ({result.plate.confidence:.3f}){flag} in {result.took_ms:.1f} ms", file=sys.stderr, ) return 0 if __name__ == "__main__": raise SystemExit(main())