Files
parking_solution/wiki/entities/opencv-anpr-service.md
T
julian 8a8e74561d wiki: design the business layer (session, tariff, permit, vision, shift, ops)
Pivot from the hardware/integrity layer to the parking operation. All
wiki-only; no code yet. Core principle throughout: business entities are
projections over the signed append-only event log, never mutable tables.

New concepts: parking-session, tariff (composable/versioned, FX-ready),
shift (manned-only Z-report), capacity-occupancy, validation-discounts,
reporting-analytics, clock-integrity, ticket-encoding, anti-passback.
New entities: permit, opencv-anpr-service, blocklist.
Decisions: session-model, vision-service (host-side ANPR + vehicle
verification; scoped AGPL exception for the isolated service).

Updates: append-only-event-chain (new event types + vision witness),
local-jwt-auth (drop 8h expiry -> until logout; code change pending),
lpr-camera (host-side recognition supersedes edge-AI), standing-decisions
(AGPL exception), open-questions (+FX, +pay-station money corners, backup).

Deferred + flagged: intercom/help-call, receipts/refunds/change, FX engine,
lane topology (#1).
2026-06-15 17:41:38 +02:00

4.8 KiB

type, tags, sources, updated, status
type tags sources updated status
entity
parking
vision
anpr
anti-fraud
service
2026-06-15 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 permit 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 permit'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 permit'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.