From d51c180fe6edb10487846df253bace92fa964959 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sun, 22 Feb 2026 15:14:45 -0800 Subject: [PATCH] Switch Frigate detection model from YOLO-NAS-S to YOLOv9-c (#246) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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 --- .dagger/src/blumeops_ci/main.py | 81 +++++++++++++++++++ argocd/manifests/frigate/configmap.yaml | 10 +-- .../feature-frigate-yolov9-model.feature.md | 1 + docs/reference/services/frigate.md | 4 +- mise-tasks/frigate-export-model | 55 +++++++++++++ 5 files changed, 144 insertions(+), 7 deletions(-) create mode 100644 docs/changelog.d/feature-frigate-yolov9-model.feature.md create mode 100755 mise-tasks/frigate-export-model diff --git a/.dagger/src/blumeops_ci/main.py b/.dagger/src/blumeops_ci/main.py index 3b72087..433faed 100644 --- a/.dagger/src/blumeops_ci/main.py +++ b/.dagger/src/blumeops_ci/main.py @@ -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" diff --git a/argocd/manifests/frigate/configmap.yaml b/argocd/manifests/frigate/configmap.yaml index 006b149..16bd4d8 100644 --- a/argocd/manifests/frigate/configmap.yaml +++ b/argocd/manifests/frigate/configmap.yaml @@ -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: diff --git a/docs/changelog.d/feature-frigate-yolov9-model.feature.md b/docs/changelog.d/feature-frigate-yolov9-model.feature.md new file mode 100644 index 0000000..5630b65 --- /dev/null +++ b/docs/changelog.d/feature-frigate-yolov9-model.feature.md @@ -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. diff --git a/docs/reference/services/frigate.md b/docs/reference/services/frigate.md index b5b597b..63cb652 100644 --- a/docs/reference/services/frigate.md +++ b/docs/reference/services/frigate.md @@ -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). diff --git a/mise-tasks/frigate-export-model b/mise-tasks/frigate-export-model new file mode 100755 index 0000000..f1cf828 --- /dev/null +++ b/mise-tasks/frigate-export-model @@ -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 " default="c" help="Model variant: s (small), c (compact), e (extra-large)" +#USAGE flag "--input-size " 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