Detection tuning
Out of the box, Fregata runs a bundled YOLOv9-tiny model at 320×320 on the Apple Neural Engine. This is fast (~1 ms per frame on Apple M4), private (it’s an ONNX file inside the app bundle, no cloud), and “good enough” for porches, driveways, and most outdoor uses. This page covers everything you’d reach for when “good enough” isn’t good enough.
What’s running, by default
Section titled “What’s running, by default”The defaults that ship with the app:
detectors: coreml: type: coreml inference_backend: ane # "ane" | "gpu"
model: model_type: yolo-generic width: 320 height: 320 input_tensor: nchw input_pixel_format: rgb input_dtype: floatTranslated:
type: coreml— the only detector type that ships in Fregata. No EdgeTPU, TensorRT, OpenVINO, ROCm, RKNN, Hailo. Those don’t apply on macOS. (For the full list of what’s removed, see Fregata vs Frigate.)inference_backend: ane— route inference through ONNX Runtime’s CoreML execution provider preferring the ANE. Fall through to the GPU or CPU only when ops aren’t supported. Switch togputo force GPU inference.model_type: yolo-generic— generic YOLO postprocessor. Other supported types:yolox,yolonas,dfine,rfdetr.
Detection resolution and FPS
Section titled “Detection resolution and FPS”Two detect: config keys control what frames the detector
sees — separately from what the camera streams and what
Fregata records. They’re worth setting explicitly even though
they have defaults.
detect.width and detect.height — match your main stream
Section titled “detect.width and detect.height — match your main stream”By default Frigate probes the first frame of your camera’s stream and uses that resolution for detection — and Fregata runs detection on the main, full-resolution stream, not a low-resolution sub-stream the way other NVRs typically do. The ANE has the headroom for it, and detection on the full frame catches small or distant objects that a 1280 × 720 sub-stream would pixel-soup before the detector sees them.
If the auto-probe doesn’t pick up your camera’s resolution
(some firmwares lie about frame size, some streams take a long
time to publish their first I-frame), Frigate falls back to
1280 × 720. Set detect.width and detect.height
explicitly to your camera’s main-stream resolution to avoid
ever hitting that fallback:
cameras: driveway: detect: width: 3840 # camera's main-stream width height: 2160 # camera's main-stream height fps: 10Find your camera’s actual main-stream resolution in the
camera’s own web UI (Reolink, Amcrest, Dahua, etc. all surface
it under stream settings) or via ffprobe:
ffprobe -v error -select_streams v:0 \ -show_entries stream=width,height \ -of csv=p=0 rtsp://user:pass@camera-ip/streamThe part of the frame with motion is automatically cropped to the detector model’s
input size (320 × 320 for the bundled YOLOv9-tiny) before
inference — those are the model.width and model.height
keys above, and changing them is a different operation (see
Bringing your own model).
detect.fps — don’t go above 10
Section titled “detect.fps — don’t go above 10”detect.fps controls how many frames per second per camera
the detector processes. It’s independent of the camera’s own
stream FPS and of the recording stream (which always captures
at the camera’s native rate for playback).
Don’t set detect.fps above 10, even though Fregata’s ANE
has the headroom. Reasons:
- No detection-accuracy benefit. Object-detection is per-frame. Detecting at 30 FPS doesn’t catch more objects than detecting at 10 FPS — it just classifies the same objects 3× as many times.
- Frigate’s config validator warns above 10. Every camera
with
detect.fps > 10(andtypeother thanlpr) triggers"Recommended value is 5"in the Frigate log on startup. Going higher works but the validator is right: the work is wasted. - It’s real ANE cycles and heat that you don’t get back.
5 FPS is plenty for most scenes; 10 FPS makes sense for fast-moving subjects (cars driving by a busy street, other fast moving objects). Pick the lowest value that catches what you need.
ANE vs GPU — which should I use?
Section titled “ANE vs GPU — which should I use?”The honest answer: leave it on ane and don’t think about it
again for the bundled model and the typical Frigate+ models. The
ANE is the faster path on Apple Silicon for INT8 / FP16 YOLO-shaped
networks, and the runtime falls back automatically when an op isn’t
supported. The GPU will be slower per frame, use more electricity, and generate more heat.
Switch to inference_backend: gpu when:
- You’re running a model the CoreML compiler can’t lower onto the ANE (you’ll see this on first warmup as a CPU-tier latency).
- You want to A/B test latency or thermals on a specific machine.
- You’re hitting a known ANE bug on a specific macOS build and need to ship a fix today.
On an M4, ANE inference for YOLOv9-tiny at 320×320 is ~1 ms; GPU is 4–8 ms; CPU is 40–80 ms. The CPU path shouldn’t be used — never run a real install on it.
On an M4, ANE inference for YOLOv9-small at 320×320, such as a Frigate+ model is ~2ms
Masks blank out parts of the frame before motion detection runs —
they’re narrow tools for fine-tuning, not for hiding an area from
Frigate. Use a motion mask for areas that obviously aren’t an object of
interest: tree branches, the camera timestamp, a flag that waves all
day. Use objects.filters.<class>.mask to suppress detections of one
class only — handy for “ignore the person on the TV or reflected in the
window” scenarios.
Don’t reach for a mask to hide an area you just don’t want alerts
about — your neighbor’s front porch, the sidewalk, a public street.
Over-masking degrades tracking: an object that walks from an unmasked
area into a masked one disappears and gets picked up as a “new” object
if it re-emerges, which is exactly the kind of false negative you don’t
want. The right tool for “stop detecting/tracking activity here, but
don’t alert on it” is a zone
combined with review.alerts.required_zones (and/or
review.detections.required_zones) — Frigate keeps tracking the object,
it just won’t create a review item until the object enters a required
zone.
The web UI has a polygon editor under Settings → Masks & Zones. Draw, save, repeat.
The full reference (coordinate format, multi-polygon syntax, interactions with motion-detection sensitivity) is upstream — see Frigate’s masks documentation. The schema works on macOS unchanged.
Zones are named polygons. They don’t change whether an object is detected — they change what events that detection creates and what gets sent to MQTT or Home Assistant.
cameras: driveway: zones: driveway_apron: coordinates: 0,720,400,720,500,500,0,500 objects: - car - truck mailbox: coordinates: 1100,400,1280,400,1280,500,1100,500A car event in driveway_apron will fire as a zone-entry event.
A person walking past the mailbox won’t, because mailbox doesn’t
list person.
This is the same configuration shape upstream Frigate uses; their zones documentation covers more advanced shapes.
Per-object thresholds
Section titled “Per-object thresholds”The bundled YOLO model returns a confidence score for every box. A sensible default is to ignore anything under ~0.5, and to be stricter (~0.7) for classes that are easy to confuse with similar objects:
objects: track: - person - car - dog filters: person: threshold: 0.7 min_area: 1500 dog: threshold: 0.65min_area is in pixels; it kills tiny detections — usually
distant people that hover at low confidence.
Bringing your own model
Section titled “Bringing your own model”Fregata supports any ONNX model that ONNX Runtime’s CoreML provider can run. The most common reasons to swap:
- You bought a Frigate+ subscription and want to use your custom-trained model. See below.
- You’ve trained YOLOv9 / YOLOv10 / RT-DETR yourself and have
an
onnxexport. Drop it in. - You want classes the bundled model doesn’t have — e.g. bicycles, packages, license plates, drones.
Place the ONNX file somewhere persistent (the conventional spot is
~/Fregata/config/models/my_model.onnx) and point the config at it:
detectors: coreml: type: coreml inference_backend: ane
model: path: /Users/<you>/Fregata/config/models/my_model.onnx model_type: yolo-generic # or yolox, yolonas, dfine, rfdetr, ssd width: 320 height: 320 input_tensor: nchw input_pixel_format: rgb input_dtype: float labelmap_path: /Users/<you>/Fregata/config/models/labels.txtThe labelmap_path is a plain text file with one class label per
line, matching the model’s output indices. If you skip it, Fregata
falls back to the COCO label map that ships with the app.
Frigate+
Section titled “Frigate+”Frigate+ models work natively. You’ll see them on your account
dashboard with a plus://... identifier; once you’ve added your
Frigate+ API key, your config.yml will update automatically to
point at it via model.path:
detectors: coreml: type: coreml
model: path: plus://abc123def456Fregata fetches the model into ~/Fregata/config/model_cache/ on
first launch and validates the architecture. It checks the model’s
declared supportedDetectors field for onnx; if your Frigate+ model is older than the
onnx-support cutover, retrain on the dashboard for free.
Verifying a model swap worked
Section titled “Verifying a model swap worked”After a config reload (or Restart Frigate from the tray):
- Watch the Detector row in the tray. The first inference logs
a warmup-tier classification —
ANE,GPU, orCPU. CPU after a model swap usually means an unsupported op. - Open the web UI’s System tab. The “Detector inference time” chart should plateau within a few seconds at the same tier.
- Send an obvious test through (walk past the camera). If the bounding box is centered on the right object, you’re done.
If the inference time has jumped from ~2 ms to 50+ ms, you’ve
fallen back to CPU. Either set inference_backend: gpu or
re-export the model with op set ≤ 17 — see
Troubleshooting.