Replacing Frigate’s default SSD MobileNet detector with YOLOv9t (tiny) running on the Intel N97’s integrated GPU via OpenVINO. Covers model export, correct Frigate config, and a critical gotcha that causes 100% false positives if you get it wrong.
Setup#
- Server: Intel N97 (Debian 13), 8 camera streams
- Frigate: 0.17, running in Docker
- Detector: OpenVINO GPU (
/dev/dri/renderD128) - Previous model: SSD MobileNet v2 (built-in, 300×300)
- New model: YOLOv9t ONNX (320×320, 8.3 MB)
Why YOLOv9t?#
The default SSD MobileNet v2 bundled with Frigate’s OpenVINO image is fast and lightweight, but accuracy suffers on partially occluded objects and objects at the edges of the frame. YOLOv9t (tiny) offers meaningfully better detection quality with a similar computational footprint — at 320×320 input and ~18ms inference on the N97 iGPU, it handles 8 concurrent camera streams comfortably.
Exporting the Model#
YOLOv9t isn’t bundled with Frigate, so you export it yourself using the Ultralytics Docker image. Run this on the host:
| |
This downloads the pretrained YOLOv9t weights, exports to ONNX with 320×320 input size, and saves yolov9t.onnx (8.3 MB) into your Frigate config directory — which is mounted at /config inside the container.
Frigate Config#
The /config mount makes the model available at /config/model_cache/yolov9t.onnx. The labelmap for YOLO models (80-class COCO) is already bundled in the Frigate container at /labelmap/coco-80.txt.
| |
The Critical Gotcha#
Do not use model_type: yolov9. It looks like the right value, but it’s invalid in Frigate 0.17. The valid options are: dfine, rfdetr, ssd, yolox, yolonas, yolo-generic.
Using yolov9 triggers a config validation error and puts Frigate into safe mode, where it silently falls back to a CPU-based TFLite model. That model produces near-100% confidence scores on everything — trees detected as people, empty driveways full of cars. It looks like the new model is wildly broken, but the new model hasn’t even loaded.
The correct value for any Ultralytics YOLO model (v8, v9, etc.) is yolo-generic.
Output Tensor Format#
The exported ONNX model outputs shape [1, 84, 2100]:
- 84 channels = 4 bbox coordinates (xywh, pixel space) + 80 class scores
- 2100 = number of anchor points at 320×320
Frigate’s yolo-generic handler transposes and post-processes this correctly, including coordinate normalization and NMS. No custom post-processing needed.
Inference Speed#
After switching, the OpenVINO detector runs at ~18ms per inference — well within budget for 8 camera streams at 1–5 FPS each.
| |
API Score Display Bug#
In Frigate 0.17, the top_score field at the top level of the events API returns null for events detected with custom models. The actual scores are nested under data.score and data.top_score. This is a display bug — detection is working correctly, and the real confidence values (e.g. 0.75–0.88 for parked cars) are there if you look at the full event JSON.
Thresholds#
The previous per-camera min_score and threshold overrides were tuned for SSD’s confidence distribution. With YOLOv9t being more accurate overall, we reset everything to Frigate’s defaults (min_score: 0.5, threshold: 0.7) and kept only the per-camera masks. Adjust from there based on observed behavior.