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,104 @@
1
+ """Module for translating species labels.
2
+
3
+ Can be used to translate species names into other languages.
4
+
5
+ Uses the requests to the eBird-API.
6
+ """
7
+ import json
8
+ import os
9
+ import urllib.request
10
+
11
+ import birdnet_analyzer.config as cfg
12
+ import birdnet_analyzer.utils as utils
13
+
14
+ LOCALES = ['af', 'ar', 'cs', 'da', 'de', 'en_uk', 'es', 'fi', 'fr', 'hu', 'it', 'ja', 'ko', 'nl', 'no', 'pl', 'pt_BR', 'pt_PT', 'ro', 'ru', 'sk', 'sl', 'sv', 'th', 'tr', 'uk', 'zh']
15
+ """ Locales for 26 common languages (according to GitHub Copilot) """
16
+
17
+ API_TOKEN = "yourAPIToken"
18
+ """ Sign up for your personal access token here: https://ebird.org/api/keygen """
19
+
20
+
21
+ def get_locale_data(locale: str):
22
+ """Download eBird locale species data.
23
+
24
+ Requests the locale data through the eBird API.
25
+
26
+ Args:
27
+ locale: Two character string of a language.
28
+
29
+ Returns:
30
+ A data object containing the response from eBird.
31
+ """
32
+ url = "https://api.ebird.org/v2/ref/taxonomy/ebird?cat=species&fmt=json&locale=" + locale
33
+ header = {"X-eBirdAPIToken": API_TOKEN}
34
+
35
+ req = urllib.request.Request(url, headers=header)
36
+ response = urllib.request.urlopen(req)
37
+
38
+ return json.loads(response.read())
39
+
40
+
41
+ def translate(locale: str):
42
+ """Translates species names for a locale.
43
+
44
+ Translates species names for the given language with the eBird API.
45
+
46
+ Args:
47
+ locale: Two character string of a language.
48
+
49
+ Returns:
50
+ The translated list of labels.
51
+ """
52
+ print(f"Translating species names for {locale}...", end="", flush=True)
53
+
54
+ # Get locale data
55
+ data = get_locale_data(locale)
56
+
57
+ # Create list of translated labels
58
+ labels: list[str] = []
59
+
60
+ for label in cfg.LABELS:
61
+ has_translation = False
62
+ for entry in data:
63
+ if label.split("_", 1)[0] == entry["sciName"]:
64
+ labels.append("{}_{}".format(label.split("_", 1)[0], entry["comName"]))
65
+ has_translation = True
66
+ break
67
+ if not has_translation:
68
+ labels.append(label)
69
+
70
+ print("Done.", flush=True)
71
+
72
+ return labels
73
+
74
+
75
+ def save_labels_file(labels: list[str], locale: str):
76
+ """Saves localized labels to a file.
77
+
78
+ Saves the given labels into a file with the format:
79
+ "{config.LABELSFILE}_{locale}.txt"
80
+
81
+ Args:
82
+ labels: List of labels.
83
+ locale: Two character string of a language.
84
+ """
85
+ # Create folder
86
+ os.makedirs(cfg.TRANSLATED_LABELS_PATH, exist_ok=True)
87
+
88
+ # Save labels file
89
+ fpath = os.path.join(
90
+ cfg.TRANSLATED_LABELS_PATH, "{}_{}.txt".format(os.path.basename(cfg.LABELS_FILE).rsplit(".", 1)[0], locale)
91
+ )
92
+ with open(fpath, "w", encoding="utf-8") as f:
93
+ for label in labels:
94
+ f.write(label + "\n")
95
+
96
+
97
+ if __name__ == "__main__":
98
+ # Load labels
99
+ cfg.LABELS = utils.read_lines(cfg.LABELS_FILE)
100
+
101
+ # Translate labels
102
+ for locale in LOCALES:
103
+ labels = translate(locale)
104
+ save_labels_file(labels, locale)
@@ -0,0 +1,419 @@
1
+ """Module containing common function."""
2
+
3
+ import sys
4
+ import itertools
5
+ import os
6
+ import traceback
7
+ from pathlib import Path
8
+
9
+ import birdnet_analyzer.config as cfg
10
+
11
+ SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
12
+ FROZEN = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
13
+
14
+
15
+ def runtime_error_handler(f: callable):
16
+ """Decorator to catch runtime errors and write them to the error log.
17
+
18
+ Args:
19
+ f: The function to be decorated.
20
+
21
+ Returns:
22
+ The decorated function.
23
+ """
24
+
25
+ def wrapper(*args, **kwargs):
26
+ try:
27
+ return f(*args, **kwargs)
28
+ except Exception as ex:
29
+ write_error_log(ex)
30
+ raise
31
+
32
+ return wrapper
33
+
34
+
35
+ def batched(iterable, n, *, strict=False):
36
+ # TODO: Remove this function when Python 3.12 is the minimum version
37
+ # batched('ABCDEFG', 3) → ABC DEF G
38
+ if n < 1:
39
+ raise ValueError("n must be at least one")
40
+ iterator = iter(iterable)
41
+ while batch := tuple(itertools.islice(iterator, n)):
42
+ if strict and len(batch) != n:
43
+ raise ValueError("batched(): incomplete batch")
44
+ yield batch
45
+
46
+
47
+ def spectrogram_from_file(path, fig_num=None, fig_size=None, offset=0, duration=None, fmin=None, fmax=None, speed=1.0):
48
+ """
49
+ Generate a spectrogram from an audio file.
50
+
51
+ Parameters:
52
+ path (str): The path to the audio file.
53
+
54
+ Returns:
55
+ matplotlib.figure.Figure: The generated spectrogram figure.
56
+ """
57
+ import birdnet_analyzer.audio as audio
58
+
59
+ # s, sr = librosa.load(path, offset=offset, duration=duration)
60
+ s, sr = audio.open_audio_file(path, offset=offset, duration=duration, fmin=fmin, fmax=fmax, speed=speed)
61
+
62
+ return spectrogram_from_audio(s, sr, fig_num, fig_size)
63
+
64
+
65
+ def spectrogram_from_audio(s, sr, fig_num=None, fig_size=None):
66
+ """
67
+ Generate a spectrogram from an audio signal.
68
+
69
+ Parameters:
70
+ s: The signal
71
+ sr: The sample rate
72
+
73
+ Returns:
74
+ matplotlib.figure.Figure: The generated spectrogram figure.
75
+ """
76
+ import librosa
77
+ import librosa.display
78
+ import matplotlib
79
+ import matplotlib.pyplot as plt
80
+ import numpy as np
81
+
82
+ matplotlib.use("agg")
83
+
84
+ if isinstance(fig_size, tuple):
85
+ f = plt.figure(fig_num, figsize=fig_size)
86
+ elif fig_size == "auto":
87
+ duration = librosa.get_duration(y=s, sr=sr)
88
+ width = min(12, max(3, duration / 10))
89
+ f = plt.figure(fig_num, figsize=(width, 3))
90
+ else:
91
+ f = plt.figure(fig_num)
92
+
93
+ f.clf()
94
+
95
+ ax = f.add_subplot(111)
96
+
97
+ ax.set_axis_off()
98
+ f.tight_layout(pad=0)
99
+
100
+ D = librosa.stft(s, n_fft=1024, hop_length=512) # STFT of y
101
+ S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max)
102
+
103
+ return librosa.display.specshow(S_db, ax=ax, n_fft=1024, hop_length=512).figure
104
+
105
+
106
+ def collect_audio_files(path: str, max_files: int = None):
107
+ """Collects all audio files in the given directory.
108
+
109
+ Args:
110
+ path: The directory to be searched.
111
+
112
+ Returns:
113
+ A sorted list of all audio files in the directory.
114
+ """
115
+ # Get all files in directory with os.walk
116
+ files = []
117
+
118
+ for root, _, flist in os.walk(path):
119
+ for f in flist:
120
+ if not f.startswith(".") and f.rsplit(".", 1)[-1].lower() in cfg.ALLOWED_FILETYPES:
121
+ files.append(os.path.join(root, f))
122
+
123
+ if max_files and len(files) >= max_files:
124
+ return sorted(files)
125
+
126
+ return sorted(files)
127
+
128
+
129
+ def collect_all_files(path: str, filetypes: list[str], pattern: str = ""):
130
+ """Collects all files of the given filetypes in the given directory.
131
+
132
+ Args:
133
+ path: The directory to be searched.
134
+ filetypes: A list of filetypes to be collected.
135
+
136
+ Returns:
137
+ A sorted list of all files in the directory.
138
+ """
139
+
140
+ files = []
141
+
142
+ for root, _, flist in os.walk(path):
143
+ for f in flist:
144
+ if not f.startswith(".") and f.rsplit(".", 1)[-1].lower() in filetypes and (pattern in f or not pattern):
145
+ files.append(os.path.join(root, f))
146
+
147
+ return sorted(files)
148
+
149
+
150
+ def read_lines(path: str):
151
+ """Reads the lines into a list.
152
+
153
+ Opens the file and reads its contents into a list.
154
+ It is expected to have one line for each species or label.
155
+
156
+ Args:
157
+ path: Absolute path to the species file.
158
+
159
+ Returns:
160
+ A list of all species inside the file.
161
+ """
162
+ return Path(path).read_text(encoding="utf-8").splitlines() if path else []
163
+
164
+
165
+ def list_subdirectories(path: str):
166
+ """Lists all directories inside a path.
167
+
168
+ Retrieves all the subdirectories in a given path without recursion.
169
+
170
+ Args:
171
+ path: Directory to be searched.
172
+
173
+ Returns:
174
+ A filter sequence containing the absolute paths to all directories.
175
+ """
176
+ return filter(lambda el: os.path.isdir(os.path.join(path, el)), os.listdir(path))
177
+
178
+
179
+ def save_to_cache(path, x_train, y_train, x_test, y_test, labels):
180
+ """Saves training data to cache.
181
+
182
+ Args:
183
+ path: Path to the cache file.
184
+ x_train: Training samples.
185
+ y_train: Training labels.
186
+ x_test: Test samples.
187
+ y_test: Test labels.
188
+ labels: Labels.
189
+ """
190
+ import numpy as np
191
+
192
+ # Make directory if needed
193
+ directory = os.path.dirname(path)
194
+ if directory and not os.path.exists(directory):
195
+ os.makedirs(directory)
196
+
197
+ # Save cache file with training data, test data, labels and configuration
198
+ np.savez(
199
+ path,
200
+ x_train=x_train,
201
+ y_train=y_train,
202
+ x_test=x_test,
203
+ y_test=y_test,
204
+ labels=np.array(labels, dtype=object),
205
+ binary_classification=cfg.BINARY_CLASSIFICATION,
206
+ multi_label=cfg.MULTI_LABEL,
207
+ fmin=cfg.BANDPASS_FMIN,
208
+ fmax=cfg.BANDPASS_FMAX,
209
+ audio_speed=cfg.AUDIO_SPEED,
210
+ crop_mode=cfg.SAMPLE_CROP_MODE,
211
+ overlap=cfg.SIG_OVERLAP,
212
+ )
213
+
214
+
215
+ def load_from_cache(path):
216
+ """Loads training data from cache.
217
+
218
+ Args:
219
+ path: Path to the cache file.
220
+
221
+ Returns:
222
+ A tuple of (x_train, y_train, labels, binary_classification, multi_label).
223
+ """
224
+ import numpy as np
225
+
226
+ # Load cache file
227
+ data = np.load(path, allow_pickle=True)
228
+
229
+ # Check if cache contains needed preprocessing parameters
230
+ if "fmin" in data and "fmax" in data and "audio_speed" in data and "crop_mode" in data and "overlap" in data:
231
+ # Check if preprocessing parameters match current settings
232
+ if (
233
+ data["fmin"] != cfg.BANDPASS_FMIN
234
+ or data["fmax"] != cfg.BANDPASS_FMAX
235
+ or data["audio_speed"] != cfg.AUDIO_SPEED
236
+ or data["crop_mode"] != cfg.SAMPLE_CROP_MODE
237
+ or data["overlap"] != cfg.SIG_OVERLAP
238
+ ):
239
+ print("\t...WARNING: Cache preprocessing parameters don't match current settings!", flush=True)
240
+ print(f"\t Cache: fmin={data['fmin']}, fmax={data['fmax']}, speed={data['audio_speed']}", flush=True)
241
+ print(f"\t Cache: crop_mode={data['crop_mode']}, overlap={data['overlap']}", flush=True)
242
+ print(
243
+ f"\t Current: fmin={cfg.BANDPASS_FMIN}, fmax={cfg.BANDPASS_FMAX}, speed={cfg.AUDIO_SPEED}", flush=True
244
+ )
245
+ print(f"\t Current: crop_mode={cfg.SAMPLE_CROP_MODE}, overlap={cfg.SIG_OVERLAP}", flush=True)
246
+
247
+ # Extract and return data
248
+ x_train = data["x_train"]
249
+ y_train = data["y_train"]
250
+
251
+ # Handle test data which might not be in older cache files
252
+ x_test = data.get("x_test", np.array([]))
253
+ y_test = data.get("y_test", np.array([]))
254
+
255
+ labels = data["labels"]
256
+ binary_classification = bool(data.get("binary_classification", False))
257
+ multi_label = bool(data.get("multi_label", False))
258
+
259
+ return x_train, y_train, x_test, y_test, labels, binary_classification, multi_label
260
+
261
+
262
+ def clear_error_log():
263
+ """Clears the error log file.
264
+
265
+ For debugging purposes.
266
+ """
267
+ if os.path.isfile(cfg.ERROR_LOG_FILE):
268
+ os.remove(cfg.ERROR_LOG_FILE)
269
+
270
+
271
+ def write_error_log(ex: Exception):
272
+ """Writes an exception to the error log.
273
+
274
+ Formats the stacktrace and writes it in the error log file configured in the config.
275
+
276
+ Args:
277
+ ex: An exception that occurred.
278
+ """
279
+ import datetime
280
+
281
+ with open(cfg.ERROR_LOG_FILE, "a") as elog:
282
+ elog.write(
283
+ datetime.datetime.now().strftime("[%Y-%m-%d %H:%M:%S]")
284
+ + "\n"
285
+ + "".join(traceback.TracebackException.from_exception(ex).format())
286
+ + "\n"
287
+ )
288
+
289
+
290
+ def img2base64(path):
291
+ import base64
292
+
293
+ with open(path, "rb") as img_file:
294
+ return base64.b64encode(img_file.read()).decode("utf-8")
295
+
296
+
297
+ def save_params(file_path, headers, values):
298
+ """Saves the params used to train the custom classifier.
299
+
300
+ The hyperparams will be saved to disk in a file named 'model_params.csv'.
301
+
302
+ Args:
303
+ file_path: The path to the file.
304
+ headers: The headers of the csv file.
305
+ values: The values of the csv file.
306
+ """
307
+ import csv
308
+
309
+ with open(file_path, "w", newline="") as paramsfile:
310
+ paramswriter = csv.writer(paramsfile)
311
+ paramswriter.writerow(headers)
312
+ paramswriter.writerow(values)
313
+
314
+
315
+ def save_result_file(result_path: str, out_string: str):
316
+ """Saves the result to a file.
317
+
318
+ Args:
319
+ result_path: The path to the result file.
320
+ out_string: The string to be written to the file.
321
+ """
322
+
323
+ # Make directory if it doesn't exist
324
+ os.makedirs(os.path.dirname(result_path), exist_ok=True)
325
+
326
+ # Write the result to the file
327
+ with open(result_path, "w", encoding="utf-8") as rfile:
328
+ rfile.write(out_string)
329
+
330
+
331
+ def check_model_files():
332
+ checkpoint_dir = os.path.join(SCRIPT_DIR, "checkpoints", "V2.4")
333
+ required_files = [
334
+ "BirdNET_GLOBAL_6K_V2.4_Model/variables/variables.data-00000-of-00001",
335
+ "BirdNET_GLOBAL_6K_V2.4_Model/variables/variables.index",
336
+ "BirdNET_GLOBAL_6K_V2.4_Model/saved_model.pb",
337
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/mdata/group1-shard1of8.bin",
338
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/mdata/group1-shard2of8.bin",
339
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/mdata/group1-shard3of8.bin",
340
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/mdata/group1-shard4of8.bin",
341
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/mdata/group1-shard5of8.bin",
342
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/mdata/group1-shard6of8.bin",
343
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/mdata/group1-shard7of8.bin",
344
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/mdata/group1-shard8of8.bin",
345
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/mdata/model.json",
346
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard1of13.bin",
347
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard2of13.bin",
348
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard3of13.bin",
349
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard4of13.bin",
350
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard5of13.bin",
351
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard6of13.bin",
352
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard7of13.bin",
353
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard8of13.bin",
354
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard9of13.bin",
355
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard10of13.bin",
356
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard11of13.bin",
357
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard12of13.bin",
358
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/group1-shard13of13.bin",
359
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/model.json",
360
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/model/labels.json",
361
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/main.js",
362
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/static/sample.wav",
363
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/templates/index.html",
364
+ "BirdNET_GLOBAL_6K_V2.4_Model_TFJS/app.py",
365
+ "BirdNET_GLOBAL_6K_V2.4_Labels.txt",
366
+ "BirdNET_GLOBAL_6K_V2.4_MData_Model_V2_FP16.tflite",
367
+ "BirdNET_GLOBAL_6K_V2.4_Model_FP16.tflite",
368
+ "BirdNET_GLOBAL_6K_V2.4_Model_FP32.tflite",
369
+ "BirdNET_GLOBAL_6K_V2.4_Model_INT8.tflite",
370
+ ]
371
+
372
+ for file in required_files:
373
+ if not os.path.exists(os.path.join(checkpoint_dir, file)):
374
+ print(f"Missing {file}")
375
+
376
+ return False
377
+
378
+ print("Model found!")
379
+
380
+ return True
381
+
382
+
383
+ def ensure_model_exists():
384
+ import zipfile
385
+
386
+ import requests
387
+ from tqdm import tqdm
388
+
389
+ if FROZEN or check_model_files():
390
+ return
391
+
392
+ checkpoint_dir = os.path.join(SCRIPT_DIR, "checkpoints")
393
+
394
+ os.makedirs(checkpoint_dir, exist_ok=True)
395
+
396
+ url = "https://tuc.cloud/index.php/s/3BsizWy5M7CtQ5w/download/V2.4.zip"
397
+ download_path = os.path.join(checkpoint_dir, "V2.4.zip")
398
+
399
+ response = requests.get(url, stream=True, timeout=30)
400
+ total_size = int(response.headers.get("content-length", 0))
401
+ block_size = 1024
402
+
403
+ with tqdm(total=total_size, unit="iB", unit_scale=True, desc="Downloading model") as tqdm_bar:
404
+ with open(download_path, "wb") as file:
405
+ for data in response.iter_content(block_size):
406
+ tqdm_bar.update(len(data))
407
+ file.write(data)
408
+
409
+ if response.status_code != 200 or (total_size not in (0, tqdm_bar.n)):
410
+ raise ValueError(f"Failed to download the file. Status code: {response.status_code}")
411
+
412
+ with zipfile.ZipFile(download_path, "r") as zip_ref:
413
+ zip_ref.extractall(os.path.dirname(download_path))
414
+
415
+ os.remove(download_path)
416
+
417
+
418
+ if __name__ == "__main__":
419
+ ensure_model_exists()
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: birdnet_analyzer
3
+ Version: 2.0.0
4
+ Summary: BirdNET analyzer for scientific audio data processing and bird classification.
5
+ Author: Stefan Kahl
6
+ Maintainer: Josef Haupt, Max Mauermann
7
+ License: MIT
8
+ Project-URL: Homepage, https://birdnet.cornell.edu/birdnet
9
+ Project-URL: Documentation, https://birdnet-team.github.io/BirdNET-Analyzer/
10
+ Project-URL: Repository, https://github.com/birdnet-team/BirdNET-Analyzer
11
+ Project-URL: Issues, https://github.com/birdnet-team/BirdNET-Analyzer/issues
12
+ Project-URL: Download, https://github.com/birdnet-team/BirdNET-Analyzer/releases/latest
13
+ Keywords: birdnet,birdnet-analyzer
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: librosa
24
+ Requires-Dist: resampy
25
+ Requires-Dist: tensorflow==2.15.1
26
+ Requires-Dist: scikit-learn==1.6.1
27
+ Requires-Dist: tqdm
28
+ Provides-Extra: train
29
+ Requires-Dist: keras-tuner; extra == "train"
30
+ Provides-Extra: server
31
+ Requires-Dist: bottle; extra == "server"
32
+ Requires-Dist: requests; extra == "server"
33
+ Provides-Extra: gui
34
+ Requires-Dist: birdnet-analyzer[embeddings,train]; extra == "gui"
35
+ Requires-Dist: gradio==5.25.2; extra == "gui"
36
+ Requires-Dist: pywebview; extra == "gui"
37
+ Requires-Dist: matplotlib; extra == "gui"
38
+ Requires-Dist: plotly[express]; extra == "gui"
39
+ Requires-Dist: seaborn; extra == "gui"
40
+ Requires-Dist: pywin32; platform_system == "Windows" and extra == "gui"
41
+ Provides-Extra: embeddings
42
+ Requires-Dist: perch-hoplite; extra == "embeddings"
43
+ Provides-Extra: all
44
+ Requires-Dist: birdnet-analyzer[gui,server]; extra == "all"
45
+ Dynamic: license-file
46
+
47
+ <div align="center">
48
+ <h1>BirdNET-Analyzer</h1>
49
+ <a href="https://birdnet-team.github.io/BirdNET-Analyzer/">
50
+ <img src="https://github.com/birdnet-team/BirdNET-Analyzer/blob/main/docs/_static/logo_birdnet_big.png?raw=true" width="300" alt="BirdNET-Logo" />
51
+ </a>
52
+ </div>
53
+ <br>
54
+ <div align="center">
55
+
56
+ ![License](https://img.shields.io/github/license/birdnet-team/BirdNET-Analyzer)
57
+ ![OS](https://badgen.net/badge/OS/Linux%2C%20Windows%2C%20macOS/blue)
58
+ [![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/release/python-3110/)
59
+ ![Species](https://badgen.net/badge/Species/6512/blue)
60
+ ![Downloads](https://www-user.tu-chemnitz.de/~johau/birdnet_total_downloads_badge.php)
61
+
62
+ [![Docker](https://github.com/birdnet-team/BirdNET-Analyzer/actions/workflows/docker-build.yml/badge.svg)](https://github.com/birdnet-team/BirdNET-Analyzer/actions/workflows/docker-build.yml)
63
+ [![Reddit](https://img.shields.io/badge/Reddit-FF4500?style=flat&logo=reddit&logoColor=white)](https://www.reddit.com/r/BirdNET_Analyzer/)
64
+ ![GitHub stars)](https://img.shields.io/github/stars/birdnet-team/BirdNET-Analyzer)
65
+ [![GitHub release](https://img.shields.io/github/v/release/birdnet-team/BirdNET-Analyzer)](https://github.com/birdnet-team/BirdNET-Analyzer/releases/latest)
66
+
67
+ </div>
68
+
69
+ This repo contains BirdNET models and scripts for processing large amounts of audio data or single audio files.
70
+ This is the most advanced version of BirdNET for acoustic analyses and we will keep this repository up-to-date with new models and improved interfaces to enable scientists with no CS background to run the analysis.
71
+
72
+ Feel free to use BirdNET for your acoustic analyses and research.
73
+ If you do, please cite as:
74
+
75
+ ```bibtex
76
+ @article{kahl2021birdnet,
77
+ title={BirdNET: A deep learning solution for avian diversity monitoring},
78
+ author={Kahl, Stefan and Wood, Connor M and Eibl, Maximilian and Klinck, Holger},
79
+ journal={Ecological Informatics},
80
+ volume={61},
81
+ pages={101236},
82
+ year={2021},
83
+ publisher={Elsevier}
84
+ }
85
+ ```
86
+
87
+ ## Documentation
88
+
89
+ You can access documentation for this project [here](https://birdnet-team.github.io/BirdNET-Analyzer/).
90
+
91
+ ## Download
92
+
93
+ You can download installers for Windows and macOS from the [releases page](https://github.com/birdnet-team/BirdNET-Analyzer/releases/latest).
94
+
95
+ ## About
96
+
97
+ Developed by the [K. Lisa Yang Center for Conservation Bioacoustics](https://www.birds.cornell.edu/ccb/) at the [Cornell Lab of Ornithology](https://www.birds.cornell.edu/home) in collaboration with [Chemnitz University of Technology](https://www.tu-chemnitz.de/index.html.en).
98
+
99
+ Go to https://birdnet.cornell.edu to learn more about the project.
100
+
101
+ Want to use BirdNET to analyze a large dataset? Don't hesitate to contact us: ccb-birdnet@cornell.edu
102
+
103
+ **Have a question, remark, or feature request? Please start a new issue thread to let us know. Feel free to submit a pull request.**
104
+
105
+ ## License
106
+
107
+ - **Source Code**: The source code for this project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
108
+ - **Models**: The models used in this project are licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License (CC BY-NC-SA 4.0)](https://creativecommons.org/licenses/by-nc-sa/4.0/).
109
+
110
+ Please ensure you review and adhere to the specific license terms provided with each model.
111
+
112
+ *Please note that all educational and research purposes are considered non-commercial use and it is therefore freely permitted to use BirdNET models in any way.*
113
+
114
+ ## Funding
115
+
116
+ This project is supported by Jake Holshuh (Cornell class of ´69) and The Arthur Vining Davis Foundations.
117
+ Our work in the K. Lisa Yang Center for Conservation Bioacoustics is made possible by the generosity of K. Lisa Yang to advance innovative conservation technologies to inspire and inform the conservation of wildlife and habitats.
118
+
119
+ The development of BirdNET is supported by the German Federal Ministry of Education and Research through the project “BirdNET+” (FKZ 01|S22072).
120
+ The German Federal Ministry for the Environment, Nature Conservation and Nuclear Safety contributes through the “DeepBirdDetect” project (FKZ 67KI31040E).
121
+ In addition, the Deutsche Bundesstiftung Umwelt supports BirdNET through the project “RangerSound” (project 39263/01).
122
+
123
+ ## Partners
124
+
125
+ BirdNET is a joint effort of partners from academia and industry.
126
+ Without these partnerships, this project would not have been possible.
127
+ Thank you!
128
+
129
+ ![Logos of all partners](https://tuc.cloud/index.php/s/KSdWfX5CnSRpRgQ/download/box_logos.png)