las-vegas-metro-rfdetr-nano
An RF-DETR Nano model that detects Las Vegas Metropolitan Police Department vehicles. Built for dashradar.app and meant to run on mobile devices, in the browser on WebGPU or on-device.

Files
| File | Precision | Size | Use |
|---|---|---|---|
onnx/model_fp16.onnx | fp16 weights, fp32 I/O | ~54 MB | Recommended, WebGPU (onnxruntime-web) |
onnx/model.onnx | fp32 | ~108 MB | Reference build |
weights.pt | fp32 | ~121 MB | Source checkpoint |
Both ONNX builds share one I/O signature and agree within 0.001 confidence on the same box, so the same code runs either one.
Input and output
input:[1, 3, 512, 512]float32 NCHW. Resolution is fixed.dets:[1, 100, 4]float32, boxes in cxcywh normalized 0..1.labels:[1, 100, 2]float32, raw logits. Apply sigmoid.
Three things to get right:
- Police vehicles are class index 1. The head is two classes wide and only one is trained. Index 0 is unused.
- Boxes are normalized to the center crop, not to the original frame. Map them back through the crop origin.
- Center-crop the input, don't squish it. Training images are center-cropped, so a squished full frame scores worse. The cost is field of view: a 16:9 frame loses about 44% of its width.
No NMS is baked in. Read the query count off the tensor instead of hardcoding
100. Each build carries its label map and release tag in ONNX metadata_props.
Usage
import numpy as np, onnxruntime as ort
from PIL import Image
MEAN = np.array([0.485, 0.456, 0.406], np.float32)
STD = np.array([0.229, 0.224, 0.225], np.float32)
THRESHOLD = ... # set from the CHANGELOG entry for the revision you pin
img = Image.open("input.jpg").convert("RGB")
W, H = img.size
side = min(W, H)
x0, y0 = (W - side) // 2, (H - side) // 2
square = img.crop((x0, y0, x0 + side, y0 + side)).resize((512, 512), Image.BILINEAR)
x = ((np.asarray(square, np.float32) / 255.0 - MEAN) / STD).transpose(2, 0, 1)[None]
sess = ort.InferenceSession("onnx/model_fp16.onnx", providers=["CPUExecutionProvider"])
dets, labels = sess.run(None, {sess.get_inputs()[0].name: x})
prob = 1 / (1 + np.exp(-labels[0]))
idx = np.argsort(-prob.reshape(-1))
query, cls, score = idx // 2, idx % 2, prob.reshape(-1)[idx]
cx, cy, bw, bh = dets[0][query].T
xyxy = np.stack([x0 + (cx - bw / 2) * side, y0 + (cy - bh / 2) * side,
x0 + (cx + bw / 2) * side, y0 + (cy + bh / 2) * side], axis=1)
for s, c, b in zip(score, cls, xyxy):
if s >= THRESHOLD and c == 1:
print(f"police vehicle {s:.2f} at {b.round().astype(int).tolist()}")
Thresholds
Thresholds do not carry across versions. Every release recalibrates the score distribution. The recommended threshold and the metrics behind it live in the CHANGELOG entry for each version, so read the entry for the revision you pin and re-tune when you move. Don't reuse a threshold read off Roboflow's hosted sweep, which serves a different query count than these builds.
Confidence tracks apparent size, not distance. The same vehicle scores above 0.9 in a tight native-resolution crop and far lower when a full frame is downscaled into 512x512. For distant vehicles use a tighter crop, not a lower threshold.
Intended use and limitations
Intended for detecting Las Vegas Metro police vehicles in realtime dashcam-style video for dashradar.app. It is trained on a specific fleet and livery, so it may not generalize to other agencies, regions, or unmarked vehicles. Do not rely on it for safety-critical decisions.
License
Apache 2.0.