Detecting Birds
Detect bird calls in arbitrary-length audio files using a trained YOLO model. Processes WAV, FLAC, OGG, and MP3 files through the same PCEN spectrogram pipeline used during training, and returns timestamped song segments with species labels and confidence scores.
Usage Synopsis¶
Parameters¶
| Parameter | Type / Default | Required? | Description |
|---|---|---|---|
--audio |
PATH / — |
Yes | Path to an audio file (WAV, FLAC, OGG, MP3) or a directory. Directories are searched recursively for all supported audio files. |
--model |
PATH / — |
Yes | Path to the trained YOLO model file (.pt, .onnx, .engine, etc.). |
--species-mapping |
CHOICE / — |
Yes | Just-Bird or Birds-With-Labels, the model the class IDs come from. It maps them to eBird codes and sets the default merge. Must match the model. See Species mapping in the Data section. |
--output-path |
PATH / results |
No | Output directory for result files (default: results/, auto-versioned to results/run_N/ when outputs already exist). See Output Formats. |
--output-format |
CHOICE [...] / json-with-algorithm-metadata |
No | One or more output formats (space-separated). Accepts json-with-algorithm-metadata, simplified-csv, xeno-canto-annota-json, raven-selection-table, or all. Ignored when --no-merge is set (only raw_detections.json is written). |
--conf |
FLOAT / model default |
No | Confidence threshold (0.0–1.0). Detections below this value are discarded. The default is 0.15 for Just-Bird and 0.2 for the other models, for direct use. For evaluation workflows, use 0.001 together with --no-merge to retain all raw detections. |
--nms-iou |
FLOAT / 0.7 |
No | IoU threshold for Non-Maximum Suppression applied both per-clip and across overlapping time windows. Higher values keep more overlapping detections. Lower values suppress more aggressively. |
--merge |
CHOICE / auto |
No | How detections from the 50%-overlap windows are merged: fusion (window ownership + weighted box fusion, tight call-level boxes), reconstruct (song merge with --song-gap / --freq-iou), or auto: fusion for Just-Bird, reconstruct for Birds-With-Labels. See Window fusion. |
--phrase-gap |
FLOAT / 0.05 (Just-Bird), 0 (Birds-With-Labels) |
No | Fusion only. Join boxes in the same frequency band that are closer than this many seconds into one phrase box. 0 keeps one box per call or syllable. See phrase boxes. |
--song-gap |
FLOAT / 0.1 |
No | reconstruct merge only. Maximum temporal gap in seconds between two detections of the same species that are still merged into one continuous song segment. Increase for species with long pauses between phrases. Decrease to keep phrases separate. |
--freq-iou |
FLOAT / 0.6 for Just-Bird, off otherwise |
No | reconstruct merge with Just-Bird only. Minimum frequency IoU required before two bird detections are merged into one song. Other species mappings ignore this flag and keep the time-only merger. Pass a negative value to force the time-only merger for Just-Bird. |
--num-workers |
INT / 1 |
No | Number of CPU processes for PCEN, spectrogram rendering, and multi-file preprocess. YOLO inference stays in the parent and runs in batches. |
--no-merge |
flag / off | No | Evaluation mode: clip-level detections only, writes raw_detections.json and ignores --output-format. Use with low --conf (e.g. 0.001) for f_beta_score_analysis.py / filter_and_merge_detections.py. |
--verbose |
flag / off | No | Print per-file processing details, clip-level progress bars, and a detection summary. Default is a single file-level progress bar. |
Environment must match the model format
Each model format (.pt, .onnx, .tflite, .engine) requires a different Python environment. Using the wrong environment raises an import error or silently degrades results. Run python install.py --model-format <FORMAT> once for each format you want to use. See Install Parameters for the full table.
Parameter Deep-Dives¶
--conf — Confidence Threshold¶
The confidence threshold is the single most important tuning parameter. It controls how many detections reach the output.
| Use-case | Recommended value |
|---|---|
| Quick field recording scan | 0.15 for Just-Bird, 0.2 for the other models (default) |
| High-precision output (few false positives) | 0.4–0.6 |
| Comprehensive evaluation (feed into F-beta sweep) | 0.001 with --no-merge |
Evaluation Workflow Tip
For evaluation, run detection once at a very low confidence (--conf 0.001 --no-merge) to capture all candidate detections as raw JSON. Then use f_beta_score_analysis.py to find the optimal threshold, and apply it cheaply with filter_and_merge_detections.py—without re-running inference.
Window fusion (default for call-level models)¶
Detection runs on 3 s windows with a 1.5 s hop, so almost every call is seen by two windows. The song merge (reconstruct) unions both views, which widens boxes and chains fast calls into multi-second events. Window fusion (--merge fusion, the default for Just-Bird, Hawaii and Southern-Sierra-Nevada) instead:
- Ownership. Keeps a box only from the window whose middle 1.5 s holds the box centre. The first window also owns the file start, the last window the file end. These middles tile the file, so each call is kept once, from its most central view.
- Weighted box fusion. Boxes from one window that overlap at 2D IoU ≥ 0.4 (near-duplicates left by YOLO's per-window NMS) are averaged, weighted by confidence. The fused box keeps the strongest confidence.
- Join. Boxes that overlap in time by at least 0.05 s with frequency IoU ≥ 0.8 are joined, so a long call split across windows becomes one box. With a phrase gap (default 0.05 s for Just-Bird, see below), this step joins phrases instead.
- Nested boxes. A box with at least 80% of its area inside a larger box of the same species is dropped, for example a syllable inside the box of its whole call. This removes 10–25% of the boxes without lowering F1. On the unseen site it raised F1 at IoU 0.5 from 0.372 to 0.384, and on the Southern-Sierra-Nevada test set from 0.637 to 0.644.
On full recordings from a site the model never saw, fusion raised event F1 at IoU 0.5 from 0.309 to 0.337 for the same model. At the confidence chosen on in-domain validation it went from 0.211 to 0.272. In the output, detections_merged counts the raw detections behind each box.
Phrase boxes (--phrase-gap). Annotation conventions differ: some datasets box every syllable, others one box per phrase or song. With a phrase gap, step 3 instead joins boxes in the same frequency band (frequency IoU ≥ 0.3) that are less than the gap apart. Step 4 still applies afterwards. The default is 0.05 s for Just-Bird, which is run on recordings of any convention, and 0 (off) for the Hawaii and Southern-Sierra-Nevada models, which are trained on call-level labels. --phrase-gap 0 keeps call-level boxes for Just-Bird too.
Micro F1 at IoU 0.5 on full recordings, single class:
| Phrase gap | Hawaii val (call labels) | Southern Sierra Nevada, unseen site (partly phrase labels) | Western-US val (song labels) |
|---|---|---|---|
| 0 (off) | 0.581 | 0.385 | 0.170 |
| 0.05 s (Just-Bird default) | 0.531 | 0.396 | 0.197 |
| 0.1 s | 0.516 | 0.397 | 0.199 |
| 0.3 s | 0.456 | 0.399 | 0.203 |
The first two columns use the Just-Bird recipe trained without Southern Sierra Nevada. The Western-US column uses the released model on Western-US validation minutes it did not train on. Most of the gain on phrase and song labels comes with the first 0.05 s, while the loss on call labels grows with the gap. On Western-US, the song merge (--merge reconstruct) did no better than fusion with a phrase gap (0.204 at its best gap, but only 0.178 at confidence 0.15). For call-level annotations, pass --phrase-gap 0.
Which models use it. Fusion is the default for Just-Bird, which boxes single calls. On test sets labelled per call it raised micro F1 at IoU 0.5 over the song merge, from 0.515 to 0.582 on Hawai'i and from 0.615 to 0.637 on Southern Sierra Nevada (measured with models trained on each of these datasets alone). Birds-With-Labels was trained on both conventions, and Amazon Basin, Northeastern US and Western US label one box per song. Those wide labels match the song merge's wide boxes better (fusion scored 0.02–0.05 lower), so Birds-With-Labels keeps reconstruct. Fusion with --phrase-gap 0.1 to 0.5 comes within about 0.01 of it there.
--song-gap and --freq-iou do not apply to fusion. Pass --merge reconstruct for the previous behaviour.
--song-gap — Song Gap Threshold¶
This applies to the reconstruct merge (the default for Birds-With-Labels). After detection, temporally adjacent detections of the same species are merged into continuous song segments. Two detections are merged when the gap between them is ≤ --song-gap seconds.
Raw detections (same species):
71.80s – 72.11s
72.50s – 73.20s ← gap = 0.39 s (merged if song-gap ≥ 0.39)
73.50s – 75.24s ← gap = 0.30 s (merged if song-gap ≥ 0.30)
Result with --song-gap 0.5:
71.80s – 75.24s (3 clips merged, avg_conf reported)
| Value | Effect |
|---|---|
0.05 |
Very conservative — only clips nearly touching are merged |
0.1 (default) |
Good balance for most species |
0.5 |
Moderate — merges phrases separated by short pauses |
2.0 |
Aggressive — may over-merge distinct song bouts |
Frequency IoU gate for Just-Bird¶
Just-Bird predicts a single class. Two songs that overlap in time would otherwise become one box, and the frequency span would grow to cover both. When --species-mapping is Just-Bird, a detection joins an open song only if the time gap is within --song-gap and the frequency IoU is at least --freq-iou (default 0.6). Several songs can stay open at once.
Other species mappings do not use this gate. Different species already stay in separate groups.
--nms-iou — NMS IoU Threshold¶
Applied inside each 3-second spectrogram clip and again across overlapping time windows. It removes duplicate bounding boxes that exceed the IoU overlap threshold, keeping only the highest-confidence box.
Relationship to the merge
--nms-iou removes duplicates within each clip. The merge (window fusion, or --song-gap with reconstruct) then combines the detections of overlapping clips. They operate at different stages of the pipeline and do not conflict.
--num-workers — Parallel Workers¶
CPU work (PCEN, spectrogram rendering, and whole-file preprocess on folders) runs in a process pool so each worker has its own GIL and a single BLAS thread. YOLO inference stays in the parent process and is batched, which avoids loading extra model copies and is safer on a single GPU. Matches the num_workers constructor argument.
Memory Usage
--num-workers does not load extra YOLO weights. Workers add RAM for matplotlib/librosa only. The parent holds one model (and one GPU context when using CUDA).
--no-merge — Evaluation mode¶
When set, detect_birds.py enters evaluation mode:
- Song merging is skipped (clip-level detections kept).
- Only
raw_detections.jsonis written under--output-path(defaultresults/). --output-formatis ignored; a note is printed if other formats were requested.
Use this for the detection & evaluation workflow. For normal field use, omit --no-merge and pick formats with --output-format.
--output-format — Output Formats¶
Accepts one or more format names separated by spaces. Specify all to write every format in one run. For full schema documentation of each format see Detection Output Formats.
--species-mapping - Interpretation of Output Labels¶
The mapping name must match the label space the model was trained on. It is not inferred from the weights filename. You pass it explicitly. For details see Data-Input/Species-Mapping.
Output Formats¶
The --output-format flag controls which file(s) are written under --output-path (unless --no-merge is set). Full schema documentation for every format, including JSON field tables and CSV column definitions, is in Detection Output Formats.
Every run also writes args.yaml in the same output directory. That file records every CLI argument used for the run, including defaults, so you can compare settings such as --nms-iou and --song-gap across result folders.
Examples¶
The examples use Just-Bird, the recommended model. It detects bird vocalizations with a single bird class. For species-level output, use the Birds-With-Labels model with its species mapping, as in the verbose logging example.
Single file¶
Directory batch¶
Processing files: 100%|████████████| 8/8 [00:24<00:00, 3.00s/file]
Saved detections to: results/with_algorithm_metadata.json
Saved detections to CSV: results/simplified.csv
Saved detections to Xeno-Canto Annota-JSON: results/xeno-canto-annota.json
Saved Raven Selection Tables to directory: results/raven
Evaluation workflow¶
Parallel inference¶
Verbose logging¶
Found 8 audio files in directory: /path/to/audio/folder
============================================================
Processing file 1/8: dawn_chorus.wav
============================================================
Loading audio: /path/to/audio/folder/dawn_chorus.wav
Duration: 60.00 seconds
Sample rate: 32000 Hz
Processing audio with PCEN...
Planning to extract 39 clips from 60.0s audio
Successfully extracted 39 clips
Running detection on 39 clips...
Detecting: 100%|████████████| 39/39 [00:03<00:00]
Found 47 raw detections
Reconstructing continuous bird songs from detections...
Final count: 12 song segments
Found 12 detections in this file
...
TOTAL DETECTIONS ACROSS ALL FILES: 94
Lossy Audio Formats
The model was trained on lossless WAV files. When processing MP3 or OGG input, detection performance may degrade — especially for faint calls and high-frequency species. In a study of avian recordings, MP3 compression reduced the detections of human listeners and automated recognisers [19]. Use WAV or FLAC whenever possible. If you must use MP3, ensure a bitrate of ≥ 256 kbps.
References¶
[19] MacPhail, A. G., Yip, D. A., Knight, E. C., Hedley, R., Knaggs, M., Shonfield, J., Upham-Mills, E., & Bayne, E. M. (2024). "Audio data compression affects acoustic indices and reduces detections of birds by human listening and automated recognisers." Bioacoustics, 33(1):74–90.