RF-DETR-Large β Dutch License Plate Detector
A single-class license-plate detector fine-tuned from
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) andlabels(per-query scores) - License: Apache 2.0
Live demo
Try it in the Space: 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)
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
(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).