Switch Frigate detection model from YOLO-NAS-S to YOLOv9-c (#246)

## Summary
- Replace abandoned YOLO-NAS-S (320x320, `yolonas`) with YOLOv9-c (640x640, `yolo-generic`)
- YOLOv9-c benefits from CUDA Graphs in Frigate 0.17 on the RTX 4080
- Add `export_yolov9` Dagger pipeline and `frigate-export-model` mise task for reproducible model exports
- Model already deployed to `sifaka:/volume1/frigate/models/yolov9-c-640.onnx`

## Config changes
- `model_type: yolonas` → `yolo-generic`
- `input_dtype: int` → `float`
- `width/height: 320` → `640`
- `path:` → `yolov9-c-640.onnx`

## Deployment and Testing
- [ ] Merge and sync Frigate ArgoCD app: `argocd app sync frigate`
- [ ] Verify Frigate starts and detects objects at https://nvr.ops.eblu.me
- [ ] Confirm GPU inference via Frigate system metrics

Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/246
This commit is contained in:
Erich Blume 2026-02-22 15:14:45 -08:00
commit d51c180fe6
5 changed files with 144 additions and 7 deletions

View file

@ -152,6 +152,87 @@ class BlumeopsCi:
.file(f"/workspace/{flake_path}/flake.lock")
)
@function
async def export_yolov9(
self,
model_size: str = "c",
input_size: int = 640,
) -> dagger.File:
"""Export YOLOv9 pretrained weights to ONNX for Frigate NVR.
Downloads pretrained weights from the WongKinYiu/yolov9 repo and
exports to ONNX with onnx-simplifier. Use with Frigate's
`model_type: yolo-generic`.
Args:
model_size: Model variant: s (small), c (compact), e (extra-large).
input_size: Input resolution (width and height). 640 recommended.
"""
output_file = f"yolov9-{model_size}-{input_size}.onnx"
weights_url = (
"https://github.com/WongKinYiu/yolov9/releases/download/v0.1/"
f"yolov9-{model_size}-converted.pt"
)
# Patch torch.load to allow weights_only=False (required for
# YOLOv9 checkpoints that contain non-tensor objects).
patch_and_export = (
"set -e; "
"cd /yolov9 && "
"sed -i "
'"s/ckpt = torch.load(attempt_download(w),'
" map_location='cpu')/ckpt = torch.load(attempt_download(w),"
" map_location='cpu', weights_only=False)/g\""
" models/experimental.py && "
f"python3 export.py --weights ./weights.pt"
f" --imgsz {input_size} --simplify --include onnx && "
f"mv ./weights.onnx /output/{output_file}"
)
return await (
dag.container(platform=dagger.Platform("linux/amd64"))
.from_("python:3.11-slim")
.with_exec(["apt-get", "update", "-qq"])
.with_exec(
[
"apt-get",
"install",
"-y",
"-qq",
"git",
"libgl1",
"libglib2.0-0",
"cmake",
"build-essential",
]
)
.with_exec(
[
"git",
"clone",
"--depth=1",
"https://github.com/WongKinYiu/yolov9.git",
"/yolov9",
]
)
.with_exec(
[
"pip",
"install",
"--quiet",
"-r",
"/yolov9/requirements.txt",
"numpy<2",
"onnx>=1.18.0",
"onnxruntime",
"onnx-simplifier>=0.4.1",
"onnxscript",
]
)
.with_exec(["mkdir", "-p", "/output"])
.with_file("/yolov9/weights.pt", dag.http(weights_url))
.with_exec(["sh", "-c", patch_and_export])
.file(f"/output/{output_file}")
)
@function
async def flake_update(
self, src: dagger.Directory, flake_path: str = "nixos/ringtail"

View file

@ -63,12 +63,12 @@ data:
type: onnx
model:
model_type: yolonas
width: 320
height: 320
model_type: yolo-generic
width: 640
height: 640
input_tensor: nchw
input_dtype: int
path: /media/frigate/models/yolo_nas_s.onnx
input_dtype: float
path: /media/frigate/models/yolov9-c-640.onnx
labelmap_path: /labelmap/coco-80.txt
record:

View file

@ -0,0 +1 @@
Switch Frigate object detection from YOLO-NAS-S (320x320) to YOLOv9-c (640x640) with CUDA Graphs support, and add `frigate-export-model` Dagger pipeline + mise task for reproducible model exports.

View file

@ -1,6 +1,6 @@
---
title: Frigate
modified: 2026-02-19
modified: 2026-02-22
tags:
- service
- surveillance
@ -47,7 +47,7 @@ Camera credentials are stored in 1Password and synced via [[external-secrets]] t
## Detection
Object detection runs on [[ringtail]]'s RTX 4080 via the ONNX detector with CUDA execution provider. The model is YOLO-NAS-S (`yolo_nas_s.onnx`). The previous Apple Silicon Detector on [[indri]] has been retired.
Object detection runs on [[ringtail]]'s RTX 4080 via the ONNX detector with CUDA execution provider (TensorRT). The model is YOLOv9-c at 640x640 (`yolov9-c-640.onnx`, `model_type: yolo-generic`), which benefits from CUDA Graphs in Frigate 0.17. To re-export or change model size, use `mise run frigate-export-model`.
Two zones are configured: `driveway_entrance` (triggers review alerts for person/car) and `driveway` (triggers review detections).

55
mise-tasks/frigate-export-model Executable file
View file

@ -0,0 +1,55 @@
#!/usr/bin/env bash
#MISE description="Export YOLOv9 model weights to ONNX for Frigate NVR via Dagger"
#USAGE flag "--model-size <size>" default="c" help="Model variant: s (small), c (compact), e (extra-large)"
#USAGE flag "--input-size <pixels>" default="640" help="Input resolution (width=height)"
#USAGE flag "--deploy" help="Copy exported model to sifaka NAS frigate share"
set -euo pipefail
MODEL_SIZE="${usage_model_size:-c}"
INPUT_SIZE="${usage_input_size:-640}"
DEPLOY="${usage_deploy:-false}"
OUTPUT_FILE="yolov9-${MODEL_SIZE}-${INPUT_SIZE}.onnx"
echo "Exporting YOLOv9-${MODEL_SIZE} (${INPUT_SIZE}x${INPUT_SIZE}) via Dagger..."
echo ""
dagger call export-yolov-9 \
--model-size="$MODEL_SIZE" \
--input-size="$INPUT_SIZE" \
export --path="$OUTPUT_FILE"
SIZE=$(du -h "$OUTPUT_FILE" | cut -f1)
echo ""
echo "Exported: ${OUTPUT_FILE} (${SIZE})"
if [[ "$DEPLOY" == "true" ]]; then
DEST="sifaka:/volume1/frigate/models/${OUTPUT_FILE}"
echo "Copying to ${DEST}..."
scp -O "$OUTPUT_FILE" "$DEST"
echo "Deployed."
echo ""
echo "Update argocd/manifests/frigate/configmap.yaml:"
echo " model:"
echo " model_type: yolo-generic"
echo " width: ${INPUT_SIZE}"
echo " height: ${INPUT_SIZE}"
echo " input_tensor: nchw"
echo " input_dtype: float"
echo " path: /media/frigate/models/${OUTPUT_FILE}"
echo " labelmap_path: /labelmap/coco-80.txt"
else
echo ""
echo "To deploy to Frigate NAS:"
echo " scp ${OUTPUT_FILE} sifaka:/volume1/frigate/models/"
echo ""
echo "Then update argocd/manifests/frigate/configmap.yaml:"
echo " model:"
echo " model_type: yolo-generic"
echo " width: ${INPUT_SIZE}"
echo " height: ${INPUT_SIZE}"
echo " input_tensor: nchw"
echo " input_dtype: float"
echo " path: /media/frigate/models/${OUTPUT_FILE}"
echo " labelmap_path: /labelmap/coco-80.txt"
fi