Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ autotune/
gui-settings.json
state.json
BirdNET_analysis_params.csv
birdnet.analyze-params.csv
*.birdnet.train-params.csv

# Build files
entitlements.plist
4 changes: 3 additions & 1 deletion birdnet_analyzer/analyze/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ def main():
import os
from multiprocessing import freeze_support

from birdnet_analyzer import cli
from birdnet_analyzer import cli, params

freeze_support()

parser = cli.analyzer_parser()
cli.apply_params_file_defaults(parser, params.load_analysis_params)
args = parser.parse_args()

with contextlib.suppress(Exception):
Expand All @@ -40,5 +41,6 @@ def main():

analyze_args = vars(args)
analyze_args.pop("use_perch") # handled via model param
analyze_args.pop("load_params") # already applied as defaults

analyze(**analyze_args)
88 changes: 32 additions & 56 deletions birdnet_analyzer/analyze/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def analyze(
"""
import birdnet_analyzer.config as cfg
from birdnet_analyzer.model_utils import run_geomodel, run_inference
from birdnet_analyzer.utils import save_params_to_file
from birdnet_analyzer.utils import save_params_file

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 @@ -227,62 +227,38 @@ def analyze(
)

if save_params:
save_params_to_file(
save_params_file(
Path(output) / cfg.ANALYSIS_PARAMS_FILENAME,
(
"Model",
"BirdNET version",
"Segment length",
"Sample rate",
"Segment overlap",
"Bandpass filter minimum",
"Bandpass filter maximum",
"Merge consecutive detections",
"Audio speed",
"Minimum confidence",
"Sensitivity",
"Top N",
"Batch size",
"Number of workers",
"Number of producers",
"Result type(s)",
"Latitude",
"Longitude",
"Week",
"Species filter threshold",
"Species list file",
"Locale",
"Custom classifier path",
"Custom classifier species list",
"Split tables",
),
(
model,
birdnet,
predictions.segment_duration_s,
predictions.model_sr,
overlap,
fmin,
fmax,
merge_consecutive,
audio_speed,
min_conf,
sensitivity,
top_n or "",
batch_size,
n_workers or "",
n_producers,
", ".join(rtypes),
lat or "",
lon or "",
week or "",
sf_thresh,
species_list_file or "",
locale,
classifier or "",
cc_species_list or "",
split_tables,
),
{
"Model": model,
"BirdNET version": birdnet,
"Segment length": predictions.segment_duration_s,
"Sample rate": predictions.model_sr,
"Segment overlap": overlap,
"Bandpass filter minimum": fmin,
"Bandpass filter maximum": fmax,
"Merge consecutive detections": merge_consecutive,
"Audio speed": audio_speed,
"Minimum confidence": min_conf,
"Sensitivity": sensitivity,
"Top N": top_n or "",
"Batch size": batch_size,
"Number of workers": n_workers or "",
"Number of producers": n_producers,
"Result type(s)": ", ".join(rtypes),
"Additional columns": ", ".join(additional_columns)
if additional_columns
else "",
"Latitude": lat or "",
"Longitude": lon or "",
"Week": week or "",
"Species filter threshold": sf_thresh,
"Species list file": species_list_file or "",
"Locale": locale,
"Custom classifier path": classifier or "",
"Custom classifier species list": cc_species_list or "",
"Split tables": split_tables,
},
)

return predictions
Expand Down
73 changes: 68 additions & 5 deletions birdnet_analyzer/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,60 @@
""" # noqa: W291


def apply_params_file_defaults(parser, loader, argv=None):
"""Makes the values of a ``--load_params`` file the defaults of a parser.

Reads the file before the actual parsing, so arguments given on the command line
override the values from the file, which in turn override the built-in defaults.
Values the parser has no argument for are ignored.

Args:
parser: The fully built argument parser.
loader: Reads the parameters file into keyword arguments, e.g.
:func:`birdnet_analyzer.params.load_analysis_params`.
argv: The command line to read the file path from. Defaults to ``sys.argv``.
"""
pre_parser = argparse.ArgumentParser(add_help=False)
pre_parser.add_argument("--load_params")
known, _ = pre_parser.parse_known_args(argv)

if not known.load_params:
return

try:
values = loader(known.load_params)
except ValueError as e:
parser.error(str(e))

dests = {action.dest for action in parser._actions}
parser.set_defaults(**{key: value for key, value in values.items() if key in dests})


def load_params_args(run: str, files_hint: str):
"""
Creates an argument parser for reading the settings of a previous run.

Args:
run: What the parameters file belongs to, e.g. "analysis".
files_hint: The file names to point the user to.

Returns:
argparse.ArgumentParser: The argument parser with the `--load_params`
argument.
"""
p = argparse.ArgumentParser(add_help=False)

p.add_argument(
"--load_params",
metavar="PARAMS_FILE",
help=f"Read default settings from the parameters file of a previous {run} "
f"({files_hint}). Arguments given on the command line take precedence. "
"Parameters files of earlier BirdNET-Analyzer versions are understood too.",
)

return p


def store_model_action(model_name: str):
class StoreModelAction(argparse.Action):
def __init__(
Expand Down Expand Up @@ -456,6 +510,7 @@ def analyzer_parser():
locale_args(),
bs_args(),
computing_resources_args(),
load_params_args("analysis", "birdnet.analyze-params.csv"),
]

parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -509,7 +564,8 @@ def __call__(self, parser, namespace, values, option_string=None):
)
parser.add_argument(
"--split_tables",
action="store_true",
action=argparse.BooleanOptionalAction,
default=False,
help="Saves separate result tables for each input audio file in the output.",
)
parser.set_defaults(model="birdnet")
Expand Down Expand Up @@ -793,6 +849,7 @@ def train_parser():
overlap_args(
help_string="Overlap of training data segments in seconds if crop_mode is 'segments'."
),
load_params_args("training run", "*.birdnet.train-params.csv"),
],
)
c = (
Expand Down Expand Up @@ -840,7 +897,8 @@ def train_parser():
parser.add_argument(
"--focal-loss",
dest="use_focal_loss",
action="store_true",
action=argparse.BooleanOptionalAction,
default=False,
help="Use focal loss for training (helps with imbalanced classes and hard examples).",
)
parser.add_argument(
Expand Down Expand Up @@ -869,11 +927,15 @@ def train_parser():
)
parser.add_argument(
"--label_smoothing",
action="store_true",
action=argparse.BooleanOptionalAction,
default=False,
help="Whether to use label smoothing for training.",
)
parser.add_argument(
"--mixup", action="store_true", help="Whether to use mixup for training."
"--mixup",
action=argparse.BooleanOptionalAction,
default=False,
help="Whether to use mixup for training.",
)
parser.add_argument(
"--upsampling_ratio",
Expand Down Expand Up @@ -905,7 +967,8 @@ def train_parser():
)
parser.add_argument(
"--autotune",
action="store_true",
action=argparse.BooleanOptionalAction,
default=False,
help="Whether to use automatic hyperparameter tuning (this will execute multiple training runs to search for optimal hyperparameters).",
)
parser.add_argument(
Expand Down
3 changes: 2 additions & 1 deletion birdnet_analyzer/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
OUTPUT_CSV_FILENAME: str = "BirdNET_CombinedTable.csv"
OUTPUT_AUDACITY_FILENAME: str = "BirdNET_AudacityLabels.txt"
OUTPUT_PARQUET_FILENAME: str = "BirdNET_CombinedTable.parquet"
ANALYSIS_PARAMS_FILENAME: str = "BirdNET_analysis_params.csv"
ANALYSIS_PARAMS_FILENAME: str = "birdnet.analyze-params.csv"
TRAIN_PARAMS_SUFFIX: str = ".birdnet.train-params.csv"
LABEL_LANGUAGE: MODEL_LANGUAGES = MODEL_LANGUAGE_EN_US
SAMPLE_CROP_MODES = Literal["center", "first", "segments", "smart"]
NON_EVENT_CLASSES: list[str] = ["noise", "other", "background", "silence"]
Expand Down
16 changes: 16 additions & 0 deletions birdnet_analyzer/gui/multi_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import birdnet_analyzer.gui.localization as loc
import birdnet_analyzer.gui.utils as gu
from birdnet_analyzer.gui.presets import PresetControls, load_analysis_params
from birdnet_analyzer.gui.state import TabState


Expand Down Expand Up @@ -121,6 +122,14 @@ def build_multi_analysis_tab() -> gu.TAB_BUILDER_RESULT:
title=loc.localize("multi-tab-info-title"),
)

preset_controls = PresetControls(
"multi",
params_loader=load_analysis_params,
params_button_label=loc.localize(
"presets-load-analyze-params-button-label"
),
)

with gr.Group(), gr.Row(equal_height=True):
select_directory_btn = gr.Button(
loc.localize("multi-tab-input-selection-button-label"),
Expand Down Expand Up @@ -298,6 +307,13 @@ def show_additional_columns(values):
inputs=output_type_radio,
outputs=additional_columns_,
)
preset_controls.wire(
state,
species_file_input=species_settings["species_file_input"],
classifier_state=model_settings["selected_classifier_state"],
classifier_file_input=model_settings["classifier_file_input"],
classifier_labels_df=model_settings["classifier_labels_df"],
)

return (
species_settings["lat_number"],
Expand Down
Loading
Loading