Skip to content

BirdCallDetector

Detect bird calls in audio files using a trained YOLO model. Processes WAV, FLAC, OGG, and MP3 files through the PCEN spectrogram pipeline and returns timestamped song segments with species labels and confidence scores.


Import

from inference.detect_birds import BirdCallDetector, merge_detections, reconstruct_songs, find_audio_files

BirdCallDetector

The main entry point for all inference workflows. Instantiate once per model, then call detect() for any number of files.

Constructor

BirdCallDetector(
    model_path,
    species_mapping,
    conf_threshold=0.001,
    nms_iou_threshold=0.7,
    song_gap_threshold=0.1,
    num_workers=1,
    verbose=True,
    freq_iou_threshold=None,
    merge_method=None,
    phrase_gap=None,
)
Parameter Type / Default Required? Description
model_path str / — Yes Path to the trained YOLO model file (.pt, .onnx, .engine, etc.).
species_mapping str / — 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.
conf_threshold float / 0.001 No Confidence threshold (0.0–1.0). Detections below this value are discarded. For direct field use, take 0.15 for Just-Bird and 0.2 for the other models. Use 0.001 to retain all raw detections for evaluation.
nms_iou_threshold float / 0.7 No IoU threshold for Non-Maximum Suppression applied per-clip and across overlapping time windows.
song_gap_threshold float / 0.1 No Maximum temporal gap in seconds between two detections of the same species that are still merged into one continuous song segment.
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.
verbose bool / True No If True, print per-file processing details and clip-level progress bars. If False, show a single file-level progress bar. The CLI passes False unless --verbose is set.
freq_iou_threshold float / None No reconstruct merge with Just-Bird only: frequency IoU gate (None = 0.6, negative = time-only).
merge_method str / None No 'fusion' (window ownership + weighted box fusion) or 'reconstruct' (song merge). None or 'auto' picks 'fusion' for Just-Bird and 'reconstruct' for Birds-With-Labels. See Window fusion.
phrase_gap float / None No Fusion only. Join same-band boxes closer than this many seconds into one phrase box. 0 keeps call-level boxes. None uses the model default: 0.05 s for Just-Bird, 0 for Birds-With-Labels.

Memory Usage

Extra workers are render processes, not extra model copies. The parent holds one YOLO model. Workers use additional RAM for matplotlib/librosa, not another set of weights.


Methods

Method Returns Description
detect(audio_path, output_path, output_formats, no_merge) List[Dict] Detect in a file or directory. Main entry point.
detect_single_file(audio_path, progress_callback, no_merge) List[Dict] Detect in a single audio file.
detect_multiple_files(audio_paths, output_path, output_formats, no_merge) List[Dict] Detect across a list of audio files.
save_results(detections, output_path, audio_path, output_formats, no_merge) None Write detections to one or more output formats.
merge_overlapping_detections(detections, merge_mode) List[Dict] Merge raw clip detections.
load_audio(audio_path) Tuple[np.ndarray, int] Load an audio file, returns (signal, sample_rate).

detect

detector.detect(
    audio_path,
    output_path=None,
    output_formats=None,
    no_merge=False,
)

Detect bird calls in an audio file or directory. Automatically routes to detect_single_file() or detect_multiple_files() based on whether audio_path is a file or a directory.

Parameter Type / Default Required? Description
audio_path str / — Yes Path to a single audio file (WAV, FLAC, OGG, MP3) or a directory. Directories are searched recursively.
output_path str / None No Output directory for result files. If None, results are only returned, not written.
output_formats List[str] / ['json-with-algorithm-metadata'] No One or more output format keys. Accepts json-with-algorithm-metadata, simplified-csv, xeno-canto-annota-json, raven-selection-table, or all. Ignored when no_merge=True.
no_merge bool / False No Evaluation mode. Returns raw per-clip detections and writes only raw_detections.json. Use with conf_threshold=0.001 for F-beta sweep workflows.

Returns: List[Dict] — merged song segments (or raw clip detections when no_merge=True).


detect_single_file

detector.detect_single_file(
    audio_path,
    progress_callback=None,
    no_merge=False,
)

Detect bird calls in a single audio file. Use this method directly when you need a progress_callback (e.g. in a GUI or web app).

Parameter Type / Default Required? Description
audio_path str / — Yes Path to the audio file.
progress_callback Callable[[int, int, str], None] / None No Called as callback(current, total, message) after each clip. Useful for progress bars.
no_merge bool / False No If True, return raw clip-level detections without merging.

Returns: List[Dict] — detections for this file.


save_results

detector.save_results(
    detections,
    output_path,
    audio_path=None,
    output_formats=None,
    no_merge=False,
)

Write detections to one or more output formats under output_path.

Parameter Type / Default Required? Description
detections List[Dict] / — Yes Detections returned by detect() or detect_single_file().
output_path str / — Yes Directory to write output files into.
audio_path str / None No Source audio path, used for metadata in JSON output.
output_formats List[str] / ['json-with-algorithm-metadata'] No One or more format keys (same values as detect()).
no_merge bool / False No If True, writes only raw_detections.json and ignores output_formats.

merge_overlapping_detections

detector.merge_overlapping_detections(detections, merge_mode='reconstruct')

Merge raw clip detections into song segments or deduplicate with NMS.

Parameter Type / Default Required? Description
detections List[Dict] / — Yes Raw per-clip detections.
merge_mode str / 'reconstruct' No 'reconstruct' applies the detector's merge_method (window fusion or the song merge, default). 'nms' removes duplicates by IoU, keeping the highest-confidence box.
file_end float / None No End of the file's last window, used by window-fusion ownership. None takes the latest clip_end among the detections.

Returns: List[Dict] — merged or deduplicated detections.


merge_detections

Standalone version of the detector's merge, for raw detections saved with --no-merge. Filter by confidence first, then merge. The evaluation scripts use it, so their scores match detection.

from inference.detect_birds import file_ends_from_raw, merge_detections, resolve_merge_settings

settings = resolve_merge_settings(raw_json["model_config"])  # fusion for the call-level models
filtered = [d for d in raw_json["detections"] if d["confidence"] >= 0.15]
merged = merge_detections(filtered, **settings, file_end=file_ends_from_raw(raw_json["detections"]))
Parameter Type / Default Required? Description
detections List[Dict] / — Yes Raw detections with time_start, time_end, freq_low_hz, freq_high_hz, confidence, species_id, species, and for fusion clip_start / clip_end. Optional filename for multi-file inputs.
merge_method str / — Yes 'fusion' or 'reconstruct'.
song_gap_threshold float / — Yes reconstruct only: max gap in seconds.
freq_iou_threshold float / None No reconstruct only: frequency IoU gate.
phrase_gap float / None No Fusion only: join same-band boxes closer than this into phrase boxes. None or 0 keeps call-level boxes (no model default at this level; resolve_merge_settings fills it in).
file_end float, Dict[str, float] or None No Fusion only: end of each file's last window. file_ends_from_raw builds the per-file dict from the unfiltered raw list, which keeps ownership identical to live detection.

resolve_merge_settings(model_config, song_gap, freq_iou, merge_method, phrase_gap) returns these keyword arguments from a raw JSON's model_config. Explicit arguments win, then the JSON, then the model defaults (resolve_phrase_gap: 0.05 s for Just-Bird). A raw JSON written before the Just-Bird default changed has no phrase_gap and therefore gets 0.05 s; pass phrase_gap=0 to score it as before.

Returns: List[Dict] — merged segments in the format below.


reconstruct_songs

Standalone version of the song-merging logic. Use this outside a BirdCallDetector instance, for example in evaluation scripts that operate on previously saved raw detections.

from inference.detect_birds import reconstruct_songs

merged = reconstruct_songs(detections, song_gap_threshold)
merged = reconstruct_songs(detections, song_gap_threshold, freq_iou_threshold=0.6)
Parameter Type / Default Required? Description
detections List[Dict] / — Yes Raw detections, each with time_start, time_end, species_id, species, confidence, freq_low_hz, freq_high_hz. Optional filename key for multi-file inputs.
song_gap_threshold float / — Yes Max gap in seconds between two detections of the same species to merge them into one song segment.
freq_iou_threshold float / None No If set, also require this frequency IoU. None keeps the time-only merger. Just-Bird detection and evaluation pass 0.6 unless overridden.

Returns: List[Dict] — merged song segments, sorted by (filename, time_start).

Each merged segment contains:

Field Type Description
species str eBird species code
species_id int Model class ID
time_start float Start time in seconds
time_end float End time in seconds
avg_confidence float Running average confidence across merged clips
max_confidence float Highest confidence among merged clips
detections_merged int Number of raw clips that were merged
freq_low_hz int Lowest frequency of merged bounding boxes
freq_high_hz int Highest frequency of merged bounding boxes
filename str Source filename (multi-file inputs only)

find_audio_files

Discover all supported audio files in a path.

from inference.detect_birds import find_audio_files

paths = find_audio_files(audio_path)
Parameter Type / Default Required? Description
audio_path str / — Yes Path to a single audio file or a directory. Directories are searched recursively.
verbose bool / True No If True, print how many audio files were found in a directory.

Returns: List[str] — sorted list of absolute file paths. Supported extensions: .wav, .flac, .ogg, .mp3.


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 directory batch example.

Basic detection

from inference.detect_birds import BirdCallDetector

detector = BirdCallDetector(
    model_path="models/Just-Bird.pt",
    species_mapping="Just-Bird",
    conf_threshold=0.15,
)

detections = detector.detect(
    "recording.wav",
    output_path="results",
    output_formats=["simplified-csv"],
)

print(f"Found {len(detections)} song segments")
from inference.detect_birds import BirdCallDetector

detector = BirdCallDetector(
    model_path="models/Birds-With-Labels.pt",
    species_mapping="Birds-With-Labels",
)

detections = detector.detect(
    "/path/to/audio/folder",
    output_path="results",
    output_formats=["all"],
)

Evaluation workflow (raw detections)

from inference.detect_birds import BirdCallDetector

detector = BirdCallDetector(
    model_path="models/Just-Bird.pt",
    species_mapping="Just-Bird",
    conf_threshold=0.001,
)

raw_detections = detector.detect(
    "data/test_audio/",
    output_path="results",
    no_merge=True,
)

This writes results/raw_detections.json. Feed it into FBetaScoreAnalyzer next.

With progress callback

from inference.detect_birds import BirdCallDetector

detector = BirdCallDetector(
    model_path="models/Just-Bird.pt",
    species_mapping="Just-Bird",
)

def on_progress(current, total, message):
    print(f"[{current}/{total}] {message}")

detections = detector.detect_single_file(
    "long_recording.flac",
    progress_callback=on_progress,
)

Using reconstruct_songs independently

import json
from inference.detect_birds import reconstruct_songs

with open("results/raw_detections.json") as f:
    data = json.load(f)

raw = data["detections"]
gap = data["model_config"]["song_gap_threshold"]

merged = reconstruct_songs(raw, song_gap_threshold=gap)
print(f"{len(raw)} raw detections → {len(merged)} song segments")

Parallel inference

from inference.detect_birds import BirdCallDetector

detector = BirdCallDetector(
    model_path="models/Just-Bird.pt",
    species_mapping="Just-Bird",
    num_workers=4,
)

detections = detector.detect(
    "long_recording.wav",
    output_path="results",
    output_formats=["simplified-csv"],
)

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.