Python system that detects, crops, and reads vehicle license plates from images, video, and live IP cameras — tuned for Turkish truck/trailer plates with an international fallback.
· Python · YOLO11-pose · ONNX · OpenCV · fully offline
Portfolio edition. This is a sanitized subset of a system built for a commercial client. It contains the computer-vision engine only. All field data, trained weights, credentials, the operational gate/database layer, and anything client-identifying have been removed. See Scope below.
- 4-corner pose detection, not bounding boxes. An axis-aligned box physically
cannot wrap a plate seen at an angle — it grabs background or clips a corner.
The detector regresses the plate's four corners (
YOLO11-pose, 1 class,kpt_shape=[4,3]), which feed directly intocv2.getPerspectiveTransformfor a perspective-correct crop. No contour hunting, no threshold tuning. - Synthetic composite data to close a measured domain gap. Ablations showed the confidence collapse on Turkish plates was brightness polarity (training plates dark-on-light vs. Turkish light-on-dark), not colour; and the 4.7:1 vs 3.1:1 aspect ratio hurt corner precision, not confidence. Fix: composite synthetic Turkish plates onto real plate quads (label is free and exact). Held-out recall went 15% → 98%.
- Torch-free ONNX deployment. The detector layer loads
.pt(dev/GPU) or.onnx(prod/CPU) behind one interface; the rest of the pipeline is agnostic. Production runs on CPU withonnxruntimeonly (no torch/ultralytics), fully offline. - Temporal K-frame voting OCR. Per vehicle, the best K frames are read and votes
are combined per character position, weighted by character probability — the cure
for single-frame
I↔J,0↔Oconfusions. - Position-aware Turkish validation. Province code 01–81, no
Q/W/X, digit/letter coercion by position (4ZBHE495 → 42 BHE 495), with a guard against turning all-digit fleet/container codes into fake plates.
flowchart LR
SRC["source<br/>image · video · rtsp"] --> DET["detector<br/>4 corners + conf<br/>.pt or .onnx"]
DET --> FLT["filters<br/>conf + valid quad"]
FLT --> TRK["tracker<br/>IoU + center fallback<br/>1 verdict / vehicle"]
TRK --> RCT["rectify<br/>perspective crop"]
RCT --> OCR["ocr<br/>fast-plate-ocr<br/>+ K-frame vote"]
OCR --> VAL["validate_tr<br/>TR format + correction"]
VAL --> ANN["annotate"]
- detector — 4 corners + confidence; loads
.pt(dev/GPU) or.onnx(prod/CPU), one interface - filters — is it a plate? (confidence + valid quadrilateral)
- tracker — IoU + center-distance fallback, one verdict per vehicle, motion-direction evidence, best-K frames (bounded memory)
- rectify — 4 corners →
getPerspectiveTransform→ upright crop - ocr — fast-plate-ocr (ONNX) + K-frame character voting
- validate_tr — TR format + position-aware correction (international plates pass through)
- annotate — draw result (audit output)
Each module has a single responsibility; thresholds live in one config.yaml
(see config.example.yaml), no magic constants in code. Design rationale: DECISIONS.md.
| Metric | Value |
|---|---|
Turkish held-out recall@0.25 |
98.1% |
| CCPD corner acceptance (error ≤ 2% of plate width) | 75.0% |
| CCPD quad IoU ≥ 0.95 | 52.6% |
| Detection recall (CCPD val) | 100% |
Cropping quality is measured with normalized corner error and quad IoU, not
mAP (which is misleading for this task).
Full methodology, the model journey, and the root-cause ablation experiments: RESULTS.md. Real pipeline outputs (rectified crops + OCR): samples/.
Python · YOLO11-pose (Ultralytics) · ONNX Runtime · OpenCV · NumPy · fast-plate-ocr · PyODBC (operational layer, not in this edition).
Included: the computer-vision engine (angpr/ — detector, rectify, filters,
tracker, ocr, validate_tr, annotate, sources).
Excluded on purpose: trained weights, all datasets (CCPD is referenced, not
bundled — MIT, https://github.com/detectRecog/CCPD), field imagery, runs/ outputs,
the gate state-machine + database layer, credentials/IP addresses, and the
deployment package. This keeps the repository free of client data and secrets.
- CCPD (MIT) is used for pre-training — referenced, not redistributed here.
- Turkish-plate appearance is handled with self-generated synthetic composites (own drawing + CCPD quads), no third-party plate dataset redistributed.
- Runtime is fully local — no external API, no cloud inference.
Hamza Akkaymak — sole author and developer (design, model, and pipeline).