birdnet-analyzer 2.0.0__py3-none-any.whl → 2.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- birdnet_analyzer/__init__.py +9 -8
- birdnet_analyzer/analyze/__init__.py +5 -5
- birdnet_analyzer/analyze/__main__.py +3 -4
- birdnet_analyzer/analyze/cli.py +25 -25
- birdnet_analyzer/analyze/core.py +241 -245
- birdnet_analyzer/analyze/utils.py +692 -701
- birdnet_analyzer/audio.py +368 -372
- birdnet_analyzer/cli.py +709 -707
- birdnet_analyzer/config.py +242 -242
- birdnet_analyzer/eBird_taxonomy_codes_2021E.json +25279 -25279
- birdnet_analyzer/embeddings/__init__.py +3 -4
- birdnet_analyzer/embeddings/__main__.py +3 -3
- birdnet_analyzer/embeddings/cli.py +12 -13
- birdnet_analyzer/embeddings/core.py +69 -70
- birdnet_analyzer/embeddings/utils.py +179 -193
- birdnet_analyzer/evaluation/__init__.py +196 -195
- birdnet_analyzer/evaluation/__main__.py +3 -3
- birdnet_analyzer/evaluation/assessment/__init__.py +0 -0
- birdnet_analyzer/evaluation/assessment/metrics.py +388 -0
- birdnet_analyzer/evaluation/assessment/performance_assessor.py +409 -0
- birdnet_analyzer/evaluation/assessment/plotting.py +379 -0
- birdnet_analyzer/evaluation/preprocessing/__init__.py +0 -0
- birdnet_analyzer/evaluation/preprocessing/data_processor.py +631 -0
- birdnet_analyzer/evaluation/preprocessing/utils.py +98 -0
- birdnet_analyzer/gui/__init__.py +19 -23
- birdnet_analyzer/gui/__main__.py +3 -3
- birdnet_analyzer/gui/analysis.py +175 -174
- birdnet_analyzer/gui/assets/arrow_down.svg +4 -4
- birdnet_analyzer/gui/assets/arrow_left.svg +4 -4
- birdnet_analyzer/gui/assets/arrow_right.svg +4 -4
- birdnet_analyzer/gui/assets/arrow_up.svg +4 -4
- birdnet_analyzer/gui/assets/gui.css +28 -28
- birdnet_analyzer/gui/assets/gui.js +93 -93
- birdnet_analyzer/gui/embeddings.py +619 -620
- birdnet_analyzer/gui/evaluation.py +795 -813
- birdnet_analyzer/gui/localization.py +75 -68
- birdnet_analyzer/gui/multi_file.py +245 -246
- birdnet_analyzer/gui/review.py +519 -527
- birdnet_analyzer/gui/segments.py +191 -191
- birdnet_analyzer/gui/settings.py +128 -129
- birdnet_analyzer/gui/single_file.py +267 -269
- birdnet_analyzer/gui/species.py +95 -95
- birdnet_analyzer/gui/train.py +696 -698
- birdnet_analyzer/gui/utils.py +810 -808
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_af.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ar.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_bg.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ca.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_cs.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_da.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_de.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_el.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_en_uk.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_es.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_fi.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_fr.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_he.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_hr.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_hu.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_in.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_is.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_it.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ja.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ko.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_lt.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ml.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_nl.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_no.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pl.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pt_BR.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pt_PT.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ro.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ru.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sk.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sl.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sr.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sv.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_th.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_tr.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_uk.txt +6522 -6522
- birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_zh.txt +6522 -6522
- birdnet_analyzer/lang/de.json +334 -334
- birdnet_analyzer/lang/en.json +334 -334
- birdnet_analyzer/lang/fi.json +334 -334
- birdnet_analyzer/lang/fr.json +334 -334
- birdnet_analyzer/lang/id.json +334 -334
- birdnet_analyzer/lang/pt-br.json +334 -334
- birdnet_analyzer/lang/ru.json +334 -334
- birdnet_analyzer/lang/se.json +334 -334
- birdnet_analyzer/lang/tlh.json +334 -334
- birdnet_analyzer/lang/zh_TW.json +334 -334
- birdnet_analyzer/model.py +1212 -1243
- birdnet_analyzer/playground.py +5 -0
- birdnet_analyzer/search/__init__.py +3 -3
- birdnet_analyzer/search/__main__.py +3 -3
- birdnet_analyzer/search/cli.py +11 -12
- birdnet_analyzer/search/core.py +78 -78
- birdnet_analyzer/search/utils.py +107 -111
- birdnet_analyzer/segments/__init__.py +3 -3
- birdnet_analyzer/segments/__main__.py +3 -3
- birdnet_analyzer/segments/cli.py +13 -14
- birdnet_analyzer/segments/core.py +81 -78
- birdnet_analyzer/segments/utils.py +383 -394
- birdnet_analyzer/species/__init__.py +3 -3
- birdnet_analyzer/species/__main__.py +3 -3
- birdnet_analyzer/species/cli.py +13 -14
- birdnet_analyzer/species/core.py +35 -35
- birdnet_analyzer/species/utils.py +74 -75
- birdnet_analyzer/train/__init__.py +3 -3
- birdnet_analyzer/train/__main__.py +3 -3
- birdnet_analyzer/train/cli.py +13 -14
- birdnet_analyzer/train/core.py +113 -113
- birdnet_analyzer/train/utils.py +877 -847
- birdnet_analyzer/translate.py +133 -104
- birdnet_analyzer/utils.py +426 -419
- {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/METADATA +137 -129
- birdnet_analyzer-2.0.1.dist-info/RECORD +125 -0
- {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/WHEEL +1 -1
- {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/licenses/LICENSE +18 -18
- birdnet_analyzer-2.0.0.dist-info/RECORD +0 -117
- {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/entry_points.txt +0 -0
- {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/top_level.txt +0 -0
birdnet_analyzer/gui/segments.py
CHANGED
@@ -1,191 +1,191 @@
|
|
1
|
-
import concurrent.futures
|
2
|
-
import os
|
3
|
-
from functools import partial
|
4
|
-
|
5
|
-
import gradio as gr
|
6
|
-
|
7
|
-
import birdnet_analyzer.config as cfg
|
8
|
-
import birdnet_analyzer.gui.
|
9
|
-
import birdnet_analyzer.gui.
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def extract_segments_wrapper(entry):
|
14
|
-
return (entry[0][0], extract_segments(entry))
|
15
|
-
|
16
|
-
|
17
|
-
@gu.gui_runtime_error_handler
|
18
|
-
def _extract_segments(
|
19
|
-
audio_dir, result_dir, output_dir, min_conf, num_seq, audio_speed, seq_length, threads, progress=gr.Progress()
|
20
|
-
):
|
21
|
-
from birdnet_analyzer.segments.utils import
|
22
|
-
|
23
|
-
gu.validate(audio_dir, loc.localize("validation-no-audio-directory-selected"))
|
24
|
-
|
25
|
-
if not result_dir:
|
26
|
-
result_dir = audio_dir
|
27
|
-
|
28
|
-
if not output_dir:
|
29
|
-
output_dir = audio_dir
|
30
|
-
|
31
|
-
if progress is not None:
|
32
|
-
progress(0, desc=f"{loc.localize('progress-search')} ...")
|
33
|
-
|
34
|
-
# Parse audio and result folders
|
35
|
-
cfg.FILE_LIST = parse_folders(audio_dir, result_dir)
|
36
|
-
|
37
|
-
# Set output folder
|
38
|
-
cfg.OUTPUT_PATH = output_dir
|
39
|
-
|
40
|
-
# Set number of threads
|
41
|
-
cfg.CPU_THREADS = int(threads)
|
42
|
-
|
43
|
-
# Set confidence threshold
|
44
|
-
cfg.MIN_CONFIDENCE = max(0.01, min(0.99, min_conf))
|
45
|
-
|
46
|
-
# Parse file list and make list of segments
|
47
|
-
cfg.FILE_LIST = parse_files(cfg.FILE_LIST, max(1, int(num_seq)))
|
48
|
-
|
49
|
-
# Audio speed
|
50
|
-
cfg.AUDIO_SPEED = max(0.1, 1.0 / (audio_speed * -1)) if audio_speed < 0 else max(1.0, float(audio_speed))
|
51
|
-
|
52
|
-
# Add config items to each file list entry.
|
53
|
-
# We have to do this for Windows which does not
|
54
|
-
# support fork() and thus each process has to
|
55
|
-
# have its own config. USE LINUX!
|
56
|
-
# flist = [(entry, max(cfg.SIG_LENGTH, float(seq_length)), cfg.getConfig()) for entry in cfg.FILE_LIST]
|
57
|
-
flist = [(entry, float(seq_length), cfg.get_config()) for entry in cfg.FILE_LIST]
|
58
|
-
|
59
|
-
result_list = []
|
60
|
-
|
61
|
-
# Extract segments
|
62
|
-
if cfg.CPU_THREADS < 2:
|
63
|
-
for i, entry in enumerate(flist):
|
64
|
-
result = extract_segments_wrapper(entry)
|
65
|
-
result_list.append(result)
|
66
|
-
|
67
|
-
if progress is not None:
|
68
|
-
progress((i, len(flist)), total=len(flist), unit="files")
|
69
|
-
else:
|
70
|
-
with concurrent.futures.ProcessPoolExecutor(max_workers=cfg.CPU_THREADS) as executor:
|
71
|
-
futures = (executor.submit(extract_segments_wrapper, arg) for arg in flist)
|
72
|
-
for i, f in enumerate(concurrent.futures.as_completed(futures), start=1):
|
73
|
-
if progress is not None:
|
74
|
-
progress((i, len(flist)), total=len(flist), unit="files")
|
75
|
-
result = f.result()
|
76
|
-
|
77
|
-
result_list.append(result)
|
78
|
-
|
79
|
-
return [[os.path.relpath(r[0], audio_dir), r[1]] for r in result_list]
|
80
|
-
|
81
|
-
|
82
|
-
def build_segments_tab():
|
83
|
-
with gr.Tab(loc.localize("segments-tab-title")):
|
84
|
-
audio_directory_state = gr.State()
|
85
|
-
result_directory_state = gr.State()
|
86
|
-
output_directory_state = gr.State()
|
87
|
-
|
88
|
-
def select_directory_to_state_and_tb(state_key):
|
89
|
-
return (gu.select_directory(collect_files=False, state_key=state_key),) * 2
|
90
|
-
|
91
|
-
with gr.Row():
|
92
|
-
select_audio_directory_btn = gr.Button(
|
93
|
-
loc.localize("segments-tab-select-audio-input-directory-button-label")
|
94
|
-
)
|
95
|
-
selected_audio_directory_tb = gr.Textbox(show_label=False, interactive=False)
|
96
|
-
select_audio_directory_btn.click(
|
97
|
-
partial(select_directory_to_state_and_tb, state_key="segments-audio-dir"),
|
98
|
-
outputs=[selected_audio_directory_tb, audio_directory_state],
|
99
|
-
show_progress=False,
|
100
|
-
)
|
101
|
-
|
102
|
-
with gr.Row():
|
103
|
-
select_result_directory_btn = gr.Button(
|
104
|
-
loc.localize("segments-tab-select-results-input-directory-button-label")
|
105
|
-
)
|
106
|
-
selected_result_directory_tb = gr.Textbox(
|
107
|
-
show_label=False,
|
108
|
-
interactive=False,
|
109
|
-
placeholder=loc.localize("segments-tab-results-input-textbox-placeholder"),
|
110
|
-
)
|
111
|
-
select_result_directory_btn.click(
|
112
|
-
partial(select_directory_to_state_and_tb, state_key="segments-result-dir"),
|
113
|
-
outputs=[result_directory_state, selected_result_directory_tb],
|
114
|
-
show_progress=False,
|
115
|
-
)
|
116
|
-
|
117
|
-
with gr.Row():
|
118
|
-
select_output_directory_btn = gr.Button(loc.localize("segments-tab-output-selection-button-label"))
|
119
|
-
selected_output_directory_tb = gr.Textbox(
|
120
|
-
show_label=False,
|
121
|
-
interactive=False,
|
122
|
-
placeholder=loc.localize("segments-tab-output-selection-textbox-placeholder"),
|
123
|
-
)
|
124
|
-
select_output_directory_btn.click(
|
125
|
-
partial(select_directory_to_state_and_tb, state_key="segments-output-dir"),
|
126
|
-
outputs=[selected_output_directory_tb, output_directory_state],
|
127
|
-
show_progress=False,
|
128
|
-
)
|
129
|
-
|
130
|
-
min_conf_slider = gr.Slider(
|
131
|
-
minimum=0.1,
|
132
|
-
maximum=0.99,
|
133
|
-
step=0.01,
|
134
|
-
value=cfg.MIN_CONFIDENCE,
|
135
|
-
label=loc.localize("segments-tab-min-confidence-slider-label"),
|
136
|
-
info=loc.localize("segments-tab-min-confidence-slider-info"),
|
137
|
-
)
|
138
|
-
num_seq_number = gr.Number(
|
139
|
-
100,
|
140
|
-
label=loc.localize("segments-tab-max-seq-number-label"),
|
141
|
-
info=loc.localize("segments-tab-max-seq-number-info"),
|
142
|
-
minimum=1,
|
143
|
-
)
|
144
|
-
audio_speed_slider = gr.Slider(
|
145
|
-
minimum=-10,
|
146
|
-
maximum=10,
|
147
|
-
value=cfg.AUDIO_SPEED,
|
148
|
-
step=1,
|
149
|
-
label=loc.localize("inference-settings-audio-speed-slider-label"),
|
150
|
-
info=loc.localize("inference-settings-audio-speed-slider-info"),
|
151
|
-
)
|
152
|
-
seq_length_number = gr.Number(
|
153
|
-
cfg.SIG_LENGTH,
|
154
|
-
label=loc.localize("segments-tab-seq-length-number-label"),
|
155
|
-
info=loc.localize("segments-tab-seq-length-number-info"),
|
156
|
-
minimum=0.1,
|
157
|
-
)
|
158
|
-
threads_number = gr.Number(
|
159
|
-
4,
|
160
|
-
label=loc.localize("segments-tab-threads-number-label"),
|
161
|
-
info=loc.localize("segments-tab-threads-number-info"),
|
162
|
-
minimum=1,
|
163
|
-
)
|
164
|
-
|
165
|
-
extract_segments_btn = gr.Button(loc.localize("segments-tab-extract-button-label"), variant="huggingface")
|
166
|
-
|
167
|
-
result_grid = gr.Matrix(
|
168
|
-
headers=[
|
169
|
-
loc.localize("segments-tab-result-dataframe-column-file-header"),
|
170
|
-
loc.localize("segments-tab-result-dataframe-column-execution-header"),
|
171
|
-
],
|
172
|
-
)
|
173
|
-
|
174
|
-
extract_segments_btn.click(
|
175
|
-
_extract_segments,
|
176
|
-
inputs=[
|
177
|
-
audio_directory_state,
|
178
|
-
result_directory_state,
|
179
|
-
output_directory_state,
|
180
|
-
min_conf_slider,
|
181
|
-
num_seq_number,
|
182
|
-
audio_speed_slider,
|
183
|
-
seq_length_number,
|
184
|
-
threads_number,
|
185
|
-
],
|
186
|
-
outputs=result_grid,
|
187
|
-
)
|
188
|
-
|
189
|
-
|
190
|
-
if __name__ == "__main__":
|
191
|
-
gu.open_window(build_segments_tab)
|
1
|
+
import concurrent.futures
|
2
|
+
import os
|
3
|
+
from functools import partial
|
4
|
+
|
5
|
+
import gradio as gr
|
6
|
+
|
7
|
+
import birdnet_analyzer.config as cfg
|
8
|
+
import birdnet_analyzer.gui.localization as loc
|
9
|
+
import birdnet_analyzer.gui.utils as gu
|
10
|
+
from birdnet_analyzer.segments.utils import extract_segments
|
11
|
+
|
12
|
+
|
13
|
+
def extract_segments_wrapper(entry):
|
14
|
+
return (entry[0][0], extract_segments(entry))
|
15
|
+
|
16
|
+
|
17
|
+
@gu.gui_runtime_error_handler
|
18
|
+
def _extract_segments(
|
19
|
+
audio_dir, result_dir, output_dir, min_conf, num_seq, audio_speed, seq_length, threads, progress=gr.Progress()
|
20
|
+
):
|
21
|
+
from birdnet_analyzer.segments.utils import parse_files, parse_folders
|
22
|
+
|
23
|
+
gu.validate(audio_dir, loc.localize("validation-no-audio-directory-selected"))
|
24
|
+
|
25
|
+
if not result_dir:
|
26
|
+
result_dir = audio_dir
|
27
|
+
|
28
|
+
if not output_dir:
|
29
|
+
output_dir = audio_dir
|
30
|
+
|
31
|
+
if progress is not None:
|
32
|
+
progress(0, desc=f"{loc.localize('progress-search')} ...")
|
33
|
+
|
34
|
+
# Parse audio and result folders
|
35
|
+
cfg.FILE_LIST = parse_folders(audio_dir, result_dir)
|
36
|
+
|
37
|
+
# Set output folder
|
38
|
+
cfg.OUTPUT_PATH = output_dir
|
39
|
+
|
40
|
+
# Set number of threads
|
41
|
+
cfg.CPU_THREADS = int(threads)
|
42
|
+
|
43
|
+
# Set confidence threshold
|
44
|
+
cfg.MIN_CONFIDENCE = max(0.01, min(0.99, min_conf))
|
45
|
+
|
46
|
+
# Parse file list and make list of segments
|
47
|
+
cfg.FILE_LIST = parse_files(cfg.FILE_LIST, max(1, int(num_seq)))
|
48
|
+
|
49
|
+
# Audio speed
|
50
|
+
cfg.AUDIO_SPEED = max(0.1, 1.0 / (audio_speed * -1)) if audio_speed < 0 else max(1.0, float(audio_speed))
|
51
|
+
|
52
|
+
# Add config items to each file list entry.
|
53
|
+
# We have to do this for Windows which does not
|
54
|
+
# support fork() and thus each process has to
|
55
|
+
# have its own config. USE LINUX!
|
56
|
+
# flist = [(entry, max(cfg.SIG_LENGTH, float(seq_length)), cfg.getConfig()) for entry in cfg.FILE_LIST]
|
57
|
+
flist = [(entry, float(seq_length), cfg.get_config()) for entry in cfg.FILE_LIST]
|
58
|
+
|
59
|
+
result_list = []
|
60
|
+
|
61
|
+
# Extract segments
|
62
|
+
if cfg.CPU_THREADS < 2:
|
63
|
+
for i, entry in enumerate(flist):
|
64
|
+
result = extract_segments_wrapper(entry)
|
65
|
+
result_list.append(result)
|
66
|
+
|
67
|
+
if progress is not None:
|
68
|
+
progress((i, len(flist)), total=len(flist), unit="files")
|
69
|
+
else:
|
70
|
+
with concurrent.futures.ProcessPoolExecutor(max_workers=cfg.CPU_THREADS) as executor:
|
71
|
+
futures = (executor.submit(extract_segments_wrapper, arg) for arg in flist)
|
72
|
+
for i, f in enumerate(concurrent.futures.as_completed(futures), start=1):
|
73
|
+
if progress is not None:
|
74
|
+
progress((i, len(flist)), total=len(flist), unit="files")
|
75
|
+
result = f.result()
|
76
|
+
|
77
|
+
result_list.append(result)
|
78
|
+
|
79
|
+
return [[os.path.relpath(r[0], audio_dir), r[1]] for r in result_list]
|
80
|
+
|
81
|
+
|
82
|
+
def build_segments_tab():
|
83
|
+
with gr.Tab(loc.localize("segments-tab-title")):
|
84
|
+
audio_directory_state = gr.State()
|
85
|
+
result_directory_state = gr.State()
|
86
|
+
output_directory_state = gr.State()
|
87
|
+
|
88
|
+
def select_directory_to_state_and_tb(state_key):
|
89
|
+
return (gu.select_directory(collect_files=False, state_key=state_key),) * 2
|
90
|
+
|
91
|
+
with gr.Row():
|
92
|
+
select_audio_directory_btn = gr.Button(
|
93
|
+
loc.localize("segments-tab-select-audio-input-directory-button-label")
|
94
|
+
)
|
95
|
+
selected_audio_directory_tb = gr.Textbox(show_label=False, interactive=False)
|
96
|
+
select_audio_directory_btn.click(
|
97
|
+
partial(select_directory_to_state_and_tb, state_key="segments-audio-dir"),
|
98
|
+
outputs=[selected_audio_directory_tb, audio_directory_state],
|
99
|
+
show_progress=False,
|
100
|
+
)
|
101
|
+
|
102
|
+
with gr.Row():
|
103
|
+
select_result_directory_btn = gr.Button(
|
104
|
+
loc.localize("segments-tab-select-results-input-directory-button-label")
|
105
|
+
)
|
106
|
+
selected_result_directory_tb = gr.Textbox(
|
107
|
+
show_label=False,
|
108
|
+
interactive=False,
|
109
|
+
placeholder=loc.localize("segments-tab-results-input-textbox-placeholder"),
|
110
|
+
)
|
111
|
+
select_result_directory_btn.click(
|
112
|
+
partial(select_directory_to_state_and_tb, state_key="segments-result-dir"),
|
113
|
+
outputs=[result_directory_state, selected_result_directory_tb],
|
114
|
+
show_progress=False,
|
115
|
+
)
|
116
|
+
|
117
|
+
with gr.Row():
|
118
|
+
select_output_directory_btn = gr.Button(loc.localize("segments-tab-output-selection-button-label"))
|
119
|
+
selected_output_directory_tb = gr.Textbox(
|
120
|
+
show_label=False,
|
121
|
+
interactive=False,
|
122
|
+
placeholder=loc.localize("segments-tab-output-selection-textbox-placeholder"),
|
123
|
+
)
|
124
|
+
select_output_directory_btn.click(
|
125
|
+
partial(select_directory_to_state_and_tb, state_key="segments-output-dir"),
|
126
|
+
outputs=[selected_output_directory_tb, output_directory_state],
|
127
|
+
show_progress=False,
|
128
|
+
)
|
129
|
+
|
130
|
+
min_conf_slider = gr.Slider(
|
131
|
+
minimum=0.1,
|
132
|
+
maximum=0.99,
|
133
|
+
step=0.01,
|
134
|
+
value=cfg.MIN_CONFIDENCE,
|
135
|
+
label=loc.localize("segments-tab-min-confidence-slider-label"),
|
136
|
+
info=loc.localize("segments-tab-min-confidence-slider-info"),
|
137
|
+
)
|
138
|
+
num_seq_number = gr.Number(
|
139
|
+
100,
|
140
|
+
label=loc.localize("segments-tab-max-seq-number-label"),
|
141
|
+
info=loc.localize("segments-tab-max-seq-number-info"),
|
142
|
+
minimum=1,
|
143
|
+
)
|
144
|
+
audio_speed_slider = gr.Slider(
|
145
|
+
minimum=-10,
|
146
|
+
maximum=10,
|
147
|
+
value=cfg.AUDIO_SPEED,
|
148
|
+
step=1,
|
149
|
+
label=loc.localize("inference-settings-audio-speed-slider-label"),
|
150
|
+
info=loc.localize("inference-settings-audio-speed-slider-info"),
|
151
|
+
)
|
152
|
+
seq_length_number = gr.Number(
|
153
|
+
cfg.SIG_LENGTH,
|
154
|
+
label=loc.localize("segments-tab-seq-length-number-label"),
|
155
|
+
info=loc.localize("segments-tab-seq-length-number-info"),
|
156
|
+
minimum=0.1,
|
157
|
+
)
|
158
|
+
threads_number = gr.Number(
|
159
|
+
4,
|
160
|
+
label=loc.localize("segments-tab-threads-number-label"),
|
161
|
+
info=loc.localize("segments-tab-threads-number-info"),
|
162
|
+
minimum=1,
|
163
|
+
)
|
164
|
+
|
165
|
+
extract_segments_btn = gr.Button(loc.localize("segments-tab-extract-button-label"), variant="huggingface")
|
166
|
+
|
167
|
+
result_grid = gr.Matrix(
|
168
|
+
headers=[
|
169
|
+
loc.localize("segments-tab-result-dataframe-column-file-header"),
|
170
|
+
loc.localize("segments-tab-result-dataframe-column-execution-header"),
|
171
|
+
],
|
172
|
+
)
|
173
|
+
|
174
|
+
extract_segments_btn.click(
|
175
|
+
_extract_segments,
|
176
|
+
inputs=[
|
177
|
+
audio_directory_state,
|
178
|
+
result_directory_state,
|
179
|
+
output_directory_state,
|
180
|
+
min_conf_slider,
|
181
|
+
num_seq_number,
|
182
|
+
audio_speed_slider,
|
183
|
+
seq_length_number,
|
184
|
+
threads_number,
|
185
|
+
],
|
186
|
+
outputs=result_grid,
|
187
|
+
)
|
188
|
+
|
189
|
+
|
190
|
+
if __name__ == "__main__":
|
191
|
+
gu.open_window(build_segments_tab)
|
birdnet_analyzer/gui/settings.py
CHANGED
@@ -1,129 +1,128 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
import
|
4
|
-
|
5
|
-
import birdnet_analyzer.gui.utils as gu
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
If
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
state =
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
settings =
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
settings_dict =
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
return current_time if current_time in options else "light"
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
import birdnet_analyzer.gui.utils as gu
|
6
|
+
from birdnet_analyzer import utils
|
7
|
+
|
8
|
+
FALLBACK_LANGUAGE = "en"
|
9
|
+
SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
|
10
|
+
GUI_SETTINGS_PATH = os.path.join(gu.APPDIR if utils.FROZEN else os.path.dirname(SCRIPT_DIR), "gui-settings.json")
|
11
|
+
LANG_DIR = str(Path(SCRIPT_DIR).parent / "lang")
|
12
|
+
STATE_SETTINGS_PATH = os.path.join(gu.APPDIR if utils.FROZEN else os.path.dirname(SCRIPT_DIR), "state.json")
|
13
|
+
|
14
|
+
|
15
|
+
def get_state_dict() -> dict:
|
16
|
+
"""
|
17
|
+
Retrieves the state dictionary from a JSON file specified by STATE_SETTINGS_PATH.
|
18
|
+
|
19
|
+
If the file does not exist, it creates an empty JSON file and returns an empty dictionary.
|
20
|
+
If any other exception occurs during file operations, it logs the error and returns an empty dictionary.
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
dict: The state dictionary loaded from the JSON file, or an empty dictionary if the file does not exist or an error occurs.
|
24
|
+
"""
|
25
|
+
try:
|
26
|
+
with open(STATE_SETTINGS_PATH, encoding="utf-8") as f:
|
27
|
+
return json.load(f)
|
28
|
+
except FileNotFoundError:
|
29
|
+
try:
|
30
|
+
with open(STATE_SETTINGS_PATH, "w", encoding="utf-8") as f:
|
31
|
+
json.dump({}, f)
|
32
|
+
return {}
|
33
|
+
except Exception as e:
|
34
|
+
utils.write_error_log(e)
|
35
|
+
return {}
|
36
|
+
|
37
|
+
|
38
|
+
def get_state(key: str, default=None) -> str:
|
39
|
+
"""
|
40
|
+
Retrieves the value associated with the given key from the state dictionary.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
key (str): The key to look up in the state dictionary.
|
44
|
+
default: The value to return if the key is not found. Defaults to None.
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
str: The value associated with the key if found, otherwise the default value.
|
48
|
+
"""
|
49
|
+
return get_state_dict().get(key, default)
|
50
|
+
|
51
|
+
|
52
|
+
def set_state(key: str, value: str):
|
53
|
+
"""
|
54
|
+
Updates the state dictionary with the given key-value pair and writes it to a JSON file.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
key (str): The key to update in the state dictionary.
|
58
|
+
value (str): The value to associate with the key in the state dictionary.
|
59
|
+
"""
|
60
|
+
try:
|
61
|
+
state = get_state_dict()
|
62
|
+
state[key] = value
|
63
|
+
|
64
|
+
with open(STATE_SETTINGS_PATH, "w") as f:
|
65
|
+
json.dump(state, f, indent=4)
|
66
|
+
except Exception as e:
|
67
|
+
utils.write_error_log(e)
|
68
|
+
|
69
|
+
|
70
|
+
def ensure_settings_file():
|
71
|
+
"""
|
72
|
+
Ensures that the settings file exists at the specified path. If the file does not exist,
|
73
|
+
it creates a new settings file with default settings.
|
74
|
+
|
75
|
+
If the file creation fails, the error is logged.
|
76
|
+
"""
|
77
|
+
if not os.path.exists(GUI_SETTINGS_PATH):
|
78
|
+
try:
|
79
|
+
with open(GUI_SETTINGS_PATH, "w") as f:
|
80
|
+
settings = {"language-id": FALLBACK_LANGUAGE, "theme": "light"}
|
81
|
+
f.write(json.dumps(settings, indent=4))
|
82
|
+
except Exception as e:
|
83
|
+
utils.write_error_log(e)
|
84
|
+
|
85
|
+
|
86
|
+
def get_setting(key, default=None):
|
87
|
+
"""
|
88
|
+
Retrieves the value associated with the given key from the settings file.
|
89
|
+
|
90
|
+
Args:
|
91
|
+
key (str): The key to look up in the settings file.
|
92
|
+
default: The value to return if the key is not found. Defaults to None.
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
str: The value associated with the key if found, otherwise the default value.
|
96
|
+
"""
|
97
|
+
ensure_settings_file()
|
98
|
+
|
99
|
+
try:
|
100
|
+
with open(GUI_SETTINGS_PATH, encoding="utf-8") as f:
|
101
|
+
settings_dict: dict = json.load(f)
|
102
|
+
|
103
|
+
return settings_dict.get(key, default)
|
104
|
+
except FileNotFoundError:
|
105
|
+
return default
|
106
|
+
|
107
|
+
|
108
|
+
def set_setting(key, value):
|
109
|
+
ensure_settings_file()
|
110
|
+
settings_dict = {}
|
111
|
+
|
112
|
+
try:
|
113
|
+
with open(GUI_SETTINGS_PATH, "r+", encoding="utf-8") as f:
|
114
|
+
settings_dict = json.load(f)
|
115
|
+
settings_dict[key] = value
|
116
|
+
f.seek(0)
|
117
|
+
json.dump(settings_dict, f, indent=4)
|
118
|
+
f.truncate()
|
119
|
+
|
120
|
+
except FileNotFoundError:
|
121
|
+
pass
|
122
|
+
|
123
|
+
|
124
|
+
def theme():
|
125
|
+
options = ("light", "dark")
|
126
|
+
current_time = get_setting("theme", "light")
|
127
|
+
|
128
|
+
return current_time if current_time in options else "light"
|