feat(vision): vehicle stage, phase A — YOLOX-S (Apache-2.0 ONNX) beside the plate recognizer

Fills /analyze vehicle.body_type + confidence (car / motorcycle / bus / truck from COCO,
mapped to the shared vocabulary) for the Car Wash desk's category suggestion
(venue-modules.md §Vehicle category from vision). Advisory: the operator decides, a
confident downgrade is flagged, nothing is gated on it.

- vision_service/vehicle.py: pure numpy/cv2 letterbox (pad 114, raw BGR), stride-grid
  decode, class-agnostic NMS, one vehicle per frame (the box holding the plate's centre,
  else the largest); YoloxVehicleDetector on onnxruntime CPU, 2 intra-op threads.
- recognizer.py: WithVehicle composes the stage over any plate recognizer (stub included);
  a failing stage yields vehicle=null + a "vehicle: …" note in /health.detail — never
  costs the plate read. model_version reads "<plate>+yolox:yolox_s.onnx@640".
- settings: VISION_VEHICLE_MODEL_PATH (unset = off), _INPUT_SIZE (640), _MIN_CONFIDENCE
  (0.4, the detector's floor; the flag threshold is site config).
- Dockerfile bakes yolox_s.onnx (best-effort curl at build; no network → stage off) and
  sets the path; compose forwards it (empty = off); .env.example documents it.
- Measured on four real dev entry frames (DS-2CD1047G3H, 2560×1440): car at 0.83–0.88 in
  ~240–330 ms; empty lane with a person → none.
- tests/test_vehicle.py: decode/NMS/pick/letterbox on synthetic tensors, the composition,
  and a missing-model /health. Wiki: opencv-anpr-service, venue-modules, log.

Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
2026-09-06 19:53:11 +02:00
parent 5e1395db18
commit 20a3cb3e80
10 changed files with 521 additions and 15 deletions
+7 -5
View File
@@ -188,11 +188,13 @@ onto the site's own categories ("car, sedan, hatchback → Vetura").
and the read is at or above the threshold → one `anomaly` (`carwash.categoryDowngrade`, both
categories, both prices, operator, snapshotId) and `downgrade_event_id` on the order. Equal,
upgrade, unsure or unmapped reads flag nothing. The order is always created.
- **Model — NOT built.** No bundled recognizer produces `body_type` yet, so today the desk shows
nothing and nothing is flagged. Phase A = a COCO detector on Apache-2.0 ONNX weights (car /
truck / bus / motorcycle, plus the vehicle crop); Phase B = the body-type classifier trained on
the pilot's own frames — every wash order is a labelled frame (entry snapshot + the category a
person chose), so the dataset builds itself on park-2. Reports (discrepancies per operator per
- **Model — phase A built (same day).** YOLOX-S (Apache-2.0 ONNX) as a vehicle stage beside the
plate recognizer: car / truck / bus / motorcycle + the vehicle crop, ~250 ms per entry frame on
CPU, weights baked into the vision image. Details and measurements on [[opencv-anpr-service]]
§Vehicle body type. Phase B = the body-type classifier trained on the pilot's own frames —
every wash order is a labelled frame (entry snapshot + the category a person chose), so the
dataset builds itself on park-2. Until then a Vetura/SUV list sees every car as Vetura and no
downgrade fires; van/truck/bus/motorcycle do separate. Reports (discrepancies per operator per
shift) wait for the first real reads.
## Car Wash — the pilot module (settled 2026-09-05)