birdnet-analyzer 2.0.0__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.
Files changed (117) hide show
  1. birdnet_analyzer/__init__.py +8 -0
  2. birdnet_analyzer/analyze/__init__.py +5 -0
  3. birdnet_analyzer/analyze/__main__.py +4 -0
  4. birdnet_analyzer/analyze/cli.py +25 -0
  5. birdnet_analyzer/analyze/core.py +245 -0
  6. birdnet_analyzer/analyze/utils.py +701 -0
  7. birdnet_analyzer/audio.py +372 -0
  8. birdnet_analyzer/cli.py +707 -0
  9. birdnet_analyzer/config.py +242 -0
  10. birdnet_analyzer/eBird_taxonomy_codes_2021E.json +25280 -0
  11. birdnet_analyzer/embeddings/__init__.py +4 -0
  12. birdnet_analyzer/embeddings/__main__.py +3 -0
  13. birdnet_analyzer/embeddings/cli.py +13 -0
  14. birdnet_analyzer/embeddings/core.py +70 -0
  15. birdnet_analyzer/embeddings/utils.py +193 -0
  16. birdnet_analyzer/evaluation/__init__.py +195 -0
  17. birdnet_analyzer/evaluation/__main__.py +3 -0
  18. birdnet_analyzer/gui/__init__.py +23 -0
  19. birdnet_analyzer/gui/__main__.py +3 -0
  20. birdnet_analyzer/gui/analysis.py +174 -0
  21. birdnet_analyzer/gui/assets/arrow_down.svg +4 -0
  22. birdnet_analyzer/gui/assets/arrow_left.svg +4 -0
  23. birdnet_analyzer/gui/assets/arrow_right.svg +4 -0
  24. birdnet_analyzer/gui/assets/arrow_up.svg +4 -0
  25. birdnet_analyzer/gui/assets/gui.css +29 -0
  26. birdnet_analyzer/gui/assets/gui.js +94 -0
  27. birdnet_analyzer/gui/assets/img/birdnet-icon.ico +0 -0
  28. birdnet_analyzer/gui/assets/img/birdnet_logo.png +0 -0
  29. birdnet_analyzer/gui/assets/img/birdnet_logo_no_transparent.png +0 -0
  30. birdnet_analyzer/gui/assets/img/clo-logo-bird.svg +1 -0
  31. birdnet_analyzer/gui/embeddings.py +620 -0
  32. birdnet_analyzer/gui/evaluation.py +813 -0
  33. birdnet_analyzer/gui/localization.py +68 -0
  34. birdnet_analyzer/gui/multi_file.py +246 -0
  35. birdnet_analyzer/gui/review.py +527 -0
  36. birdnet_analyzer/gui/segments.py +191 -0
  37. birdnet_analyzer/gui/settings.py +129 -0
  38. birdnet_analyzer/gui/single_file.py +269 -0
  39. birdnet_analyzer/gui/species.py +95 -0
  40. birdnet_analyzer/gui/train.py +698 -0
  41. birdnet_analyzer/gui/utils.py +808 -0
  42. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_af.txt +6522 -0
  43. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ar.txt +6522 -0
  44. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_bg.txt +6522 -0
  45. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ca.txt +6522 -0
  46. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_cs.txt +6522 -0
  47. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_da.txt +6522 -0
  48. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_de.txt +6522 -0
  49. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_el.txt +6522 -0
  50. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_en_uk.txt +6522 -0
  51. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_es.txt +6522 -0
  52. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_fi.txt +6522 -0
  53. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_fr.txt +6522 -0
  54. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_he.txt +6522 -0
  55. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_hr.txt +6522 -0
  56. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_hu.txt +6522 -0
  57. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_in.txt +6522 -0
  58. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_is.txt +6522 -0
  59. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_it.txt +6522 -0
  60. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ja.txt +6522 -0
  61. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ko.txt +6522 -0
  62. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_lt.txt +6522 -0
  63. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ml.txt +6522 -0
  64. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_nl.txt +6522 -0
  65. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_no.txt +6522 -0
  66. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pl.txt +6522 -0
  67. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pt_BR.txt +6522 -0
  68. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pt_PT.txt +6522 -0
  69. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ro.txt +6522 -0
  70. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ru.txt +6522 -0
  71. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sk.txt +6522 -0
  72. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sl.txt +6522 -0
  73. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sr.txt +6522 -0
  74. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sv.txt +6522 -0
  75. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_th.txt +6522 -0
  76. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_tr.txt +6522 -0
  77. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_uk.txt +6522 -0
  78. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_zh.txt +6522 -0
  79. birdnet_analyzer/lang/de.json +335 -0
  80. birdnet_analyzer/lang/en.json +335 -0
  81. birdnet_analyzer/lang/fi.json +335 -0
  82. birdnet_analyzer/lang/fr.json +335 -0
  83. birdnet_analyzer/lang/id.json +335 -0
  84. birdnet_analyzer/lang/pt-br.json +335 -0
  85. birdnet_analyzer/lang/ru.json +335 -0
  86. birdnet_analyzer/lang/se.json +335 -0
  87. birdnet_analyzer/lang/tlh.json +335 -0
  88. birdnet_analyzer/lang/zh_TW.json +335 -0
  89. birdnet_analyzer/model.py +1243 -0
  90. birdnet_analyzer/search/__init__.py +3 -0
  91. birdnet_analyzer/search/__main__.py +3 -0
  92. birdnet_analyzer/search/cli.py +12 -0
  93. birdnet_analyzer/search/core.py +78 -0
  94. birdnet_analyzer/search/utils.py +111 -0
  95. birdnet_analyzer/segments/__init__.py +3 -0
  96. birdnet_analyzer/segments/__main__.py +3 -0
  97. birdnet_analyzer/segments/cli.py +14 -0
  98. birdnet_analyzer/segments/core.py +78 -0
  99. birdnet_analyzer/segments/utils.py +394 -0
  100. birdnet_analyzer/species/__init__.py +3 -0
  101. birdnet_analyzer/species/__main__.py +3 -0
  102. birdnet_analyzer/species/cli.py +14 -0
  103. birdnet_analyzer/species/core.py +35 -0
  104. birdnet_analyzer/species/utils.py +75 -0
  105. birdnet_analyzer/train/__init__.py +3 -0
  106. birdnet_analyzer/train/__main__.py +3 -0
  107. birdnet_analyzer/train/cli.py +14 -0
  108. birdnet_analyzer/train/core.py +113 -0
  109. birdnet_analyzer/train/utils.py +847 -0
  110. birdnet_analyzer/translate.py +104 -0
  111. birdnet_analyzer/utils.py +419 -0
  112. birdnet_analyzer-2.0.0.dist-info/METADATA +129 -0
  113. birdnet_analyzer-2.0.0.dist-info/RECORD +117 -0
  114. birdnet_analyzer-2.0.0.dist-info/WHEEL +5 -0
  115. birdnet_analyzer-2.0.0.dist-info/entry_points.txt +11 -0
  116. birdnet_analyzer-2.0.0.dist-info/licenses/LICENSE +19 -0
  117. birdnet_analyzer-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,8 @@
1
+ from birdnet_analyzer.analyze import analyze
2
+ from birdnet_analyzer.embeddings import embeddings
3
+ from birdnet_analyzer.train import train
4
+ from birdnet_analyzer.search import search
5
+ from birdnet_analyzer.segments import segments
6
+ from birdnet_analyzer.species import species
7
+
8
+ __all__ = ["analyze", "train", "embeddings", "search", "segments", "species"]
@@ -0,0 +1,5 @@
1
+ from birdnet_analyzer.analyze.core import analyze
2
+
3
+ __all__ = [
4
+ "analyze",
5
+ ]
@@ -0,0 +1,4 @@
1
+ from birdnet_analyzer.analyze.cli import main
2
+
3
+
4
+ main()
@@ -0,0 +1,25 @@
1
+ from birdnet_analyzer.utils import runtime_error_handler
2
+ from birdnet_analyzer import analyze
3
+
4
+
5
+ @runtime_error_handler
6
+ def main():
7
+ import os
8
+ from multiprocessing import freeze_support
9
+
10
+ import birdnet_analyzer.cli as cli
11
+
12
+ # Freeze support for executable
13
+ freeze_support()
14
+
15
+ parser = cli.analyzer_parser()
16
+
17
+ args = parser.parse_args()
18
+
19
+ try:
20
+ if os.get_terminal_size().columns >= 64:
21
+ print(cli.ASCII_LOGO, flush=True)
22
+ except Exception:
23
+ pass
24
+
25
+ analyze(**vars(args))
@@ -0,0 +1,245 @@
1
+ import os
2
+ from typing import List, Literal
3
+
4
+
5
+ def analyze(
6
+ input: str,
7
+ output: str | None = None,
8
+ *,
9
+ min_conf: float = 0.25,
10
+ classifier: str | None = None,
11
+ lat: float = -1,
12
+ lon: float = -1,
13
+ week: int = -1,
14
+ slist: str | None = None,
15
+ sensitivity: float = 1.0,
16
+ overlap: float = 0,
17
+ fmin: int = 0,
18
+ fmax: int = 15000,
19
+ audio_speed: float = 1.0,
20
+ batch_size: int = 1,
21
+ combine_results: bool = False,
22
+ rtype: Literal["table", "audacity", "kaleidoscope", "csv"]
23
+ | List[Literal["table", "audacity", "kaleidoscope", "csv"]] = "table",
24
+ skip_existing_results: bool = False,
25
+ sf_thresh: float = 0.03,
26
+ top_n: int | None = None,
27
+ merge_consecutive: int = 1,
28
+ threads: int = 8,
29
+ locale: str = "en",
30
+ ):
31
+ """
32
+ Analyzes audio files for bird species detection using the BirdNET-Analyzer.
33
+ Args:
34
+ input (str): Path to the input directory or file containing audio data.
35
+ output (str | None, optional): Path to the output directory for results. Defaults to None.
36
+ min_conf (float, optional): Minimum confidence threshold for detections. Defaults to 0.25.
37
+ classifier (str | None, optional): Path to a custom classifier file. Defaults to None.
38
+ lat (float, optional): Latitude for location-based filtering. Defaults to -1.
39
+ lon (float, optional): Longitude for location-based filtering. Defaults to -1.
40
+ week (int, optional): Week of the year for seasonal filtering. Defaults to -1.
41
+ slist (str | None, optional): Path to a species list file for filtering. Defaults to None.
42
+ sensitivity (float, optional): Sensitivity of the detection algorithm. Defaults to 1.0.
43
+ overlap (float, optional): Overlap between analysis windows in seconds. Defaults to 0.
44
+ fmin (int, optional): Minimum frequency for analysis in Hz. Defaults to 0.
45
+ fmax (int, optional): Maximum frequency for analysis in Hz. Defaults to 15000.
46
+ audio_speed (float, optional): Speed factor for audio playback during analysis. Defaults to 1.0.
47
+ batch_size (int, optional): Batch size for processing. Defaults to 1.
48
+ combine_results (bool, optional): Whether to combine results into a single file. Defaults to False.
49
+ rtype (Literal["table", "audacity", "kaleidoscope", "csv"] | List[Literal["table", "audacity", "kaleidoscope", "csv"]], optional):
50
+ Output format(s) for results. Defaults to "table".
51
+ skip_existing_results (bool, optional): Whether to skip analysis for files with existing results. Defaults to False.
52
+ sf_thresh (float, optional): Threshold for species filtering. Defaults to 0.03.
53
+ top_n (int | None, optional): Limit the number of top detections per file. Defaults to None.
54
+ merge_consecutive (int, optional): Merge consecutive detections within this time window in seconds. Defaults to 1.
55
+ threads (int, optional): Number of CPU threads to use for analysis. Defaults to 8.
56
+ locale (str, optional): Locale for species names and output. Defaults to "en".
57
+ Returns:
58
+ None
59
+ Raises:
60
+ ValueError: If input path is invalid or required parameters are missing.
61
+ Notes:
62
+ - The function ensures the BirdNET model is available before analysis.
63
+ - Results can be combined into a single file if `combine_results` is True.
64
+ - Analysis parameters are saved to a file in the output directory.
65
+ """
66
+ from multiprocessing import Pool
67
+
68
+ import birdnet_analyzer.config as cfg
69
+ from birdnet_analyzer.analyze.utils import analyze_file, save_analysis_params
70
+ from birdnet_analyzer.analyze.utils import combine_results as combine
71
+ from birdnet_analyzer.utils import ensure_model_exists
72
+
73
+ ensure_model_exists()
74
+
75
+ flist = _set_params(
76
+ input=input,
77
+ output=output,
78
+ min_conf=min_conf,
79
+ custom_classifier=classifier,
80
+ lat=lat,
81
+ lon=lon,
82
+ week=week,
83
+ slist=slist,
84
+ sensitivity=sensitivity,
85
+ locale=locale,
86
+ overlap=overlap,
87
+ fmin=fmin,
88
+ fmax=fmax,
89
+ audio_speed=audio_speed,
90
+ bs=batch_size,
91
+ combine_results=combine_results,
92
+ rtype=rtype,
93
+ sf_thresh=sf_thresh,
94
+ top_n=top_n,
95
+ merge_consecutive=merge_consecutive,
96
+ skip_existing_results=skip_existing_results,
97
+ threads=threads,
98
+ labels_file=cfg.LABELS_FILE,
99
+ )
100
+
101
+ print(f"Found {len(cfg.FILE_LIST)} files to analyze")
102
+
103
+ if not cfg.SPECIES_LIST:
104
+ print(f"Species list contains {len(cfg.LABELS)} species")
105
+ else:
106
+ print(f"Species list contains {len(cfg.SPECIES_LIST)} species")
107
+
108
+ result_files = []
109
+
110
+ # Analyze files
111
+ if cfg.CPU_THREADS < 2 or len(flist) < 2:
112
+ for entry in flist:
113
+ result_files.append(analyze_file(entry))
114
+ else:
115
+ with Pool(cfg.CPU_THREADS) as p:
116
+ # Map analyzeFile function to each entry in flist
117
+ results = p.map_async(analyze_file, flist)
118
+ # Wait for all tasks to complete
119
+ results.wait()
120
+ result_files = results.get()
121
+
122
+ # Combine results?
123
+ if cfg.COMBINE_RESULTS:
124
+ print(f"Combining results, writing to {cfg.OUTPUT_PATH}...", end="", flush=True)
125
+ combine(result_files)
126
+ print("done!", flush=True)
127
+
128
+ save_analysis_params(os.path.join(cfg.OUTPUT_PATH, cfg.ANALYSIS_PARAMS_FILENAME))
129
+
130
+
131
+ def _set_params(
132
+ input,
133
+ output,
134
+ min_conf,
135
+ custom_classifier,
136
+ lat,
137
+ lon,
138
+ week,
139
+ slist,
140
+ sensitivity,
141
+ locale,
142
+ overlap,
143
+ fmin,
144
+ fmax,
145
+ audio_speed,
146
+ bs,
147
+ combine_results,
148
+ rtype,
149
+ skip_existing_results,
150
+ sf_thresh,
151
+ top_n,
152
+ merge_consecutive,
153
+ threads,
154
+ labels_file=None,
155
+ ):
156
+ import birdnet_analyzer.config as cfg
157
+ from birdnet_analyzer.analyze.utils import load_codes # noqa: E402
158
+ from birdnet_analyzer.species.utils import get_species_list
159
+ from birdnet_analyzer.utils import collect_audio_files, read_lines
160
+
161
+ cfg.CODES = load_codes()
162
+ cfg.LABELS = read_lines(labels_file if labels_file else cfg.LABELS_FILE)
163
+ cfg.SKIP_EXISTING_RESULTS = skip_existing_results
164
+ cfg.LOCATION_FILTER_THRESHOLD = sf_thresh
165
+ cfg.TOP_N = top_n
166
+ cfg.MERGE_CONSECUTIVE = merge_consecutive
167
+ cfg.INPUT_PATH = input
168
+ cfg.MIN_CONFIDENCE = min_conf
169
+ cfg.SIGMOID_SENSITIVITY = sensitivity
170
+ cfg.SIG_OVERLAP = overlap
171
+ cfg.BANDPASS_FMIN = fmin
172
+ cfg.BANDPASS_FMAX = fmax
173
+ cfg.AUDIO_SPEED = audio_speed
174
+ cfg.RESULT_TYPES = rtype
175
+ cfg.COMBINE_RESULTS = combine_results
176
+ cfg.BATCH_SIZE = bs
177
+
178
+ if not output:
179
+ if os.path.isfile(cfg.INPUT_PATH):
180
+ cfg.OUTPUT_PATH = os.path.dirname(cfg.INPUT_PATH)
181
+ else:
182
+ cfg.OUTPUT_PATH = cfg.INPUT_PATH
183
+ else:
184
+ cfg.OUTPUT_PATH = output
185
+
186
+ if os.path.isdir(cfg.INPUT_PATH):
187
+ cfg.FILE_LIST = collect_audio_files(cfg.INPUT_PATH)
188
+ else:
189
+ cfg.FILE_LIST = [cfg.INPUT_PATH]
190
+
191
+ if os.path.isdir(cfg.INPUT_PATH):
192
+ cfg.CPU_THREADS = threads
193
+ cfg.TFLITE_THREADS = 1
194
+ else:
195
+ cfg.CPU_THREADS = 1
196
+ cfg.TFLITE_THREADS = threads
197
+
198
+ if custom_classifier is not None:
199
+ cfg.CUSTOM_CLASSIFIER = custom_classifier # we treat this as absolute path, so no need to join with dirname
200
+
201
+ if custom_classifier.endswith(".tflite"):
202
+ cfg.LABELS_FILE = custom_classifier.replace(".tflite", "_Labels.txt") # same for labels file
203
+
204
+ if not os.path.isfile(cfg.LABELS_FILE):
205
+ cfg.LABELS_FILE = custom_classifier.replace("Model_FP32.tflite", "Labels.txt")
206
+
207
+ cfg.LABELS = read_lines(cfg.LABELS_FILE)
208
+ else:
209
+ cfg.APPLY_SIGMOID = False
210
+ # our output format
211
+ cfg.LABELS_FILE = os.path.join(custom_classifier, "labels", "label_names.csv")
212
+
213
+ if not os.path.isfile(cfg.LABELS_FILE):
214
+ cfg.LABELS_FILE = os.path.join(custom_classifier, "assets", "label.csv")
215
+ cfg.LABELS = read_lines(cfg.LABELS_FILE)
216
+ else:
217
+ cfg.LABELS = [line.split(",")[1] for line in read_lines(cfg.LABELS_FILE)]
218
+ else:
219
+ cfg.LATITUDE, cfg.LONGITUDE, cfg.WEEK = lat, lon, week
220
+ cfg.CUSTOM_CLASSIFIER = None
221
+
222
+ if cfg.LATITUDE == -1 and cfg.LONGITUDE == -1:
223
+ if not slist:
224
+ cfg.SPECIES_LIST_FILE = None
225
+ else:
226
+ cfg.SPECIES_LIST_FILE = slist
227
+
228
+ if os.path.isdir(cfg.SPECIES_LIST_FILE):
229
+ cfg.SPECIES_LIST_FILE = os.path.join(cfg.SPECIES_LIST_FILE, "species_list.txt")
230
+
231
+ cfg.SPECIES_LIST = read_lines(cfg.SPECIES_LIST_FILE)
232
+ else:
233
+ cfg.SPECIES_LIST_FILE = None
234
+ cfg.SPECIES_LIST = get_species_list(cfg.LATITUDE, cfg.LONGITUDE, cfg.WEEK, cfg.LOCATION_FILTER_THRESHOLD)
235
+
236
+ lfile = os.path.join(
237
+ cfg.TRANSLATED_LABELS_PATH, os.path.basename(cfg.LABELS_FILE).replace(".txt", "_{}.txt".format(locale))
238
+ )
239
+
240
+ if locale not in ["en"] and os.path.isfile(lfile):
241
+ cfg.TRANSLATED_LABELS = read_lines(lfile)
242
+ else:
243
+ cfg.TRANSLATED_LABELS = cfg.LABELS
244
+
245
+ return [(f, cfg.get_config()) for f in cfg.FILE_LIST]