Machine Learning Hooks
Note
This page covers the ML detection pipeline, which is required for both
Path 1 (detection + optional push) and Path 2 (full ES).
For installation instructions, see Installation.
The hooks use pyzmNg v2
for detection. Make sure you have pyzm installed before proceeding.
Important
Setting up hooks requires familiarity with the Linux command line, Python
package management, and basic troubleshooting. Support is not provided for
general environment issues (e.g. missing pip3, cv2 import errors).
The hooks are provided as-is.
Key Features
Detection: objects, faces
Recognition: faces
License plate recognition (ALPR) via cloud services
Audio recognition: bird species identification via BirdNET
Platforms:
CPU (object, face detection, face recognition),
GPU (object, face detection, face recognition),
EdgeTPU (object, face detection)
Machine learning can run locally or remotely via
pyzm.serve
Requirements
Python 3.10+
OpenCV 4.13+ (for the default ONNX YOLO models)
pyzmNg v2 (
pip install pyzm)
How it works
The main detection script is zm_detect.py. It reads objectconfig.yml,
connects to ZoneMinder, downloads event frames, runs the ML detection pipeline
(via pyzmNg’s Detector API), and returns results.
Path 1: Detection + optional push (no ES)
Requires ZM 1.38.1+. ZoneMinder can call zm_detect.py directly via its
Event Start Command feature — no Event Server needed.
Configure per monitor in ZM: go to the monitor’s Config -> Recording tab and set:
Event Start Command:
/var/lib/zmeventnotification/bin/zm_detect.py -e %EID% -m %MID% -r "%EC%" -n --pyzm-debug
(
-cdefaults to/etc/zm/objectconfig.yml; pass it explicitly only if your config is elsewhere.)
ZM substitutes %EID%, %MID%, %EC% tokens at runtime when an event starts.
Detection results are:
Written to the ZM event notes (
-nflag)Saved as
objdetect.jpgandobjects.jsonin the event folder (ifwrite_image_to_zm: "yes"inobjectconfig.yml)Optionally tagged in ZM (if
tag_detected_objects: "yes", requires ZM >= 1.37.44)Optionally sent as push notifications via FCM (if
push.enabled: "yes"inobjectconfig.yml, requires ZM >= 1.39.2 with the Notifications API)
What you get: object/face/ALPR/audio detection, annotated images, detection notes in ZM,
local or remote ML via pyzm.serve, and (optionally) FCM push notifications to
registered devices.
What you don’t get: WebSocket notifications, MQTT, notification rules/muting, or the ES control interface.
Note
Push notifications in Path 1 require ZoneMinder 1.39.2+ (which adds the
Notifications REST API for token storage). Devices register their FCM tokens
via the ZM API; zm_detect reads them via pyzmNg and sends push notifications
through an FCM cloud function proxy after detection. See the push section
in objectconfig.yml for configuration.
To set up Path 1, you only need to:
Install pyzmNg and the hooks (see Path 1: Detection + Optional Push (no ES))
Edit
/etc/zm/objectconfig.ymlwith your ZM portal credentials and desired modelsSet the Event Start Command in the monitor’s Config -> Recording tab as shown above
Optionally, set Event End Command (same tab) to a similar invocation if you want end-of-event processing
Optionally, enable push notifications by configuring the
pushsection inobjectconfig.yml(see push_config)
Path 2: Full Event Server
The ES is a Perl daemon that monitors ZoneMinder’s shared memory for new events, invokes the ML hooks, and handles push notifications, WebSockets, MQTT, rules, and more.
When an event occurs, the ES invokes zm_event_start.sh, which calls zm_detect.py.
Based on the detection result and your notification settings, the ES sends alerts via
FCM (iOS/Android push), WebSockets, MQTT, and/or third-party push APIs.
What you get (in addition to Path 1): push notifications to zmNinjaNG and other
FCM clients, WebSocket notifications, MQTT publishing, notification rules (time-based
muting, per-monitor controls), per-device monitor filtering via tokens.txt, and the
ES control interface.
To set up Path 2:
Install the ES and its Perl dependencies (see Path 2: Full Event Server)
Install pyzmNg and the hooks (see Path 1: Detection + Optional Push (no ES))
Edit
/etc/zm/zmeventnotification.ymland/etc/zm/objectconfig.ymlEnable
OPT_USE_EVENTNOTIFICATIONin ZMOptions -> Systems
See Key Principles — How Detection and Notifications Work for a detailed walkthrough of how the ES processes events.
Note
Do not configure both EventStartCommand (Path 1) and the ES hook (Path 2)
for the same monitors — you would end up running detection twice on every event.
Manual testing
Regardless of which path you use, you can always test detection manually:
# Test with a ZM event
sudo -u www-data /var/lib/zmeventnotification/bin/zm_detect.py \
--eventid <eid> --monitorid <mid> --debug
# Test with a local image file (no ZM event needed)
sudo -u www-data /var/lib/zmeventnotification/bin/zm_detect.py \
--file /path/to/image.jpg --debug
Testing push notifications (Direct mode):
If you have push.enabled: "yes" in objectconfig.yml and tokens registered
in the Notifications table, you can test push delivery from the command line.
Use --file with --eventid and --monitorid to trigger push without a live
event. The --fakeit flag overrides detection results so you don’t need an image
that actually matches your detection pattern:
sudo -u www-data /var/lib/zmeventnotification/bin/zm_detect.py \
--file /path/to/any/image.jpg --eventid <eid> --monitorid <mid> \
--debug --fakeit "person"
Replace <eid> with a real event ID (so the notification links to a viewable event)
and <mid> with the monitor ID. Registered devices should receive a push notification
within a few seconds. Check the debug output for push: log lines to verify delivery.
(--config defaults to /etc/zm/objectconfig.yml and can be omitted if your config is at the standard path.)
Post install steps
Make sure you edit your installed
objectconfig.ymlto the right settings. You MUST change thegeneralsection for your own portal.If you use
zm_event_start.sh(Path 2), make sure theCONFIG_FILEvariable in the script matches your config location. When callingzm_detect.pydirectly (Path 1),-cdefaults to/etc/zm/objectconfig.yml.
Test operation
You can test detection directly with zm_detect.py (no need to go through the shell wrapper):
sudo -u www-data /var/lib/zmeventnotification/bin/zm_detect.py \
--eventid <eid> --monitorid <mid> --debug
Replace <eid> with an actual event ID from your ZM console. The <mid> is the monitor ID
(optional — if specified, monitor-specific settings from objectconfig.yml will be used).
You can also test with a local image file instead of a ZM event:
sudo -u www-data /var/lib/zmeventnotification/bin/zm_detect.py \
--file /path/to/test.jpg --debug
--config defaults to /etc/zm/objectconfig.yml. Pass it explicitly only if your config is elsewhere.
If using the ES hook mode, you can also test the full shell wrapper:
sudo -u www-data /var/lib/zmeventnotification/bin/zm_event_start.sh <eid> <mid>
If it doesn’t work, see Machine Learning Hooks FAQ for debugging steps.
Upgrading
To upgrade at a later stage, see How do I safely upgrade zmeventnotificationNg to new versions?.
Which models should I use?
YOLO ONNX (Recommended): The default and recommended model. Uses ONNX format via OpenCV’s DNN module. Both YOLOv11 and YOLOv26 are supported (both require OpenCV 4.13+). Multiple sizes are available for each:
n(nano),s(small),m(medium),l(large) — smaller models are faster, larger models are more accurate. The default isyolo11n(nano) which provides a good balance.YOLOv4: Still supported via Darknet weights. Requires OpenCV 4.4+.
Google Coral Edge TPU: Supported for both object detection and face detection. See install instructions above.
YOLOv3 / Tiny YOLOv3 / Tiny YOLOv4: Still available but no longer installed by default. Set the appropriate
INSTALL_*flag toyesduring install if you need them.BirdNET audio recognition: Identifies 6500+ bird species from audio in ZM events. Install via the installer with
--install-birdnet(orINSTALL_BIRDNET=yes), or manually:/opt/zoneminder/venv/bin/pip install birdnet-analyzerFor face recognition, use
face_model: cnnfor more accuracy andface_model: hogfor better speed
Understanding detection configuration
You can chain arbitrary detection types (object, face, alpr, audio) and multiple models within
each type. The detection pipeline is configured through two key structures in objectconfig.yml:
ml_sequence— specifies the sequence of ML detection stepsstream_sequence— specifies frame selection and retry preferences
Note
All configuration is now in YAML format in objectconfig.yml. The old {{variable}}
template substitution syntax is no longer supported. All values must be specified directly
in the YAML file. The use_sequence flag no longer exists — the sequence structures are
always used.
The only substitution supported is ${base_data_path} which is replaced with the value from
general.base_data_path.
Per-monitor overrides
If you want to change ml_sequence or stream_sequence on a per monitor basis, you can do so
under the monitors section. You can override the entire structure or just parts of it:
monitors:
3:
ml_sequence:
general:
model_sequence: "object,face"
object:
general:
pattern: "(person|car)"
7:
ml_sequence:
general:
model_sequence: "object,alpr"
Per-monitor zones
You can define detection zones per monitor. Each zone specifies a polygon region and optionally
a detection_pattern (regex of labels to look for in that zone) and an ignore_pattern
(regex of labels to suppress even if they match detection_pattern).
monitors:
999:
zones:
my_driveway:
coords: "306,356 1003,341 1074,683 154,715"
detection_pattern: "(person|car)"
ignore_pattern: "(car|truck)"
front_porch:
coords: "0,0 200,300 700,900"
coords— polygon coordinates as"x1,y1 x2,y2 x3,y3 ..."detection_pattern— regex for which labels to accept in this zone (optional; if omitted, all labels match)ignore_pattern— regex for labels to suppress in this zone even ifdetection_patternallows them (optional). Useful for excluding parked cars or other stationary objects from a specific area.
You can also import zones from ZoneMinder instead of defining them manually:
general:
import_zm_zones: "yes"
only_triggered_zm_zones: "no"
Understanding ml_sequence
The ml_sequence structure lies in the ml section of objectconfig.yml.
At a high level, this is how it is structured:
ml:
ml_sequence:
general:
model_sequence: "<comma separated detection types>"
<detection_type>:
general:
pattern: "<pattern>"
same_model_sequence_strategy: "<strategy>"
sequence:
- name: "Model name"
enabled: "yes"
# ... model-specific parameters ...
- name: "Another model"
enabled: "yes"
# ... model-specific parameters ...
Here is a concrete example from the default objectconfig.yml:
ml:
ml_sequence:
general:
model_sequence: "object,face,alpr,audio"
object:
general:
pattern: "(person|car|motorbike|bus|truck|boat)"
same_model_sequence_strategy: first
sequence:
- name: YOLO ONNX
enabled: "yes"
object_weights: "${base_data_path}/models/ultralytics/yolo11n.onnx"
object_min_confidence: 0.3
object_framework: opencv
object_processor: gpu
face:
general:
pattern: ".*"
same_model_sequence_strategy: union
sequence:
- name: DLIB face recognition
enabled: "yes"
face_detection_framework: dlib
known_images_path: "${base_data_path}/known_faces"
face_model: cnn
audio:
general:
pattern: ".*"
same_model_sequence_strategy: first
sequence:
- name: BirdNET
enabled: "yes"
audio_framework: birdnet
birdnet_min_conf: 0.5
Explanation:
The
generalsection at the top level specifies characteristics that apply to all elements inside the structure.model_sequencedictates the detection types (comma separated). Exampleobject,face,alpr,audiowill first run object detection, then face, then alpr, then audio
For each detection type in
model_sequence, you specify model configurations in thesequencelist. Each entry in the sequence is a model configuration with anameandenabledflag.Note: All
ml_sequencesettings (pattern, zones, past-detection filtering, etc.) work identically whether detection runs locally or via a remotepyzm.serveserver. The remote server is a pure inference engine — all filtering is applied client-side using yourobjectconfig.yml.
Leveraging same_model_sequence_strategy and frame_strategy effectively
When using model chaining, these attributes control how aggressively the pipeline searches for matches.
same_model_sequence_strategy is part of ml_sequence with the following possible values:
first- When detecting objects, if there are multiple fallbacks, break out the moment we get a matchusing any object detection library (Default)
most- run through all libraries, select one that has most object matches
most_unique- run through all libraries, select one that has most unique object matches
union- run through all libraries, combine all detections from every variant into one merged list. Useful when you have multiple models that detect different classes (e.g. a base YOLO model and a fine-tuned model) and want to combine their results
frame_strategy is part of ml_sequence.general with the following possible values:
‘most_models’: Match the frame that has matched most models (does not include same model alternatives) (Default)
‘first’: Stop at first match
‘first_new’: Like
first, but only counts detections that pass past-detection filtering (i.e. genuinely new objects, not parked cars already detected in a prior run)‘most’: Match the frame that has the highest number of detected objects
‘most_unique’: Match the frame that has the highest number of unique detected objects
When two frames tie on the primary metric (e.g. same number of detections), the frame with the higher total confidence sum wins.
A proper example:
Take a look at this article for a walkthrough.
All options:
ml_sequence supports various other attributes. See the
pyzmNg DetectorConfig documentation
for the full list of supported keys (match_past_detections, past_det_max_diff_area,
aliases, max_detection_size, etc.).
Understanding stream_sequence
The stream_sequence structure lies in the ml section of objectconfig.yml.
At a high level, this is how it is structured:
ml:
stream_sequence:
frame_set: "snapshot,alarm"
resize: 800
contig_frames_before_error: 5
max_attempts: 3
sleep_between_attempts: 4
Explanation:
frame_setdefines the set of frames it should use for analysis (comma separated)resize: resize frames to this width in pixels before detection. Omit to use original resolution.contig_frames_before_error: How many contiguous errors should occur before giving up on the series of framesmax_attempts: How many times to try each frame (before counting it as an error in thecontig_frames_before_errorcount)sleep_between_attempts: When an error is encountered, how many seconds to wait for retrying
A proper example:
Take a look at this article for a walkthrough.
All options:
stream_sequence supports various other attributes. See the
pyzmNg StreamConfig documentation
for the full list (max_frames, start_frame, frame_skip, save_frames, etc.).
How ml_sequence and stream_sequence work together
The combined logic works as follows:
for each frame in stream sequence:
perform stream_sequence actions on each frame
for each model_sequence in ml_options:
if detected, use frame_strategy (in ml_sequence.general) to decide if we should try other model sequences
perform general actions:
for each model_configuration in ml_options.sequence:
detect()
if detected, use same_model_sequence_strategy to decide if we should try other model configurations
Using the remote ML detection server (pyzm.serve)
Note
pyzm.serve replaces the legacy mlapi server. It is built into pyzmNg itself,
uses the same Detector API, and requires no separate configuration file. The old
mlapiconfig.ini is no longer needed.
Server setup (GPU box):
pip install pyzm[serve]
# Basic usage
python -m pyzm.serve --models yolo11s --port 5000
# With authentication
python -m pyzm.serve --models yolo11s --port 5000 \
--auth --auth-user admin --auth-password secret
# Multiple models, GPU inference
python -m pyzm.serve --models yolo11s yolo26s --port 5000 --processor gpu
Client setup (objectconfig.yml on the ZM box):
remote:
ml_gateway: "http://192.168.1.183:5000"
ml_gateway_mode: "image" # "image" (default) or "url"
ml_fallback_local: "yes"
ml_user: "!ML_USER"
ml_password: "!ML_PASSWORD"
ml_timeout: 60
When ml_gateway is set, zm_detect.py creates the Detector in remote mode.
The server keeps models loaded in memory so subsequent requests skip the expensive
model-load step.
If the remote server is unreachable and ml_fallback_local is yes, detection
falls back to running locally on the ZM box.
All settings (ml_sequence, stream_sequence, monitor overrides, zones,
image writing, etc.) stay in objectconfig.yml — there is no second config file to manage.
The remote server is a pure inference engine that only needs models and a processor setting;
all filtering (pattern, zones, size, past-detection dedup) is applied client-side by the
Detector using your local config.
Two detection modes:
- Image mode (
ml_gateway_mode: "image", default) Frame extraction happens locally on the ZM box, then each frame is JPEG-encoded and uploaded to the server’s
/detectendpoint. This works universally but transfers every frame through the ZM box.- URL mode (
ml_gateway_mode: "url") The ZM box sends frame URLs (e.g.
https://zm/index.php?view=image&eid=123&fid=snapshot) and ZM auth credentials to the server’s/detect_urlsendpoint. The server fetches images directly from ZoneMinder and runs detection. This is more efficient when the GPU box has fast/direct network access to ZM, since frames don’t have to pass through the ZM box as an intermediary.To use URL mode, the server must be able to reach your ZM web portal over HTTP/HTTPS.
Server endpoints:
GET /health— returns{"status": "ok", "models_loaded": true}POST /detect— (image mode) accepts multipartfile(JPEG), returns raw (unfiltered) detectionsPOST /detect_urls— (URL mode) accepts JSON with frame URLs and auth token, returns per-frame raw detectionsPOST /login— (auth mode only) accepts{"username": ..., "password": ...}, returns JWT token
The server returns raw detection results without any filtering. Pattern matching, zone
filtering, size filtering, and past-detection deduplication are all applied client-side
by the Detector class using your objectconfig.yml settings. This means your
configuration works identically whether running locally or remotely.
Here is a part of my config, for example:
general:
import_zm_zones: "yes"
monitors:
3:
# doorbell
ml_sequence:
general:
model_sequence: "object,face"
object:
general:
pattern: "(person|monitor_doorbell)"
7:
# Driveway
ml_sequence:
general:
model_sequence: "object,alpr"
object:
general:
pattern: "(person|car|motorbike|bus|truck|boat)"
2:
# Front lawn
ml_sequence:
general:
model_sequence: "object"
object:
general:
pattern: "(person)"
4:
# deck
ml_sequence:
object:
general:
pattern: "(person|monitor_deck)"
stream_sequence:
frame_set: "alarm"
resize: 800
contig_frames_before_error: 5
max_attempts: 3
sleep_between_attempts: 4
About specific detection types
License plate recognition
Three ALPR options are provided:
Plate Recognizer . Uses a deep learning model that provides more accurate results than OpenALPR in my testing. Requires a license key (a free tier is available with 2500 lookups per month).
OpenALPR . While OpenALPR’s detection is not as good as Plate Recognizer, when it does detect, it provides a lot more information (like car make/model/year etc.)
OpenALPR command line. This is a basic version of OpenALPR that can be self compiled and executed locally. It is far inferior to the cloud services and does NOT use any form of deep learning. However, it is free, and if you have a camera that has a good view of plates, it will work.
alpr_service defined the service to be used.
Face Dection & Recognition
When it comes to faces, there are two aspects (that many often confuse):
Detecting a Face
Recognizing a Face
Face Detection
If you only want “face detection”, you can use either dlib/face_recognition or Google’s TPU. Both are supported.
Take a look at objectconfig.yml for how to set them up.
Face Detection + Face Recognition
Face Recognition uses
this library. Before
you try and use face recognition, please make sure you did a
/opt/zoneminder/venv/bin/pip install face_recognition The reason this is not
automatically done during setup is that it installs a lot of
dependencies that takes time (including dlib) and not everyone wants it.
Using the right face recognition modes
Face recognition uses dlib. Note that in
objectconfig.ymlyou have two options of face detection/recognition. Dlib has two modes of operation (controlled byface_model). Face recognition works in two steps: - A: Detect a face - B: Recognize a face
face_model affects step A. If you use cnn as a value, it will use a DNN to detect a face. If you use hog as a value, it will use a much faster method to detect a face. cnn is much more accurate in finding faces than hog but much slower. In my experience, hog works ok for front faces while cnn detects profiles/etc as well.
Step B kicks in only after step A succeeds (i.e. a face has been detected). The algorithm used there is common irrespective of whether you found a face via hog or cnn.
Configuring face recognition directories
Make sure you have images of people you want to recognize in
/var/lib/zmeventnotification/known_facesYou can have multiple faces per person
Typical configuration:
known_faces/
+----------bruce_lee/
+------1.jpg
+------2.jpg
+----------david_gilmour/
+------1.jpg
+------img2.jpg
+------3.jpg
+----------ramanujan/
+------face1.jpg
+------face2.jpg
In this example, you have 3 names, each with different images.
It is recommended that you now train the images by doing:
sudo -u www-data /var/lib/zmeventnotification/bin/zm_train_faces.py
If you find yourself running out of memory while training, use the size argument like so:
sudo -u www-data /var/lib/zmeventnotification/bin/zm_train_faces.py --size 800
Note that you do not necessarily have to train it first but I highly recommend it. When detection runs, it will look for the trained file and if missing, will auto-create it. However, detection may also load yolo and if you have limited GPU resources, you may run out of memory when training.
When face recognition is triggered, it will load each of these files and if there are faces in them, will load them and compare them to the alarmed image
known faces images
Make sure the face is recognizable
crop it to around 800 pixels width (doesn’t seem to need bigger images, but experiment. Larger the image, the larger the memory requirements)
crop around the face - not a tight crop, but no need to add a full body. A typical “passport” photo crop, maybe with a bit more of shoulder is ideal.
Audio Recognition (BirdNET)
BirdNET identifies 6500+ bird species from audio extracted from ZoneMinder events. It uses the BirdNET-Analyzer deep learning model. Audio is extracted from the event video, split into 3-second chunks, and each chunk is analyzed for bird species. The best confidence per species across all chunks is reported.
Installation:
BirdNET is not installed by default. Use the installer flag:
sudo -H ./install.sh --install-birdnet
Or install manually into the venv:
/opt/zoneminder/venv/bin/pip install birdnet-analyzer
Configuration (in objectconfig.yml):
Add
audioto yourmodel_sequence:ml_sequence: general: model_sequence: "object,face,alpr,audio"
Configure the
audiosection:audio: general: pattern: ".*" same_model_sequence_strategy: first sequence: - name: BirdNET enabled: "yes" audio_framework: birdnet birdnet_min_conf: 0.5 birdnet_lat: -1 birdnet_lon: -1 birdnet_sensitivity: 1.0 birdnet_overlap: 0.0
Parameters:
birdnet_min_conf— minimum confidence threshold (0.0–1.0). Default: 0.5birdnet_lat,birdnet_lon— latitude/longitude for seasonal species filtering. Set to your location to restrict predictions to species expected in your area at the current time of year. -1 disables location filtering (all 6500+ species considered). If set to -1 but the ZM monitor has lat/lon in the database, those values are used as a fallback.birdnet_sensitivity— sigmoid sensitivity (higher = more sensitive, more false positives). Default: 1.0birdnet_overlap— overlap in seconds between consecutive 3-second audio chunks (0.0–2.9). Default: 0.0
Notes:
Audio detection only runs on monitors that have audio recording enabled in ZoneMinder.
Unlike image-based detections, audio detections do not have spatial bounding boxes (a dummy 1×1 box is used internally).
BirdNET results appear in event notes alongside object/face/ALPR detections.
The
patternregex in theaudiogeneral section filters species names, e.g.pattern: "(Robin|Sparrow)"to only report specific species.
Troubleshooting
See Machine Learning Hooks FAQ for troubleshooting, debugging, and common issues.
zm_detect.py command-line reference
zm_detect.py [-h] [-c CONFIG] [-e EVENTID] [-p EVENTPATH] [-m MONITORID]
[-v] [--bareversion] [-o OUTPUT_PATH] [-f FILE] [-r REASON]
[-n] [-d] [--fakeit LABELS] [--pyzm-debug]
[-O KEY=VALUE [KEY=VALUE ...]]
-c, --configPath to
objectconfig.yml(default:/etc/zm/objectconfig.yml).-e, --eventidZM event ID to analyze (required unless
--fileis given).-f, --fileSkip event download, detect on a local image file instead.
-m, --monitoridMonitor ID. Enables per-monitor overrides (zones, ml_sequence, etc.) from config.
-p, --eventpathPath to store output files (
objdetect.jpg,objects.json).-r, --reasonReason/cause string for the event (passed by ZM or the ES).
-n, --notesUpdate ZM event notes with detection results.
-d, --debugPrint debug logs to terminal.
--fakeit LABELSOverride detection with fake labels for testing (comma-separated). Example:
--fakeit "dog,person"--pyzm-debugRoute pyzm library internal debug logs through ZMLog.
-v, --versionPrint version and exit.
--bareversionPrint just the version number (no pyzm version) and exit.
-o, --output-pathDirectory to write debug images to (used with
write_debug_image).-O, --override KEY=VALUEOverride any config value from
objectconfig.ymlvia dot-notation paths. Repeatable — specify once per override. Applied after all other config loading (including per-monitor overrides), so this is the highest-priority override.Flat keys:
zm_detect.py -e 12345 -m 1 -O show_percent=20 -O write_debug_image=yes
Nested keys (use dots to traverse dicts):
zm_detect.py -e 12345 -m 1 -O ml_sequence.object.general.pattern="(car|person)"
List indexing (use
[N]for sequence entries):zm_detect.py -e 12345 -m 1 -O ml_sequence.object.sequence[0].object_min_confidence=0.5
Name-based lookup (use
[Name]to match by thenamefield, case-insensitive):zm_detect.py -e 12345 -m 1 -O "ml_sequence.object.sequence[YOLOv11 ONNX].enabled=no" zm_detect.py -e 12345 -m 1 -O "ml_sequence.audio.sequence[BirdNET].birdnet_min_conf=0.3"
Values are auto-coerced: integers and floats are parsed as numbers,
yes/noremain as strings. Invalid paths log a warning and are skipped.