--- license: apache-2.0 library_name: rfdetr pipeline_tag: object-detection base_model: roboflow/rf-detr-large datasets: - Rickkosse/license-plates - Rickkosse/kenteken-synthetic-dataset tags: - object-detection - license-plate-detection - alpr - rf-detr - onnx - netherlands --- # RF-DETR-Large — Dutch License Plate Detector A single-class license-plate **detector** fine-tuned from [`roboflow/rf-detr-large`](https://huggingface.co/roboflow/rf-detr-large) on Dutch + synthetic plate data, exported to ONNX (fixed 768×768 input). - **Task:** license-plate detection (one class: `license_plate`) - **Base model:** RF-DETR-Large (Apache 2.0) - **Input:** `[1, 3, 768, 768]` RGB, ImageNet-normalized - **Outputs:** `dets` (boxes, cx/cy/w/h normalized) and `labels` (per-query scores) - **License:** Apache 2.0 ## Live demo Try it in the Space: **[Rickkosse/license-plate-detector](https://huggingface.co/spaces/Rickkosse/license-plate-detector)** (detection + `fast-plate-ocr` reading, with an "unreadable" gate for low-confidence plates). ## Intended use & limitations Detects Dutch-style plates well when they are reasonably large and frontal. Small, distant, or strongly angled plates in wide scenes are harder (a known data-coverage limitation). This is a **prototype**: training data was cc-by-nc / synthetic, so it is not a certified production model. For production, retrain on rights-clean, hand-verified data — the pipeline is unchanged. ## Usage (ONNX Runtime) ```python import numpy as np, onnxruntime as ort from PIL import Image RES = 768 MEAN = np.array([0.485, 0.456, 0.406], np.float32) STD = np.array([0.229, 0.224, 0.225], np.float32) sess = ort.InferenceSession("rfdetr-large.onnx", providers=ort.get_available_providers()) in_name = sess.get_inputs()[0].name def detect(path, conf=0.5): pil = Image.open(path).convert("RGB") w0, h0 = pil.size img = pil.resize((RES, RES), Image.BILINEAR) x = (np.asarray(img, np.float32) / 255.0 - MEAN) / STD x = np.ascontiguousarray(x.transpose(2, 0, 1)[None], np.float32) dets, labels = sess.run(["dets", "labels"], {in_name: x}) d = dets[0] if dets.ndim == 3 else dets l = labels[0] if labels.ndim == 3 else labels scores = l.max(axis=-1) if l.ndim > 1 else l if scores.max() > 1 or scores.min() < 0: scores = 1 / (1 + np.exp(-scores)) # logits -> prob out = [] for (cx, cy, bw, bh), s in zip(d, scores): if s < conf: continue out.append((max(0,(cx-bw/2)*w0), max(0,(cy-bh/2)*h0), min(w0,(cx+bw/2)*w0), min(h0,(cy+bh/2)*h0), float(s))) return out print(detect("car.jpg")) # [(x1, y1, x2, y2, score), ...] in pixels ``` ## Reading plates (optional, two-stage ALPR) Pair detection with [`fast-plate-ocr`](https://github.com/ankandrew/fast-plate-ocr) (MIT): crop each detected box and read it. The OCR expects a **grayscale** crop with a channel axis `(H, W, 1)`. See the Space `app.py` for a full example, including the confidence gate that flags unreadable plates instead of guessing. ## Citation Built on RF-DETR by Roboflow. OCR by `fast-plate-ocr` (ankandrew).