Vehicle counting for Little Cottonwood Canyon (SR-210, Utah) from public UDOT traffic cameras, using RF-DETR running locally via Roboflow Inference.
Every 10 minutes, a cron pulls snapshots from 7 cameras between the SR-209 intersection and Alta, counts vehicles on-device (Apple Silicon GPU), and appends a time series — a canyon "busy-ness index" that feeds powder-day traffic logic (anyone who has sat in the red snake on a powder morning knows).
- Cameras: UDOT publishes ~1-minute snapshots at
https://www.udottraffic.utah.gov/map/Cctv/<id>. No API key needed; the developer API only adds metadata, not faster frames. - Model:
rfdetr-base(Apache 2.0), run locally — no cloud calls, no per-frame cost. COCO classes filtered to car/truck/bus/motorcycle. - Dedup: DETR-family models can emit the same physical vehicle under two
classes (car 0.40 + truck 0.46). Class-agnostic NMS
(
detections.with_nms(threshold=0.5, class_agnostic=True)) keeps one box per object — without it, counts run ~20% hot. Found by hand-counting a frame against the model output. - Guards: near-black frames (night) and dead feeds are logged as such rather than counted as zero traffic.
pip install inference supervision python-dotenv
echo "ROBOFLOW_API_KEY=<your key>" > .env
python canyon_watch.pyOutputs canyon_watch/readings.jsonl (one line per camera per poll, plus a
_canyon_total) and annotated snapshots per run.
Cron (every 10 minutes):
*/10 * * * * /path/to/python /path/to/canyon_watch.py >> canyon_watch/cron.log 2>&1
Native (non-Docker) inference on M-series Macs currently crashes with
AttributeError: module 'torch.mps' has no attribute 'current_device' —
see roboflow/inference#2757.
The script includes the one-line workaround.
