--- type: entity tags: [parking, vision, anpr, anti-fraud, service] sources: [] updated: 2026-06-15 status: open --- # OpenCV ANPR / Vision Service A **local microservice** that analyses camera snapshots: reads the licence **plate** (ANPR) and extracts **vehicle attributes** for verification. Built by us (decision 2026-06-15) to do recognition **host-side on ordinary IP-camera snapshots**, replacing the dedicated edge-AI [[lpr-camera]]. See decision [[vision-service]]. ## Two jobs 1. **Identity (ANPR).** snapshot → `{ plate, confidence, bbox }`. Feeds the existing `IdentitySource = "lpr"` ([[parking-session]]): the plate is a session/identity key and the way a plate-bound [[subscription]] is matched. 2. **Verification (anti-fraud witness).** snapshot → vehicle attributes — at minimum `{ make?, model?, colour, bodyType }`, ideally a compact **visual fingerprint** (an embedding). This is the answer to **plate-spoofing**: *a fraudster prints a registered/paid plate and drives in with a different car.* Plate-reading alone can't catch that; comparing the **vehicle** seen at entry vs. exit (and vs. the [[subscription]]'s known car) can. A plate that entered on a red hatchback but exits on a black SUV is a **reconciliation anomaly** — exactly the independent-witness role the [[append-only-event-chain]] flags as the unbuilt gap. See [[reconciliation]]. > The two jobs are why this is worth building rather than just plate-OCR: the service is both an > **identity source** and an **independent witness**, the visual analogue of the whole system's > "two records that must reconcile" thesis. ## Architecture — separate localhost process - A **Python service** (e.g. FastAPI) running **on the appliance**, called by the Node backend over **localhost HTTP** (`POST /analyze` with the JPEG bytes the camera driver already pulls — see [[lpr-camera]] "driver/storage boundary": `Snapshot.bytes`). - **Fully offline** ([[offline-first]]): all inference is local, no cloud. Model weights ship on the appliance. - **Process isolation is deliberate** — it keeps a heavy Python/native/AGPL stack out of the Node app's process and license surface (see licensing below), and gives it its own failure domain. If the service is down/slow, the host falls back (transient ticket path) rather than blocking the lane. - **Request/response (first cut):** - `POST /analyze` → `{ plate: {text, confidence, bbox}|null, vehicle: {colour, bodyType, make?, model?, embedding?}, modelVersion, tookMs }` - `GET /health` → readiness + model versions. - The Node side wraps it behind an internal interface (like a device adapter) so the recognizer can be swapped without touching business logic. ## Licensing — scoped AGPL exception (amends the standing rule) The app is strictly **MIT/Apache/BSD** ([[technology-stack]], [[standing-decisions]]). Accurate ANPR/vehicle models are mostly **AGPL** (YOLO/Ultralytics detectors, OpenALPR) or commercial. Decision (2026-06-15): **allow AGPL inside this service only.** It is a **separate process**, not linked into the app, so its obligations don't reach the Node/React codebase; the app's permissive guarantee is preserved. Recorded as an explicit exception in [[standing-decisions]] / [[vision-service]]. - OpenCV core itself is **Apache-2.0** (clean either way). - AGPL note: if the appliance is ever offered as a network service to third parties, AGPL's network-use clause could require offering the service's source — relevant only if productised beyond the on-site appliance; flag at that point. ## Anti-fraud / threat-model fit - **Plate spoofing** (the motivating case): vehicle-attribute / fingerprint mismatch entry↔exit or vs. a [[subscription]]'s registered car → anomaly. Doesn't *block* on its own (recognition is probabilistic) — it **flags for [[reconciliation]]** and is captured in the signed record. - The recognition result and the source image both attach to the signed [[append-only-event-chain]] entry, so the *evidence* is tamper-evident even though recognition itself is host-side and fallible. - Recognition is **advisory, never the sole authority** to open a barrier where money/access is at stake — confidence thresholds + fallback to ticket/manual; a low-confidence read must not strand a car ([[fail-state-safety]]). ## Open - **Recognizer choice** (permissive-only vs. AGPL model) and accuracy targets — see [[vision-service]]; AGPL now permitted in-service. - **Vehicle fingerprint**: attribute classifier vs. embedding-similarity; what threshold makes a mismatch an anomaly without false-positiving on lighting/angle. - **Compute footprint** on the appliance (CPU-only vs. a small GPU/NPU) — procurement input ([[bom]], [[open-questions]]). - Per-camera **opt-in** ("optionally bound", user's word): which lanes/cameras route snapshots to the service.