Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ that directory is the way to test a first-run experience.
of what the next line does or how the code was arrived at — that belongs in
commit messages and the changelog. The same bar applies to test comments: the
test name and its assertions are the explanation.
- **GPU inference is BirdNET 3.0 only.** `--device GPU` reaches the birdnet session,
but only the ONNX backend 3.0 runs on has a device to dispatch to: 2.4 and custom
classifiers are asserted to CPU by the TensorFlow Lite backend, and Perch's
TensorFlow backend has no GPU on native Windows. `model_utils.effective_device`
downgrades anything else to CPU up front, because the library would otherwise only
fail inside the worker subprocesses. See
`docs/implementation-details/gpu-inference.rst`, which also records why DirectML is
not accepted as a GPU.
- **Don't widen the ruff config to make a fix pass.** The `select`/`ignore` lists in
`pyproject.toml` are deliberate.
- **Packaging is an allow-list, not a deny-list.** What ships is decided by the
Expand Down
10 changes: 10 additions & 0 deletions birdnet_analyzer/analyze/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def analyze(
batch_size: int = 1,
n_workers: int | None = None,
n_producers: int = 1,
device: str = "CPU",
rtype: RESULT_TYPES | list[RESULT_TYPES] = "table",
sf_thresh: float = 0.03,
top_n: int | None = None,
Expand Down Expand Up @@ -79,6 +80,10 @@ def analyze(
audio_speed (float, optional): Speed factor for audio playback during analysis.
Defaults to 1.0.
batch_size (int, optional): Batch size for processing. Defaults to 1.
device (str, optional): Device to run inference on: "CPU", "GPU" or
"GPU:<index>". Only BirdNET 3.0 can use the GPU, and only with a
GPU-capable ONNX Runtime installed; any other combination falls back to
the CPU with a warning. Defaults to "CPU".
rtype (Literal["table", "audacity", "kaleidoscope", "csv", "parquet"] |
List[Literal["table", "audacity", "kaleidoscope", "csv", "parquet"]], optional):
Output format(s) for results. Defaults to "table".
Expand Down Expand Up @@ -117,6 +122,7 @@ def analyze(
import birdnet_analyzer.config as cfg
from birdnet_analyzer.analyze.resume import ResumeJournal, RunMetadata
from birdnet_analyzer.model_utils import (
effective_device,
effective_sensitivity,
run_geomodel,
run_inference,
Expand All @@ -125,6 +131,8 @@ def analyze(

# Settled before the params file, result columns and resume fingerprint see it.
sensitivity = effective_sensitivity(sensitivity, model, birdnet, classifier)
# Deliberately not part of the resume fingerprint: it does not change results.
device = effective_device(device, model, birdnet, classifier)

species_list_file = slist if isinstance(slist, (str, Path)) else ""
rtypes: list[RESULT_TYPES] = [rtype] if isinstance(rtype, str) else rtype
Expand Down Expand Up @@ -209,6 +217,7 @@ def analyze(
callback=on_update,
n_workers=n_workers,
n_producers=n_producers,
device=device,
on_file_complete=journal.on_file_complete if journal else None,
)

Expand Down Expand Up @@ -323,6 +332,7 @@ def analyze(
"Batch size": batch_size,
"Number of workers": n_workers or "",
"Number of producers": n_producers,
"Device": device,
"Result type(s)": ", ".join(rtypes),
"Additional columns": ", ".join(additional_columns)
if additional_columns
Expand Down
17 changes: 17 additions & 0 deletions birdnet_analyzer/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,22 @@ def computing_resources_args():
return p


def device_args():
"""Argument parser for the inference device (--device)."""
p = argparse.ArgumentParser(add_help=False)

p.add_argument(
"--device",
type=str,
default="CPU",
help="Device to run inference on: 'CPU', 'GPU' or 'GPU:<index>'. Only BirdNET "
"3.0 supports the GPU, and only with a GPU-capable ONNX Runtime installed; "
"anything else falls back to the CPU with a warning.",
)

return p


def db_args():
"""Argument parser for the database path (-db/--database)."""
p = argparse.ArgumentParser(add_help=False)
Expand Down Expand Up @@ -442,6 +458,7 @@ def analyzer_parser():
locale_args(),
bs_args(),
computing_resources_args(),
device_args(),
load_params_args("analysis", "birdnet.analyze-params.csv"),
verbosity_args(),
]
Expand Down
3 changes: 3 additions & 0 deletions birdnet_analyzer/gui/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def run_analysis(
n_producers,
n_workers,
progress: gr.Progress | None,
device: str = "CPU",
split_tables: bool = False,
):
"""Starts the analysis.
Expand Down Expand Up @@ -93,6 +94,7 @@ def run_analysis(
batch_size: The number of samples in a batch.
n_producers: The number of producer threads to be used.
n_workers: The number of worker threads to be used.
device: The device to run inference on ("CPU", "GPU" or "GPU:<index>").
input_dir: The input directory.
progress: The gradio progress bar.
split_tables: Whether to split the output into separate tables per input file.
Expand Down Expand Up @@ -160,6 +162,7 @@ def run_analysis(
save_params=save_params,
n_producers=n_producers,
n_workers=n_workers,
device=device,
split_tables=split_tables,
_return_only=bool(input_path), # only for single file tab
)
4 changes: 3 additions & 1 deletion birdnet_analyzer/gui/embeddings.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ def on_cb_click(status, current, db_dir):
info=loc.localize("embedding-settings-audio-speed-slider-info"),
)

bs_number, producers_number, workers_number = gu.computing_settings(state)
bs_number, producers_number, workers_number, _ = gu.computing_settings(
state
)

fmin_number, fmax_number = gu.bandpass_settings(state)

Expand Down
7 changes: 6 additions & 1 deletion birdnet_analyzer/gui/multi_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def run_batch_analysis(
batch_size,
producers_number,
workers_number,
device,
input_dir,
progress=gr.Progress(),
):
Expand Down Expand Up @@ -103,6 +104,7 @@ def run_batch_analysis(
progress=progress,
n_producers=producers_number,
n_workers=workers_number,
device=device,
split_tables=split_tables_checkbox,
)
except RuntimeError:
Expand Down Expand Up @@ -342,7 +344,9 @@ def select_directory_wrapper():
info=loc.localize("multi-tab-split-table-checkbox-info"),
)

bs_number, producers_number, workers_number = gu.computing_settings(state)
bs_number, producers_number, workers_number, device_radio = (
gu.computing_settings(state, with_device=True)
)
resume_status_md = gr.Markdown(visible=False)

with gr.Row(equal_height=True):
Expand Down Expand Up @@ -386,6 +390,7 @@ def select_directory_wrapper():
bs_number,
producers_number,
workers_number,
device_radio,
input_directory_state,
]

Expand Down
7 changes: 7 additions & 0 deletions birdnet_analyzer/gui/presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ def load_analysis_params(path: str) -> dict[str, Any]:
"batch_size": "batch_size_number",
"n_producers": "producers_number",
"n_workers": "workers_number",
"device": "device_radio",
"top_n": "top_n_input",
"lat": "lat_number",
"lon": "lon_number",
Expand All @@ -340,6 +341,12 @@ def load_analysis_params(path: str) -> dict[str, Any]:
if "audio_speed" in kwargs:
values["audio_speed_slider"] = _speed_to_slider(kwargs["audio_speed"])

if "device_radio" in values:
import birdnet_analyzer.gui.utils as gu

device = str(values["device_radio"]).strip().upper().partition(":")[0]
values["device_radio"] = device if device in gu.analysis_devices() else "CPU"

values["use_top_n_checkbox"] = "top_n" in kwargs

# With top N in use the analysis ran without a confidence threshold and stored 0,
Expand Down
60 changes: 57 additions & 3 deletions birdnet_analyzer/gui/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1578,9 +1578,32 @@ def _get_win_drives():
return [f"{drive}:\\" for drive in UPPER_CASE] + _get_network_shortcuts()


def computing_settings(state: TabState):
# Measured several times faster than the batch size of 1 the CPU defaults to.
GPU_BATCH_SIZE = 16


def analysis_devices() -> list[str]:
"""The device choices to offer, newest-model first.

``GPU`` is only offered when an ONNX Runtime that can actually run BirdNET 3.0 on
it is installed, so the choice never silently degrades to CPU mid-analysis.
"""
from birdnet_analyzer.model_utils import gpu_available

return ["CPU", "GPU"] if gpu_available() else ["CPU"]


def computing_settings(state: TabState, with_device: bool = False):
"""Build the shared computing settings row.

Always returns four components; ``device_radio`` is None unless ``with_device``,
which only the analysis tab sets - the other tabs run models that are CPU-only.
"""
import psutil

device_radio = None
cpu_workers = psutil.cpu_count(logical=True) or 1

with gr.Row():
bs_number = state.persist(
"batch_size_number",
Expand All @@ -1605,12 +1628,43 @@ def computing_settings(state: TabState):
gr.Number,
precision=1,
label=loc.localize("computing-settings-workers-number-label"),
value=psutil.cpu_count(logical=True) or 1,
value=cpu_workers,
info=loc.localize("computing-settings-workers-number-info"),
minimum=1,
)

return bs_number, producers_number, workers_number
if with_device:
devices = analysis_devices()
device_radio = state.persist(
"device_radio",
gr.Radio,
choices=devices,
value="CPU",
label=loc.localize("computing-settings-device-radio-label"),
info=loc.localize("computing-settings-device-radio-info")
if len(devices) > 1
else loc.localize("computing-settings-device-radio-unavailable-info"),
interactive=len(devices) > 1,
)

if device_radio is not None:

def settings_for_device(device):
on_gpu = device != "CPU"

return gr.update(value=1 if on_gpu else cpu_workers), gr.update(
value=GPU_BATCH_SIZE if on_gpu else 1
)

device_radio.input(
settings_for_device,
inputs=device_radio,
outputs=[workers_number, bs_number],
show_progress="hidden",
queue=False,
)

return bs_number, producers_number, workers_number, device_radio


def info_box(description: str, title="Info") -> gr.Accordion:
Expand Down
3 changes: 3 additions & 0 deletions birdnet_analyzer/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"analyze-start-button-label": "Analysieren",
"computing-settings-batchsize-number-info": "Anzahl der gleichzeitig verarbeiteten Proben. Lassen Sie diesen Wert unverändert, wenn Sie nicht genau wissen, was Sie tun.",
"computing-settings-batchsize-number-label": "Batch-Größe",
"computing-settings-device-radio-info": "Hardware, auf der die Inferenz läuft. Die GPU steht nur für BirdNET 3.0 zur Verfügung.",
"computing-settings-device-radio-label": "Gerät",
"computing-settings-device-radio-unavailable-info": "Die Inferenz läuft auf der CPU: Es ist keine GPU-fähige ONNX Runtime installiert.",
"computing-settings-producers-number-info": "Anzahl der Producer-Prozesse, die die Inferenz-Warteschlange befüllen. Lassen Sie diesen Wert unverändert, wenn Sie nicht genau wissen, was Sie tun.",
"computing-settings-producers-number-label": "Produzenten",
"computing-settings-workers-number-info": "Maximale Anzahl an Worker-Threads für die Inferenz. Lassen Sie diesen Wert unverändert, wenn Sie nicht genau wissen, was Sie tun.",
Expand Down
3 changes: 3 additions & 0 deletions birdnet_analyzer/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"analyze-start-button-label": "Analyze",
"computing-settings-batchsize-number-info": "Number of samples processed simultaneously. Leave this value at its default unless you know what you're doing.",
"computing-settings-batchsize-number-label": "Batch size",
"computing-settings-device-radio-info": "Hardware used for inference. The GPU is only available for BirdNET 3.0.",
"computing-settings-device-radio-label": "Device",
"computing-settings-device-radio-unavailable-info": "Inference runs on the CPU: no GPU-capable ONNX Runtime is installed.",
"computing-settings-producers-number-info": "Number of producer processes feeding the inference queue. Leave this value at its default unless you know what you're doing.",
"computing-settings-producers-number-label": "Producers",
"computing-settings-workers-number-info": "Maximum number of worker threads used for inference. Leave this value at its default unless you know what you're doing.",
Expand Down
3 changes: 3 additions & 0 deletions birdnet_analyzer/lang/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"analyze-start-button-label": "Analysoi",
"computing-settings-batchsize-number-info": "Samanaikaisesti käsiteltävien näytteiden määrä. Jätä oletusarvoon, ellet tiedä tarkalleen mitä teet.",
"computing-settings-batchsize-number-label": "Eräkoko",
"computing-settings-device-radio-info": "Laitteisto, jolla inferenssi suoritetaan. GPU on käytettävissä vain BirdNET 3.0:lle.",
"computing-settings-device-radio-label": "Laite",
"computing-settings-device-radio-unavailable-info": "Inferenssi suoritetaan suorittimella: GPU-yhteensopivaa ONNX Runtimea ei ole asennettu.",
"computing-settings-producers-number-info": "Inferenssijonoa täyttävien tuottajaprosessien määrä. Jätä oletusarvoon, ellet tiedä tarkalleen mitä teet.",
"computing-settings-producers-number-label": "Tuottajat",
"computing-settings-workers-number-info": "Inferenssiin käytettävien työntekijäsäikeiden enimmäismäärä. Jätä oletusarvoon, ellet tiedä tarkalleen mitä teet.",
Expand Down
3 changes: 3 additions & 0 deletions birdnet_analyzer/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"analyze-start-button-label": "Analyser",
"computing-settings-batchsize-number-info": "Nombre d'échantillons traités simultanément. Laissez cette valeur par défaut sauf si vous savez exactement ce que vous faites.",
"computing-settings-batchsize-number-label": "Taille du lot",
"computing-settings-device-radio-info": "Matériel utilisé pour l'inférence. Le GPU n'est disponible que pour BirdNET 3.0.",
"computing-settings-device-radio-label": "Périphérique",
"computing-settings-device-radio-unavailable-info": "L'inférence s'exécute sur le CPU : aucun ONNX Runtime compatible GPU n'est installé.",
"computing-settings-producers-number-info": "Nombre de processus producteurs alimentant la file d'inférence. Laissez cette valeur par défaut sauf si vous savez exactement ce que vous faites.",
"computing-settings-producers-number-label": "Producteurs",
"computing-settings-workers-number-info": "Nombre maximal de threads de travail utilisés pour l'inférence. Laissez cette valeur par défaut sauf si vous savez exactement ce que vous faites.",
Expand Down
3 changes: 3 additions & 0 deletions birdnet_analyzer/lang/id.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"analyze-start-button-label": "Analisis",
"computing-settings-batchsize-number-info": "Jumlah sampel yang diproses secara bersamaan. Biarkan pada nilai bawaan kecuali Anda benar-benar tahu apa yang Anda lakukan.",
"computing-settings-batchsize-number-label": "Ukuran batch",
"computing-settings-device-radio-info": "Perangkat keras yang digunakan untuk inferensi. GPU hanya tersedia untuk BirdNET 3.0.",
"computing-settings-device-radio-label": "Perangkat",
"computing-settings-device-radio-unavailable-info": "Inferensi berjalan di CPU: tidak ada ONNX Runtime yang mendukung GPU terpasang.",
"computing-settings-producers-number-info": "Jumlah proses produser yang mengisi antrean inferensi. Biarkan pada nilai bawaan kecuali Anda benar-benar tahu apa yang Anda lakukan.",
"computing-settings-producers-number-label": "Produser",
"computing-settings-workers-number-info": "Jumlah maksimum thread worker yang digunakan untuk inferensi. Biarkan pada nilai bawaan kecuali Anda benar-benar tahu apa yang Anda lakukan.",
Expand Down
3 changes: 3 additions & 0 deletions birdnet_analyzer/lang/pt-br.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"analyze-start-button-label": "Analisar",
"computing-settings-batchsize-number-info": "Quantidade de amostras processadas simultaneamente. Deixe o valor padrão a menos que saiba exatamente o que está fazendo.",
"computing-settings-batchsize-number-label": "Tamanho do lote",
"computing-settings-device-radio-info": "Hardware usado na inferência. A GPU só está disponível para o BirdNET 3.0.",
"computing-settings-device-radio-label": "Dispositivo",
"computing-settings-device-radio-unavailable-info": "A inferência é executada na CPU: nenhum ONNX Runtime com suporte a GPU está instalado.",
"computing-settings-producers-number-info": "Número de processos produtores que alimentam a fila de inferência. Deixe o valor padrão a menos que saiba exatamente o que está fazendo.",
"computing-settings-producers-number-label": "Produtores",
"computing-settings-workers-number-info": "Número máximo de threads de trabalho usados na inferência. Deixe o valor padrão a menos que saiba exatamente o que está fazendo.",
Expand Down
3 changes: 3 additions & 0 deletions birdnet_analyzer/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"analyze-start-button-label": "Анализировать",
"computing-settings-batchsize-number-info": "Количество образцов, обрабатываемых одновременно. Оставьте значение по умолчанию, если не уверены в своих действиях.",
"computing-settings-batchsize-number-label": "Размер пакета",
"computing-settings-device-radio-info": "Оборудование, используемое для инференса. Графический процессор доступен только для BirdNET 3.0.",
"computing-settings-device-radio-label": "Устройство",
"computing-settings-device-radio-unavailable-info": "Инференс выполняется на процессоре: ONNX Runtime с поддержкой GPU не установлен.",
"computing-settings-producers-number-info": "Число процессов-производителей, наполняющих очередь инференса. Оставьте значение по умолчанию, если не уверены в своих действиях.",
"computing-settings-producers-number-label": "Производители",
"computing-settings-workers-number-info": "Максимальное число рабочих потоков, используемых для инференса. Оставьте значение по умолчанию, если не уверены в своих действиях.",
Expand Down
3 changes: 3 additions & 0 deletions birdnet_analyzer/lang/se.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"analyze-start-button-label": "Analysera",
"computing-settings-batchsize-number-info": "Antal sampel som bearbetas samtidigt. Låt standardvärdet stå om du inte vet exakt vad du gör.",
"computing-settings-batchsize-number-label": "Batch-storlek",
"computing-settings-device-radio-info": "Hårdvara som används för inferens. GPU är endast tillgängligt för BirdNET 3.0.",
"computing-settings-device-radio-label": "Enhet",
"computing-settings-device-radio-unavailable-info": "Inferensen körs på CPU:n: ingen ONNX Runtime med GPU-stöd är installerad.",
"computing-settings-producers-number-info": "Antal producentprocesser som fyller inferenskön. Låt standardvärdet stå om du inte vet exakt vad du gör.",
"computing-settings-producers-number-label": "Producenter",
"computing-settings-workers-number-info": "Maximalt antal arbetstrådar som används för inferens. Låt standardvärdet stå om du inte vet exakt vad du gör.",
Expand Down
Loading