Recognize a vehicle's make, model, and (where possible) year from a photo. The founding idea was to treat the wheel/rim as a fingerprint; the research pass (docs/architecture.md) tested that premise and reframed it honestly:
- Whole-car make/model/year is near-solved (~95% top-1 on Stanford Cars with a plain CNN) — the signal lives in body outline, greenhouse/window shape, and proportions.
- The wheel is a strong-but-fallible fine-grained cue, not a fingerprint. One OEM design is shared across many models/years; one model-year ships several wheels; aftermarket/replica wheels sever the link entirely and cause confident false positives.
So the system is a whole-car classifier with the wheel as an optional tie-breaker for cases the body can't separate (badge-engineered platform twins) — matching the "sieve an image through several models" framing, with the body carrying the load and the wheel adding fine-grained lift only where it measurably helps.
photo ─▶ whole-car make/model/year classifier (the body baseline — carries ~95%)
│
└─▶ detect wheel(s) ─▶ crop ─▶ embed (metric-learning encoder)
│
▼
nearest-neighbor over wheel design index
│
late-fusion re-rank with body outline / size / window shape / color
+ fitment-DB plausibility + abstention on aftermarket / unknown
▼
ranked (make, model, year-range) candidates + confidence
The wheel path uses an embedding + nearest-neighbor design (not a fixed closed-set classifier) so the label space can grow every model year, new designs enroll few-shot, and the output degrades gracefully to a ranked top-k with an explicit "unknown".
- Wheel-only exact make/model/year is not achievable. The wheel's real contribution is a number we measure — the gap between the ~95% body baseline and wheel-crop-only accuracy — not one borrowed from prior work.
- Year can't come from the wheel alone (designs persist 10–15 years); at best a generation/range via fusion + a fitment database.
- The build is therefore gated: two cheap kill-switch experiments (experiments/phase_minus_1) decide whether the wheel branch is worth building at all, before any dataset is assembled.
| Phase | Deliverable |
|---|---|
| −1 | Kill-switch gate (1 wk): (a) are wheel designs visually separable across angles? (b) does body+wheel beat body-only? If either fails, stop. |
| 0 | Foundations + legality: CVAT + DVC + MLflow, provenance ledger, label-space skeleton, design-id→MMY ground-truth strategy. |
| 1 | Body-only baseline (~95% null hypothesis) + first wheel-crop dataset for one make (Toyota) + a wheel detector. |
| 2 | Wheel encoder (Sub-center ArcFace) + FAISS index + the decisive ablation (wheel lift over baseline, per class) → go/no-go. |
| 3 | Fusion (body/size/greenhouse/color) + abstention + fitment plausibility. Contingent on Phase 2 lift. |
| 4–5 | Scale to full make coverage; hardening; commercialization gate (attorney sign-off, permissive-only release). |
Full detail, dataset inventory with licenses, and risks: docs/architecture.md.
src/wheels_model/ data (schema), labeling, detect, train, infer, utils (v0 scaffold)
docs/ architecture.md, sourcing-policy.md
experiments/ phase_minus_1/ — the two kill-switch experiments
config/ config.example.yaml (copy to config.yaml, gitignored)
data/ image corpus (git-ignored; versioned separately)
tests/ schema tests
The target module decomposition (baseline / encode / index / fuse / gate / eval / sourcing) is
in architecture.md §10; it's migrated in as
each phase lands rather than built empty up front.
python -m venv .venv && . .venv/Scripts/activate # Windows Git Bash
pip install -e ".[dev]"
pytest # schema testsThe ML stack (torch / timm / ultralytics / faiss) installs per-phase; torch is CUDA-specific —
see requirements.txt.
Every image becomes one WheelImage record
(schema.py) capturing provenance + license + tier (the
legal ledger), the make/model/year target, auxiliary vehicle attributes, the wheel crop box, and
wheel attributes including OEM-vs-aftermarket. Images are never committed to git; the corpus is
described by a manifest and versioned separately. Sourcing rules:
docs/sourcing-policy.md.