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.
Files changed (122) hide show
  1. birdnet_analyzer/__init__.py +9 -8
  2. birdnet_analyzer/analyze/__init__.py +5 -5
  3. birdnet_analyzer/analyze/__main__.py +3 -4
  4. birdnet_analyzer/analyze/cli.py +25 -25
  5. birdnet_analyzer/analyze/core.py +241 -245
  6. birdnet_analyzer/analyze/utils.py +692 -701
  7. birdnet_analyzer/audio.py +368 -372
  8. birdnet_analyzer/cli.py +709 -707
  9. birdnet_analyzer/config.py +242 -242
  10. birdnet_analyzer/eBird_taxonomy_codes_2021E.json +25279 -25279
  11. birdnet_analyzer/embeddings/__init__.py +3 -4
  12. birdnet_analyzer/embeddings/__main__.py +3 -3
  13. birdnet_analyzer/embeddings/cli.py +12 -13
  14. birdnet_analyzer/embeddings/core.py +69 -70
  15. birdnet_analyzer/embeddings/utils.py +179 -193
  16. birdnet_analyzer/evaluation/__init__.py +196 -195
  17. birdnet_analyzer/evaluation/__main__.py +3 -3
  18. birdnet_analyzer/evaluation/assessment/__init__.py +0 -0
  19. birdnet_analyzer/evaluation/assessment/metrics.py +388 -0
  20. birdnet_analyzer/evaluation/assessment/performance_assessor.py +409 -0
  21. birdnet_analyzer/evaluation/assessment/plotting.py +379 -0
  22. birdnet_analyzer/evaluation/preprocessing/__init__.py +0 -0
  23. birdnet_analyzer/evaluation/preprocessing/data_processor.py +631 -0
  24. birdnet_analyzer/evaluation/preprocessing/utils.py +98 -0
  25. birdnet_analyzer/gui/__init__.py +19 -23
  26. birdnet_analyzer/gui/__main__.py +3 -3
  27. birdnet_analyzer/gui/analysis.py +175 -174
  28. birdnet_analyzer/gui/assets/arrow_down.svg +4 -4
  29. birdnet_analyzer/gui/assets/arrow_left.svg +4 -4
  30. birdnet_analyzer/gui/assets/arrow_right.svg +4 -4
  31. birdnet_analyzer/gui/assets/arrow_up.svg +4 -4
  32. birdnet_analyzer/gui/assets/gui.css +28 -28
  33. birdnet_analyzer/gui/assets/gui.js +93 -93
  34. birdnet_analyzer/gui/embeddings.py +619 -620
  35. birdnet_analyzer/gui/evaluation.py +795 -813
  36. birdnet_analyzer/gui/localization.py +75 -68
  37. birdnet_analyzer/gui/multi_file.py +245 -246
  38. birdnet_analyzer/gui/review.py +519 -527
  39. birdnet_analyzer/gui/segments.py +191 -191
  40. birdnet_analyzer/gui/settings.py +128 -129
  41. birdnet_analyzer/gui/single_file.py +267 -269
  42. birdnet_analyzer/gui/species.py +95 -95
  43. birdnet_analyzer/gui/train.py +696 -698
  44. birdnet_analyzer/gui/utils.py +810 -808
  45. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_af.txt +6522 -6522
  46. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ar.txt +6522 -6522
  47. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_bg.txt +6522 -6522
  48. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ca.txt +6522 -6522
  49. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_cs.txt +6522 -6522
  50. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_da.txt +6522 -6522
  51. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_de.txt +6522 -6522
  52. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_el.txt +6522 -6522
  53. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_en_uk.txt +6522 -6522
  54. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_es.txt +6522 -6522
  55. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_fi.txt +6522 -6522
  56. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_fr.txt +6522 -6522
  57. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_he.txt +6522 -6522
  58. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_hr.txt +6522 -6522
  59. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_hu.txt +6522 -6522
  60. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_in.txt +6522 -6522
  61. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_is.txt +6522 -6522
  62. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_it.txt +6522 -6522
  63. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ja.txt +6522 -6522
  64. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ko.txt +6522 -6522
  65. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_lt.txt +6522 -6522
  66. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ml.txt +6522 -6522
  67. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_nl.txt +6522 -6522
  68. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_no.txt +6522 -6522
  69. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pl.txt +6522 -6522
  70. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pt_BR.txt +6522 -6522
  71. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_pt_PT.txt +6522 -6522
  72. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ro.txt +6522 -6522
  73. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_ru.txt +6522 -6522
  74. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sk.txt +6522 -6522
  75. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sl.txt +6522 -6522
  76. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sr.txt +6522 -6522
  77. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_sv.txt +6522 -6522
  78. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_th.txt +6522 -6522
  79. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_tr.txt +6522 -6522
  80. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_uk.txt +6522 -6522
  81. birdnet_analyzer/labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_zh.txt +6522 -6522
  82. birdnet_analyzer/lang/de.json +334 -334
  83. birdnet_analyzer/lang/en.json +334 -334
  84. birdnet_analyzer/lang/fi.json +334 -334
  85. birdnet_analyzer/lang/fr.json +334 -334
  86. birdnet_analyzer/lang/id.json +334 -334
  87. birdnet_analyzer/lang/pt-br.json +334 -334
  88. birdnet_analyzer/lang/ru.json +334 -334
  89. birdnet_analyzer/lang/se.json +334 -334
  90. birdnet_analyzer/lang/tlh.json +334 -334
  91. birdnet_analyzer/lang/zh_TW.json +334 -334
  92. birdnet_analyzer/model.py +1212 -1243
  93. birdnet_analyzer/playground.py +5 -0
  94. birdnet_analyzer/search/__init__.py +3 -3
  95. birdnet_analyzer/search/__main__.py +3 -3
  96. birdnet_analyzer/search/cli.py +11 -12
  97. birdnet_analyzer/search/core.py +78 -78
  98. birdnet_analyzer/search/utils.py +107 -111
  99. birdnet_analyzer/segments/__init__.py +3 -3
  100. birdnet_analyzer/segments/__main__.py +3 -3
  101. birdnet_analyzer/segments/cli.py +13 -14
  102. birdnet_analyzer/segments/core.py +81 -78
  103. birdnet_analyzer/segments/utils.py +383 -394
  104. birdnet_analyzer/species/__init__.py +3 -3
  105. birdnet_analyzer/species/__main__.py +3 -3
  106. birdnet_analyzer/species/cli.py +13 -14
  107. birdnet_analyzer/species/core.py +35 -35
  108. birdnet_analyzer/species/utils.py +74 -75
  109. birdnet_analyzer/train/__init__.py +3 -3
  110. birdnet_analyzer/train/__main__.py +3 -3
  111. birdnet_analyzer/train/cli.py +13 -14
  112. birdnet_analyzer/train/core.py +113 -113
  113. birdnet_analyzer/train/utils.py +877 -847
  114. birdnet_analyzer/translate.py +133 -104
  115. birdnet_analyzer/utils.py +426 -419
  116. {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/METADATA +137 -129
  117. birdnet_analyzer-2.0.1.dist-info/RECORD +125 -0
  118. {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/WHEEL +1 -1
  119. {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/licenses/LICENSE +18 -18
  120. birdnet_analyzer-2.0.0.dist-info/RECORD +0 -117
  121. {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/entry_points.txt +0 -0
  122. {birdnet_analyzer-2.0.0.dist-info → birdnet_analyzer-2.0.1.dist-info}/top_level.txt +0 -0
birdnet_analyzer/cli.py CHANGED
@@ -1,707 +1,709 @@
1
- import argparse
2
- import os
3
-
4
- import birdnet_analyzer.config as cfg
5
-
6
- SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
7
- ASCII_LOGO = r"""
8
- .
9
- .-=-
10
- .:=++++.
11
- ..-======#=:.
12
- .-%%%#*+=-#+++:..
13
- .-+***======++++++=..
14
- .=====+==++++++++-.
15
- .=+++====++++++++++=:.
16
- .++++++++=======----===:
17
- =+++++++====-----+++++++-.
18
- .=++++==========-=++=====+=:.
19
- -++======---:::::-=++++***+:.
20
- ..---::::::::::::::::-=*****+-.
21
- ..--------::::::::::::--+##-.:.
22
- ++++=::::::... ..-------------::::::-::.::.
23
- ..::-------:::.-=.:::::+-.... ....:--:..
24
- ..::-======--+::...... .:---:.
25
- ..:--==+++++==-.. .-+==-
26
- ......::----: **=--
27
- ..-=-:. *+=:=
28
- ..-==== +++ =+**
29
- ========+
30
- **=====
31
- ***+==
32
- ****+
33
- """
34
-
35
-
36
- def io_args():
37
- """
38
- Creates an argument parser for input and output paths.
39
- Returns:
40
- argparse.ArgumentParser: The argument parser with input and output path arguments.
41
- Arguments:
42
- input (str): Path to the input file or folder. Defaults to the value specified in cfg.INPUT_PATH.
43
- output (str): Path to the output folder. Defaults to the input path if not specified.
44
- """
45
- p = argparse.ArgumentParser(add_help=False)
46
- p.add_argument(
47
- "input",
48
- metavar="INPUT",
49
- help="Path to input file or folder.",
50
- )
51
- p.add_argument("-o", "--output", help="Path to output folder. Defaults to the input path.")
52
-
53
- return p
54
-
55
-
56
- def bandpass_args():
57
- """
58
- Creates an argument parser for bandpass filter frequency arguments.
59
- This function sets up an argument parser with two arguments:
60
- --fmin and --fmax, which define the minimum and maximum frequencies
61
- for the bandpass filter, respectively. The values are constrained
62
- to be within the range defined by cfg.SIG_FMIN and cfg.SIG_FMAX.
63
- Returns:
64
- argparse.ArgumentParser: The configured argument parser.
65
- """
66
- p = argparse.ArgumentParser(add_help=False)
67
- p.add_argument(
68
- "--fmin",
69
- type=lambda a: max(0, min(cfg.SIG_FMAX, int(a))),
70
- default=cfg.SIG_FMIN,
71
- help="Minimum frequency for bandpass filter in Hz.",
72
- )
73
- p.add_argument(
74
- "--fmax",
75
- type=lambda a: max(cfg.SIG_FMIN, min(cfg.SIG_FMAX, int(a))),
76
- default=cfg.SIG_FMAX,
77
- help="Maximum frequency for bandpass filter in Hz.",
78
- )
79
-
80
- return p
81
-
82
-
83
- def species_args():
84
- """
85
- Creates an argument parser for species-related arguments.
86
- Returns:
87
- argparse.ArgumentParser: The argument parser with the following arguments:
88
- --lat (float): Recording location latitude. Set -1 to ignore. Default is -1.
89
- --lon (float): Recording location longitude. Set -1 to ignore. Default is -1.
90
- --week (int): Week of the year when the recording was made. Values in [1, 48] (4 weeks per month).
91
- Set -1 for year-round species list. Default is -1.
92
- --slist (str): Path to species list file or folder. If folder is provided, species list needs to be named
93
- "species_list.txt". If lat and lon are provided, this list will be ignored.
94
- --sf_thresh (float): Minimum species occurrence frequency threshold for location filter. Values in [0.01, 0.99].
95
- Defaults to cfg.LOCATION_FILTER_THRESHOLD.
96
- """
97
- p = argparse.ArgumentParser(add_help=False)
98
- p.add_argument("--lat", type=float, default=-1, help="Recording location latitude. Set -1 to ignore.")
99
- p.add_argument("--lon", type=float, default=-1, help="Recording location longitude. Set -1 to ignore.")
100
- p.add_argument(
101
- "--week",
102
- type=int,
103
- default=-1,
104
- help="Week of the year when the recording was made. Values in [1, 48] (4 weeks per month). Set -1 for year-round species list.",
105
- )
106
- p.add_argument(
107
- "--slist",
108
- help='Path to species list file or folder. If folder is provided, species list needs to be named "species_list.txt". If lat and lon are provided, this list will be ignored.',
109
- )
110
- p.add_argument(
111
- "--sf_thresh",
112
- type=lambda a: max(0.0001, min(0.99, float(a))),
113
- default=cfg.LOCATION_FILTER_THRESHOLD,
114
- help="Minimum species occurrence frequency threshold for location filter. Values in [0.0001, 0.99].",
115
- )
116
-
117
- return p
118
-
119
-
120
- def sigmoid_args():
121
- """
122
- Creates an argument parser for sigmoid sensitivity.
123
- This function sets up an argument parser with a single argument `--sensitivity`.
124
- The sensitivity value is constrained to be within the range [0.75, 1.25], where higher
125
- values result in higher detection sensitivity. The default value is taken from
126
- `cfg.SIGMOID_SENSITIVITY`.
127
- Returns:
128
- argparse.ArgumentParser: The argument parser with the sensitivity argument configured.
129
- """
130
- p = argparse.ArgumentParser(add_help=False)
131
- p.add_argument(
132
- "--sensitivity",
133
- type=lambda a: min(1.25, max(0.75, float(a))),
134
- default=cfg.SIGMOID_SENSITIVITY,
135
- help="Detection sensitivity; Higher values result in higher sensitivity. Values in [0.75, 1.25]. Values other than 1.0 will shift the sigmoid functionon the x-axis. Use complementary to the cut-off threshold.",
136
- )
137
-
138
- return p
139
-
140
-
141
- def overlap_args(help_string="Overlap of prediction segments. Values in [0.0, 2.9]."):
142
- """
143
- Creates an argument parser for the overlap of prediction segments.
144
- Args:
145
- help_string (str): A custom help string for the overlap argument. Defaults to a formatted string
146
- indicating the range [0.0, 2.9] and the default value from cfg.SIG_OVERLAP.
147
- Returns:
148
- argparse.ArgumentParser: An argument parser with the overlap argument configured.
149
- """
150
- p = argparse.ArgumentParser(add_help=False)
151
- p.add_argument(
152
- "--overlap",
153
- type=lambda a: max(0.0, min(2.9, float(a))),
154
- default=cfg.SIG_OVERLAP,
155
- help=help_string,
156
- )
157
-
158
- return p
159
-
160
-
161
- def audio_speed_args():
162
- """
163
- Creates an argument parser for audio speed configuration.
164
- This function sets up an argument parser with a single argument `--audio_speed`
165
- which allows the user to specify a speed factor for audio playback. The speed factor
166
- must be a float value where values less than 1.0 will slow down the audio and values
167
- greater than 1.0 will speed it up. The minimum allowed value is 0.01. The default
168
- value is taken from the configuration (`cfg.AUDIO_SPEED`).
169
- Returns:
170
- argparse.ArgumentParser: The argument parser with the `--audio_speed` argument configured.
171
- """
172
- p = argparse.ArgumentParser(add_help=False)
173
- p.add_argument(
174
- "--audio_speed",
175
- type=lambda a: max(0.01, float(a)),
176
- default=cfg.AUDIO_SPEED,
177
- help="Speed factor for audio playback. Values < 1.0 will slow down the audio, values > 1.0 will speed it up. At a 10x decrease (audio speed 0.1), a 384 kHz recording becomes a 38.4 kHz recording.",
178
- )
179
-
180
- return p
181
-
182
-
183
- def threads_args():
184
- """
185
- Creates an argument parser for specifying the number of CPU threads to use.
186
- The parser adds an argument `--threads` (or `-t`) which accepts an integer value.
187
- The value is constrained to be at least 1. If not specified, the default value is
188
- set to half the number of available CPU cores, but not exceeding 8.
189
- Returns:
190
- argparse.ArgumentParser: The argument parser with the `--threads` argument.
191
- """
192
- import multiprocessing
193
-
194
- p = argparse.ArgumentParser(add_help=False)
195
- p.add_argument(
196
- "-t",
197
- "--threads",
198
- type=lambda a: max(1, int(a)),
199
- default=min(8, max(1, multiprocessing.cpu_count() // 2)),
200
- help="Number of CPU threads.",
201
- )
202
-
203
- return p
204
-
205
-
206
- def min_conf_args():
207
- """
208
- Creates an argument parser for the minimum confidence threshold.
209
-
210
- Returns:
211
- argparse.ArgumentParser: An argument parser with the --min_conf argument.
212
-
213
- The --min_conf argument:
214
- - Sets the minimum confidence threshold for predictions.
215
- - Accepts float values in the range [0.01, 0.99].
216
- - Defaults to the value specified in cfg.MIN_CONFIDENCE.
217
- - Ensures that the provided value is clamped between 0.01 and 0.99.
218
- """
219
- p = argparse.ArgumentParser(add_help=False)
220
- p.add_argument(
221
- "--min_conf",
222
- default=cfg.MIN_CONFIDENCE,
223
- type=lambda a: max(0.00001, min(0.99, float(a))),
224
- help="Minimum confidence threshold. Values in [0.00001, 0.99].",
225
- )
226
-
227
- return p
228
-
229
-
230
- def locale_args():
231
- """
232
- Creates an argument parser for locale settings.
233
- This function creates an argument parser with a single argument `--locale`
234
- (or `-l`) which specifies the locale for translated species common names.
235
- The default value is 'en' (US English). The available locale values include
236
- 'af', 'en_UK', 'de', 'it', and others.
237
- Returns:
238
- argparse.ArgumentParser: An argument parser with the locale argument.
239
- """
240
- p = argparse.ArgumentParser(add_help=False)
241
- p.add_argument(
242
- "-l",
243
- "--locale",
244
- default="en",
245
- help="Locale for translated species common names. Values in ['af', 'en_UK', 'de', 'it', ...].",
246
- )
247
-
248
- return p
249
-
250
-
251
- def bs_args(default=cfg.BATCH_SIZE):
252
- """
253
- Creates an argument parser for batch size configuration.
254
- Returns:
255
- argparse.ArgumentParser: An argument parser with a batch size argument.
256
- The parser includes the following argument:
257
- -b, --batch_size: An integer specifying the number of samples to process at the same time.
258
- The value must be at least 1. Defaults to the value of cfg.BATCH_SIZE.
259
- """
260
- p = argparse.ArgumentParser(add_help=False)
261
- p.add_argument(
262
- "-b",
263
- "--batch_size",
264
- type=lambda a: max(1, int(a)),
265
- default=default,
266
- help="Number of samples to process at the same time.",
267
- )
268
-
269
- return p
270
-
271
-
272
- def db_args():
273
- """
274
- Creates an arguments parser for the database path.
275
- Returns:
276
- argparse.ArgumentParser: An argument parser with a database size argument.
277
- The parser includes the following argument:
278
- -db, --database: Path to the database folder.
279
- """
280
- p = argparse.ArgumentParser(add_help=False)
281
- p.add_argument(
282
- "-db",
283
- "--database",
284
- help="Path to the database folder.",
285
- )
286
-
287
- return p
288
-
289
-
290
- def analyzer_parser():
291
- """
292
- Creates and returns an argument parser for the BirdNET Analyzer CLI.
293
- The parser includes various argument groups for different functionalities such as
294
- I/O operations, bandpass filtering, species selection, sigmoid function parameters,
295
- overlap settings, audio speed adjustments, threading, minimum confidence levels,
296
- locale settings, and batch size.
297
- If the environment variable "IS_GITHUB_RUNNER" is set to "true", a simplified parser
298
- description is used. Otherwise, a detailed ASCII logo and usage instructions are included.
299
- The parser also defines a custom action `UniqueSetAction` to ensure that the `--rtype`
300
- argument values are stored as a set of unique, lowercase strings.
301
- Arguments:
302
- --rtype: Specifies output format. Accepts multiple values from ['table', 'audacity', 'kaleidoscope', 'csv'].
303
- --combine_results: Outputs a combined file for all selected result types if set.
304
- -c, --classifier: Path to a custom trained classifier. Overrides --lat, --lon, and --locale if set.
305
- --skip_existing_results: Skips files that have already been analyzed if set.
306
- --top_n: Saves only the top N predictions for each segment. Threshold will be ignored.
307
- --merge_consecutive: Maximum number of consecutive detections to merge for each species.
308
- Returns:
309
- argparse.ArgumentParser: Configured argument parser for the BirdNET Analyzer CLI.
310
- """
311
- parents = [
312
- io_args(),
313
- bandpass_args(),
314
- species_args(),
315
- sigmoid_args(),
316
- overlap_args(),
317
- audio_speed_args(),
318
- threads_args(),
319
- min_conf_args(),
320
- locale_args(),
321
- bs_args(),
322
- ]
323
-
324
- parser = argparse.ArgumentParser(
325
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
326
- parents=parents,
327
- )
328
-
329
- class UniqueSetAction(argparse.Action):
330
- def __call__(self, parser, args, values, option_string=None):
331
- setattr(args, self.dest, {v.lower() for v in values})
332
-
333
- parser.add_argument(
334
- "--rtype",
335
- default={"table"},
336
- choices=["table", "audacity", "kaleidoscope", "csv"],
337
- nargs="+",
338
- help="Specifies output format. Values in `['table', 'audacity', 'kaleidoscope', 'csv']`.",
339
- action=UniqueSetAction,
340
- )
341
- parser.add_argument(
342
- "--combine_results",
343
- help="Also outputs a combined file for all the selected result types. If not set combined tables will be generated.",
344
- action="store_true",
345
- )
346
-
347
- parser.add_argument(
348
- "-c",
349
- "--classifier",
350
- default=cfg.CUSTOM_CLASSIFIER,
351
- help="Path to custom trained classifier. If set, --lat, --lon and --locale are ignored.",
352
- )
353
-
354
- parser.add_argument(
355
- "--skip_existing_results",
356
- action="store_true",
357
- help="Skip files that have already been analyzed.",
358
- )
359
-
360
- parser.add_argument(
361
- "--top_n",
362
- type=lambda a: max(1, int(a)),
363
- help="Saves only the top N predictions for each segment independent of their score. Threshold will be ignored.",
364
- )
365
-
366
- parser.add_argument(
367
- "--merge_consecutive",
368
- type=int,
369
- default=1,
370
- help="Maximum number of consecutive detections above MIN_CONF to merge for each detected species. This will result in fewer entires in the result file with segments longer than 3 seconds. Set to 0 or 1 to disable merging. Set to None to include all consecutive detections. We use the mean of the top 3 scores from all consecutive detections for merging.",
371
- )
372
-
373
- return parser
374
-
375
-
376
- def embeddings_parser():
377
- """
378
- Creates and returns an argument parser for extracting feature embeddings with BirdNET.
379
-
380
- The parser includes arguments from the following parent parsers:
381
- - db_args(): Handles database arguments.
382
- - bandpass_args(): Handles bandpass filter arguments.
383
- - audio_speed_args(): Handles audio speed arguments.
384
- - overlap_args(): Handles overlap arguments.
385
- - threads_args(): Handles threading arguments.
386
- - bs_args(): Handles batch size arguments.
387
-
388
- Returns:
389
- argparse.ArgumentParser: Configured argument parser for extracting feature embeddings.
390
- """
391
-
392
- parents = [db_args(), bandpass_args(), audio_speed_args(), overlap_args(), threads_args(), bs_args()]
393
-
394
- parser = argparse.ArgumentParser(
395
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
396
- parents=parents,
397
- )
398
-
399
- parser.add_argument(
400
- "-i",
401
- "--input",
402
- help="Path to input file or folder.",
403
- )
404
-
405
- return parser
406
-
407
-
408
- def search_parser():
409
- """
410
- Creates and returns an argument parser for searching BirdNET embeddings.
411
-
412
- The parser includes the following arguments:
413
- - -q, --queryfile: Path to the query file.
414
- - -o, --output: Path to the output folder.
415
- - --n_results: Number of results to return.
416
- - --score_function: Scoring function to use. Choose 'cosine', 'euclidean' or 'dot'. Defaults to 'cosine'.
417
- - --crop_mode: Crop mode for the query sample. Can be 'center', 'first' or 'segments'.
418
-
419
- The parser also includes arguments from the following parent parsers:
420
- - overlap_args(): Handles overlap arguments if segments is selected as crop mode.
421
- - db_args(): Handles database arguments.
422
- """
423
-
424
- parents = [overlap_args(), db_args()]
425
-
426
- parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, parents=parents)
427
- parser.add_argument("-q", "--queryfile", help="Path to the query file.")
428
- parser.add_argument("-o", "--output", help="Path to the output folder.")
429
- parser.add_argument("--n_results", default=10, help="Number of results to return.")
430
-
431
- # TODO: use choice argument.
432
- parser.add_argument(
433
- "--score_function",
434
- default="cosine",
435
- choices=["cosine", "euclidean", "dot"],
436
- help="Scoring function to use. Choose 'cosine', 'euclidean' or 'dot'. Defaults to 'cosine'.",
437
- )
438
- parser.add_argument(
439
- "--crop_mode",
440
- default=cfg.SAMPLE_CROP_MODE,
441
- choices=["center", "first", "segments"],
442
- help="Crop mode for the query sample. Can be 'center', 'first' or 'segments'.",
443
- )
444
-
445
- return parser
446
-
447
-
448
- def client_parser():
449
- """
450
- Creates and returns an argument parser for the client that queries an analyzer API endpoint server.
451
- The parser includes the following arguments:
452
- - --host: Host name or IP address of the API endpoint server (default: "localhost").
453
- - -p, --port: Port of the API endpoint server (default: 8080).
454
- - --pmode: Score pooling mode, with possible values 'avg' or 'max' (default: "avg").
455
- - --num_results: Number of results per request (default: 5).
456
- - --save: Flag to define if files should be stored on the server.
457
- The parser also includes arguments from the following parent parsers:
458
- - io_args()
459
- - species_args()
460
- - sigmoid_args()
461
- - overlap_args()
462
- Returns:
463
- argparse.ArgumentParser: Configured argument parser for the client.
464
- """
465
- parser = argparse.ArgumentParser(
466
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
467
- parents=[io_args(), species_args(), sigmoid_args(), overlap_args()],
468
- )
469
- parser.add_argument("--host", default="localhost", help="Host name or IP address of API endpoint server.")
470
- parser.add_argument("-p", "--port", type=int, default=8080, help="Port of API endpoint server.")
471
- parser.add_argument("--pmode", default="avg", help="Score pooling mode. Values in ['avg', 'max'].")
472
- parser.add_argument("--num_results", type=int, default=5, help="Number of results per request.")
473
- parser.add_argument(
474
- "--save",
475
- action="store_true",
476
- help="Define if files should be stored on server.",
477
- )
478
-
479
- return parser
480
-
481
-
482
- def segments_parser():
483
- """
484
- Creates an argument parser for extracting segments from audio files based on BirdNET detections.
485
- Returns:
486
- argparse.ArgumentParser: Configured argument parser with the following arguments:
487
- - input (str): Path to folder containing audio files.
488
- - results (str, optional): Path to folder containing result files. Defaults to the `input` path.
489
- - output (str, optional): Output folder path for extracted segments. Defaults to the `input` path.
490
- - max_segments (int, optional): Number of randomly extracted segments per species. Defaults to 100.
491
- - seg_length (float, optional): Length of extracted segments in seconds. Defaults to cfg.SIG_LENGTH.
492
- """
493
- parser = argparse.ArgumentParser(
494
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
495
- parents=[audio_speed_args(), threads_args(), min_conf_args()],
496
- )
497
- parser.add_argument("input", metavar="INPUT", help="Path to folder containing audio files.")
498
- parser.add_argument("-r", "--results", help="Path to folder containing result files. Defaults to the `input` path.")
499
- parser.add_argument(
500
- "-o", "--output", help="Output folder path for extracted segments. Defaults to the `input` path."
501
- )
502
- parser.add_argument(
503
- "--max_segments",
504
- type=lambda a: max(1, int(a)),
505
- default=100,
506
- help="Number of randomly extracted segments per species.",
507
- )
508
-
509
- parser.add_argument(
510
- "--seg_length",
511
- type=lambda a: max(1.0, float(a)),
512
- default=cfg.SIG_LENGTH,
513
- help="Minimum length of extracted segments in seconds. If a segment is shorter than this value, it will be padded with audio from the source file.",
514
- )
515
-
516
- return parser
517
-
518
-
519
- def server_parser():
520
- """
521
- Creates and configures an argument parser for the API endpoint server.
522
- The parser includes arguments for specifying the host, port, and storage path for uploaded files.
523
- It also inherits arguments from `threads_args` and `locale_args`.
524
- Returns:
525
- argparse.ArgumentParser: Configured argument parser with server-specific options.
526
- """
527
- parser = argparse.ArgumentParser(
528
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
529
- parents=[threads_args(), locale_args()],
530
- )
531
-
532
- parser.add_argument("--host", default="0.0.0.0", help="Host name or IP address of API endpoint server.")
533
- parser.add_argument("-p", "--port", type=int, default=8080, help="Port of API endpoint server.")
534
- parser.add_argument(
535
- "--spath",
536
- default="uploads/"
537
- if os.environ.get("IS_GITHUB_RUNNER", "false").lower() == "true"
538
- else os.path.join(SCRIPT_DIR, "uploads"),
539
- help="Path to folder where uploaded files should be stored.",
540
- )
541
-
542
- return parser
543
-
544
-
545
- def species_parser():
546
- """
547
- Creates an argument parser for retrieving a list of species for a given location using BirdNET.
548
- The parser includes the following arguments:
549
- - output: Path to the output file or folder. If a folder is provided, the file will be named 'species_list.txt'.
550
- - --sortby: Optional argument to sort species by occurrence frequency ('freq') or alphabetically ('alpha'). Defaults to 'freq'.
551
- Returns:
552
- argparse.ArgumentParser: Configured argument parser for species retrieval.
553
- """
554
- parser = argparse.ArgumentParser(
555
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
556
- parents=[species_args()],
557
- )
558
- parser.add_argument(
559
- "output",
560
- metavar="OUTPUT",
561
- help="Path to output file or folder. If this is a folder, file will be named 'species_list.txt'.",
562
- )
563
-
564
- parser.add_argument(
565
- "--sortby",
566
- default="freq",
567
- choices=["freq", "alpha"],
568
- help="Sort species by occurrence frequency or alphabetically. Values in ['freq', 'alpha'].",
569
- )
570
-
571
- return parser
572
-
573
-
574
- def train_parser():
575
- """
576
- Creates an argument parser for training a custom classifier with BirdNET.
577
- The parser includes arguments for various training parameters such as input data path, crop mode,
578
- output path, number of epochs, batch size, validation split ratio, learning rate, hidden units,
579
- dropout rate, mixup, upsampling ratio and mode, model format, model save mode, cache mode and file,
580
- and hyperparameter tuning options.
581
- Returns:
582
- argparse.ArgumentParser: Configured argument parser for training a custom classifier.
583
- """
584
- parser = argparse.ArgumentParser(
585
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
586
- parents=[
587
- bandpass_args(),
588
- audio_speed_args(),
589
- threads_args(),
590
- bs_args(cfg.TRAIN_BATCH_SIZE),
591
- overlap_args(help_string="Overlap of training data segments in seconds if crop_mode is 'segments'."),
592
- ],
593
- )
594
- c = (
595
- "checkpoints/custom/Custom_Classifier"
596
- if os.environ.get("IS_GITHUB_RUNNER", "false").lower() == "true"
597
- else os.path.join(SCRIPT_DIR, "checkpoints/custom/Custom_Classifier")
598
- )
599
- parser.add_argument(
600
- "input",
601
- metavar="INPUT",
602
- help="Path to training data folder. Subfolder names are used as labels.",
603
- )
604
- parser.add_argument(
605
- "--test_data",
606
- help="Path to test data folder. If not specified, a random validation split will be used.")
607
- parser.add_argument(
608
- "--crop_mode",
609
- default=cfg.SAMPLE_CROP_MODE,
610
- choices=["center", "first", "segments", "smart"],
611
- help="Crop mode for training data. Can be 'center', 'first', 'segments' or 'smart'.",
612
- )
613
- parser.add_argument("-o", "--output", default=c, help="Path to trained classifier model output.")
614
- parser.add_argument(
615
- "--epochs",
616
- type=int,
617
- default=cfg.TRAIN_EPOCHS,
618
- help="Number of training epochs.",
619
- )
620
- parser.add_argument(
621
- "--val_split",
622
- type=float,
623
- default=cfg.TRAIN_VAL_SPLIT,
624
- help="Validation split ratio. Will be ignored if test_data is set.",
625
- )
626
- parser.add_argument(
627
- "--learning_rate",
628
- type=float,
629
- default=cfg.TRAIN_LEARNING_RATE,
630
- help="Learning rate.",
631
- )
632
- parser.add_argument(
633
- "--focal-loss",
634
- action="store_true",
635
- help="Use focal loss for training (helps with imbalanced classes and hard examples).",
636
- )
637
- parser.add_argument(
638
- "--focal-loss-gamma",
639
- default=cfg.FOCAL_LOSS_GAMMA,
640
- type=float,
641
- help="Focal loss gamma parameter (focusing parameter). Higher values give more weight to hard examples.",
642
- )
643
- parser.add_argument(
644
- "--focal-loss-alpha",
645
- default=cfg.FOCAL_LOSS_ALPHA,
646
- type=float,
647
- help="Focal loss alpha parameter (balancing parameter). Controls weight between positive and negative examples.",
648
- )
649
- parser.add_argument(
650
- "--hidden_units",
651
- type=int,
652
- default=cfg.TRAIN_HIDDEN_UNITS,
653
- help="Number of hidden units. If set to >0, a two-layer classifier is used.",
654
- )
655
- parser.add_argument(
656
- "--dropout",
657
- type=lambda a: min(max(0, float(a)), 0.9),
658
- default=cfg.TRAIN_DROPOUT,
659
- help="Dropout rate. Higher values result in more regularization. Values in [0.0, 0.9].",
660
- )
661
- parser.add_argument("--label_smoothing", action="store_true", help="Whether to use label smoothing for training.")
662
- parser.add_argument("--mixup", action="store_true", help="Whether to use mixup for training.")
663
- parser.add_argument(
664
- "--upsampling_ratio",
665
- type=lambda a: min(max(0, float(a)), 1),
666
- default=cfg.UPSAMPLING_RATIO,
667
- help="Balance train data and upsample minority classes. Values between 0 and 1.",
668
- )
669
- parser.add_argument(
670
- "--upsampling_mode",
671
- default=cfg.UPSAMPLING_MODE,
672
- choices=["repeat", "linear", "mean", "smote"],
673
- help="Upsampling mode.",
674
- )
675
- parser.add_argument(
676
- "--model_format",
677
- default=cfg.TRAINED_MODEL_OUTPUT_FORMAT,
678
- choices=["tflite", "raven", "both"],
679
- help="Model output format.",
680
- )
681
- parser.add_argument(
682
- "--model_save_mode",
683
- default=cfg.TRAINED_MODEL_SAVE_MODE,
684
- choices=["replace", "append"],
685
- help="Model save mode. 'replace' will overwrite the original classification layer and 'append' will combine the original classification layer with the new one.",
686
- )
687
- parser.add_argument("--cache_mode", choices=["load", "save"], help="Cache mode. Can be 'load' or 'save'.")
688
- parser.add_argument("--cache_file", default=cfg.TRAIN_CACHE_FILE, help="Path to cache file.")
689
- parser.add_argument(
690
- "--autotune",
691
- action="store_true",
692
- help="Whether to use automatic hyperparameter tuning (this will execute multiple training runs to search for optimal hyperparameters).",
693
- )
694
- parser.add_argument(
695
- "--autotune_trials",
696
- type=int,
697
- default=cfg.AUTOTUNE_TRIALS,
698
- help="Number of training runs for hyperparameter tuning.",
699
- )
700
- parser.add_argument(
701
- "--autotune_executions_per_trial",
702
- type=int,
703
- default=cfg.AUTOTUNE_EXECUTIONS_PER_TRIAL,
704
- help="The number of times a training run with a set of hyperparameters is repeated during hyperparameter tuning (this reduces the variance).",
705
- )
706
-
707
- return parser
1
+ # ruff: noqa: E501
2
+ import argparse
3
+ import os
4
+
5
+ import birdnet_analyzer.config as cfg
6
+
7
+ SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
8
+ ASCII_LOGO = r"""
9
+ .
10
+ .-=-
11
+ .:=++++.
12
+ ..-======#=:.
13
+ .-%%%#*+=-#+++:..
14
+ .-+***======++++++=..
15
+ .=====+==++++++++-.
16
+ .=+++====++++++++++=:.
17
+ .++++++++=======----===:
18
+ =+++++++====-----+++++++-.
19
+ .=++++==========-=++=====+=:.
20
+ -++======---:::::-=++++***+:.
21
+ ..---::::::::::::::::-=*****+-.
22
+ ..--------::::::::::::--+##-.:.
23
+ ++++=::::::... ..-------------::::::-::.::.
24
+ ..::-------:::.-=.:::::+-.... ....:--:..
25
+ ..::-======--+::...... .:---:.
26
+ ..:--==+++++==-.. .-+==-
27
+ ......::----: **=--
28
+ ..-=-:. *+=:=
29
+ ..-==== +++ =+**
30
+ ========+
31
+ **=====
32
+ ***+==
33
+ ****+
34
+ """ # noqa: W291
35
+
36
+
37
+ def io_args():
38
+ """
39
+ Creates an argument parser for input and output paths.
40
+ Returns:
41
+ argparse.ArgumentParser: The argument parser with input and output path arguments.
42
+ Arguments:
43
+ input (str): Path to the input file or folder. Defaults to the value specified in cfg.INPUT_PATH.
44
+ output (str): Path to the output folder. Defaults to the input path if not specified.
45
+ """
46
+ p = argparse.ArgumentParser(add_help=False)
47
+ p.add_argument(
48
+ "audio_input",
49
+ metavar="INPUT",
50
+ help="Path to input file or folder.",
51
+ )
52
+ p.add_argument("-o", "--output", help="Path to output folder. Defaults to the input path.")
53
+
54
+ return p
55
+
56
+
57
+ def bandpass_args():
58
+ """
59
+ Creates an argument parser for bandpass filter frequency arguments.
60
+ This function sets up an argument parser with two arguments:
61
+ --fmin and --fmax, which define the minimum and maximum frequencies
62
+ for the bandpass filter, respectively. The values are constrained
63
+ to be within the range defined by cfg.SIG_FMIN and cfg.SIG_FMAX.
64
+ Returns:
65
+ argparse.ArgumentParser: The configured argument parser.
66
+ """
67
+ p = argparse.ArgumentParser(add_help=False)
68
+ p.add_argument(
69
+ "--fmin",
70
+ type=lambda a: max(0, min(cfg.SIG_FMAX, int(a))),
71
+ default=cfg.SIG_FMIN,
72
+ help="Minimum frequency for bandpass filter in Hz.",
73
+ )
74
+ p.add_argument(
75
+ "--fmax",
76
+ type=lambda a: max(cfg.SIG_FMIN, min(cfg.SIG_FMAX, int(a))),
77
+ default=cfg.SIG_FMAX,
78
+ help="Maximum frequency for bandpass filter in Hz.",
79
+ )
80
+
81
+ return p
82
+
83
+
84
+ def species_args():
85
+ """
86
+ Creates an argument parser for species-related arguments.
87
+ Returns:
88
+ argparse.ArgumentParser: The argument parser with the following arguments:
89
+ --lat (float): Recording location latitude. Set -1 to ignore. Default is -1.
90
+ --lon (float): Recording location longitude. Set -1 to ignore. Default is -1.
91
+ --week (int): Week of the year when the recording was made. Values in [1, 48] (4 weeks per month).
92
+ Set -1 for year-round species list. Default is -1.
93
+ --slist (str): Path to species list file or folder. If folder is provided, species list needs to be named
94
+ "species_list.txt". If lat and lon are provided, this list will be ignored.
95
+ --sf_thresh (float): Minimum species occurrence frequency threshold for location filter. Values in [0.01, 0.99].
96
+ Defaults to cfg.LOCATION_FILTER_THRESHOLD.
97
+ """
98
+ p = argparse.ArgumentParser(add_help=False)
99
+ p.add_argument("--lat", type=float, default=-1, help="Recording location latitude. Set -1 to ignore.")
100
+ p.add_argument("--lon", type=float, default=-1, help="Recording location longitude. Set -1 to ignore.")
101
+ p.add_argument(
102
+ "--week",
103
+ type=int,
104
+ default=-1,
105
+ help="Week of the year when the recording was made. Values in [1, 48] (4 weeks per month). Set -1 for year-round species list.",
106
+ )
107
+ p.add_argument(
108
+ "--slist",
109
+ help='Path to species list file or folder. If folder is provided, species list needs to be named "species_list.txt". If lat and lon are provided, this list will be ignored.',
110
+ )
111
+ p.add_argument(
112
+ "--sf_thresh",
113
+ type=lambda a: max(0.0001, min(0.99, float(a))),
114
+ default=cfg.LOCATION_FILTER_THRESHOLD,
115
+ help="Minimum species occurrence frequency threshold for location filter. Values in [0.0001, 0.99].",
116
+ )
117
+
118
+ return p
119
+
120
+
121
+ def sigmoid_args():
122
+ """
123
+ Creates an argument parser for sigmoid sensitivity.
124
+ This function sets up an argument parser with a single argument `--sensitivity`.
125
+ The sensitivity value is constrained to be within the range [0.75, 1.25], where higher
126
+ values result in higher detection sensitivity. The default value is taken from
127
+ `cfg.SIGMOID_SENSITIVITY`.
128
+ Returns:
129
+ argparse.ArgumentParser: The argument parser with the sensitivity argument configured.
130
+ """
131
+ p = argparse.ArgumentParser(add_help=False)
132
+ p.add_argument(
133
+ "--sensitivity",
134
+ type=lambda a: min(1.25, max(0.75, float(a))),
135
+ default=cfg.SIGMOID_SENSITIVITY,
136
+ help="Detection sensitivity; Higher values result in higher sensitivity. Values in [0.75, 1.25]. Values other than 1.0 will shift the sigmoid functionon the x-axis. Use complementary to the cut-off threshold.",
137
+ )
138
+
139
+ return p
140
+
141
+
142
+ def overlap_args(help_string="Overlap of prediction segments. Values in [0.0, 2.9]."):
143
+ """
144
+ Creates an argument parser for the overlap of prediction segments.
145
+ Args:
146
+ help_string (str): A custom help string for the overlap argument. Defaults to a formatted string
147
+ indicating the range [0.0, 2.9] and the default value from cfg.SIG_OVERLAP.
148
+ Returns:
149
+ argparse.ArgumentParser: An argument parser with the overlap argument configured.
150
+ """
151
+ p = argparse.ArgumentParser(add_help=False)
152
+ p.add_argument(
153
+ "--overlap",
154
+ type=lambda a: max(0.0, min(2.9, float(a))),
155
+ default=cfg.SIG_OVERLAP,
156
+ help=help_string,
157
+ )
158
+
159
+ return p
160
+
161
+
162
+ def audio_speed_args():
163
+ """
164
+ Creates an argument parser for audio speed configuration.
165
+ This function sets up an argument parser with a single argument `--audio_speed`
166
+ which allows the user to specify a speed factor for audio playback. The speed factor
167
+ must be a float value where values less than 1.0 will slow down the audio and values
168
+ greater than 1.0 will speed it up. The minimum allowed value is 0.01. The default
169
+ value is taken from the configuration (`cfg.AUDIO_SPEED`).
170
+ Returns:
171
+ argparse.ArgumentParser: The argument parser with the `--audio_speed` argument configured.
172
+ """
173
+ p = argparse.ArgumentParser(add_help=False)
174
+ p.add_argument(
175
+ "--audio_speed",
176
+ type=lambda a: max(0.01, float(a)),
177
+ default=cfg.AUDIO_SPEED,
178
+ help="Speed factor for audio playback. Values < 1.0 will slow down the audio, values > 1.0 will speed it up. At a 10x decrease (audio speed 0.1), a 384 kHz recording becomes a 38.4 kHz recording.",
179
+ )
180
+
181
+ return p
182
+
183
+
184
+ def threads_args():
185
+ """
186
+ Creates an argument parser for specifying the number of CPU threads to use.
187
+ The parser adds an argument `--threads` (or `-t`) which accepts an integer value.
188
+ The value is constrained to be at least 1. If not specified, the default value is
189
+ set to half the number of available CPU cores, but not exceeding 8.
190
+ Returns:
191
+ argparse.ArgumentParser: The argument parser with the `--threads` argument.
192
+ """
193
+ import multiprocessing
194
+
195
+ p = argparse.ArgumentParser(add_help=False)
196
+ p.add_argument(
197
+ "-t",
198
+ "--threads",
199
+ type=lambda a: max(1, int(a)),
200
+ default=min(8, max(1, multiprocessing.cpu_count() // 2)),
201
+ help="Number of CPU threads.",
202
+ )
203
+
204
+ return p
205
+
206
+
207
+ def min_conf_args():
208
+ """
209
+ Creates an argument parser for the minimum confidence threshold.
210
+
211
+ Returns:
212
+ argparse.ArgumentParser: An argument parser with the --min_conf argument.
213
+
214
+ The --min_conf argument:
215
+ - Sets the minimum confidence threshold for predictions.
216
+ - Accepts float values in the range [0.01, 0.99].
217
+ - Defaults to the value specified in cfg.MIN_CONFIDENCE.
218
+ - Ensures that the provided value is clamped between 0.01 and 0.99.
219
+ """
220
+ p = argparse.ArgumentParser(add_help=False)
221
+ p.add_argument(
222
+ "--min_conf",
223
+ default=cfg.MIN_CONFIDENCE,
224
+ type=lambda a: max(0.00001, min(0.99, float(a))),
225
+ help="Minimum confidence threshold. Values in [0.00001, 0.99].",
226
+ )
227
+
228
+ return p
229
+
230
+
231
+ def locale_args():
232
+ """
233
+ Creates an argument parser for locale settings.
234
+ This function creates an argument parser with a single argument `--locale`
235
+ (or `-l`) which specifies the locale for translated species common names.
236
+ The default value is 'en' (US English). The available locale values include
237
+ 'af', 'en_UK', 'de', 'it', and others.
238
+ Returns:
239
+ argparse.ArgumentParser: An argument parser with the locale argument.
240
+ """
241
+ p = argparse.ArgumentParser(add_help=False)
242
+ p.add_argument(
243
+ "-l",
244
+ "--locale",
245
+ default="en",
246
+ help="Locale for translated species common names. Values in ['af', 'en_UK', 'de', 'it', ...].",
247
+ )
248
+
249
+ return p
250
+
251
+
252
+ def bs_args(default=cfg.BATCH_SIZE):
253
+ """
254
+ Creates an argument parser for batch size configuration.
255
+ Returns:
256
+ argparse.ArgumentParser: An argument parser with a batch size argument.
257
+ The parser includes the following argument:
258
+ -b, --batch_size: An integer specifying the number of samples to process at the same time.
259
+ The value must be at least 1. Defaults to the value of cfg.BATCH_SIZE.
260
+ """
261
+ p = argparse.ArgumentParser(add_help=False)
262
+ p.add_argument(
263
+ "-b",
264
+ "--batch_size",
265
+ type=lambda a: max(1, int(a)),
266
+ default=default,
267
+ help="Number of samples to process at the same time.",
268
+ )
269
+
270
+ return p
271
+
272
+
273
+ def db_args():
274
+ """
275
+ Creates an arguments parser for the database path.
276
+ Returns:
277
+ argparse.ArgumentParser: An argument parser with a database size argument.
278
+ The parser includes the following argument:
279
+ -db, --database: Path to the database folder.
280
+ """
281
+ p = argparse.ArgumentParser(add_help=False)
282
+ p.add_argument(
283
+ "-db",
284
+ "--database",
285
+ help="Path to the database folder.",
286
+ )
287
+
288
+ return p
289
+
290
+
291
+ def analyzer_parser():
292
+ """
293
+ Creates and returns an argument parser for the BirdNET Analyzer CLI.
294
+ The parser includes various argument groups for different functionalities such as
295
+ I/O operations, bandpass filtering, species selection, sigmoid function parameters,
296
+ overlap settings, audio speed adjustments, threading, minimum confidence levels,
297
+ locale settings, and batch size.
298
+ If the environment variable "IS_GITHUB_RUNNER" is set to "true", a simplified parser
299
+ description is used. Otherwise, a detailed ASCII logo and usage instructions are included.
300
+ The parser also defines a custom action `UniqueSetAction` to ensure that the `--rtype`
301
+ argument values are stored as a set of unique, lowercase strings.
302
+ Arguments:
303
+ --rtype: Specifies output format. Accepts multiple values from ['table', 'audacity', 'kaleidoscope', 'csv'].
304
+ --combine_results: Outputs a combined file for all selected result types if set.
305
+ -c, --classifier: Path to a custom trained classifier. Overrides --lat, --lon, and --locale if set.
306
+ --skip_existing_results: Skips files that have already been analyzed if set.
307
+ --top_n: Saves only the top N predictions for each segment. Threshold will be ignored.
308
+ --merge_consecutive: Maximum number of consecutive detections to merge for each species.
309
+ Returns:
310
+ argparse.ArgumentParser: Configured argument parser for the BirdNET Analyzer CLI.
311
+ """
312
+ parents = [
313
+ io_args(),
314
+ bandpass_args(),
315
+ species_args(),
316
+ sigmoid_args(),
317
+ overlap_args(),
318
+ audio_speed_args(),
319
+ threads_args(),
320
+ min_conf_args(),
321
+ locale_args(),
322
+ bs_args(),
323
+ ]
324
+
325
+ parser = argparse.ArgumentParser(
326
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
327
+ parents=parents,
328
+ )
329
+
330
+ class UniqueSetAction(argparse.Action):
331
+ def __call__(self, parser, args, values, option_string=None):
332
+ setattr(args, self.dest, {v.lower() for v in values})
333
+
334
+ parser.add_argument(
335
+ "--rtype",
336
+ default={"table"},
337
+ choices=["table", "audacity", "kaleidoscope", "csv"],
338
+ nargs="+",
339
+ help="Specifies output format. Values in `['table', 'audacity', 'kaleidoscope', 'csv']`.",
340
+ action=UniqueSetAction,
341
+ )
342
+ parser.add_argument(
343
+ "--combine_results",
344
+ help="Also outputs a combined file for all the selected result types. If not set combined tables will be generated.",
345
+ action="store_true",
346
+ )
347
+
348
+ parser.add_argument(
349
+ "-c",
350
+ "--classifier",
351
+ default=cfg.CUSTOM_CLASSIFIER,
352
+ help="Path to custom trained classifier. If set, --lat, --lon and --locale are ignored.",
353
+ )
354
+
355
+ parser.add_argument(
356
+ "--skip_existing_results",
357
+ action="store_true",
358
+ help="Skip files that have already been analyzed.",
359
+ )
360
+
361
+ parser.add_argument(
362
+ "--top_n",
363
+ type=lambda a: max(1, int(a)),
364
+ help="Saves only the top N predictions for each segment independent of their score. Threshold will be ignored.",
365
+ )
366
+
367
+ parser.add_argument(
368
+ "--merge_consecutive",
369
+ type=int,
370
+ default=1,
371
+ help="Maximum number of consecutive detections above MIN_CONF to merge for each detected species. This will result in fewer entires in the result file with segments longer than 3 seconds. Set to 0 or 1 to disable merging. Set to None to include all consecutive detections. We use the mean of the top 3 scores from all consecutive detections for merging.",
372
+ )
373
+
374
+ return parser
375
+
376
+
377
+ def embeddings_parser():
378
+ """
379
+ Creates and returns an argument parser for extracting feature embeddings with BirdNET.
380
+
381
+ The parser includes arguments from the following parent parsers:
382
+ - db_args(): Handles database arguments.
383
+ - bandpass_args(): Handles bandpass filter arguments.
384
+ - audio_speed_args(): Handles audio speed arguments.
385
+ - overlap_args(): Handles overlap arguments.
386
+ - threads_args(): Handles threading arguments.
387
+ - bs_args(): Handles batch size arguments.
388
+
389
+ Returns:
390
+ argparse.ArgumentParser: Configured argument parser for extracting feature embeddings.
391
+ """
392
+
393
+ parents = [db_args(), bandpass_args(), audio_speed_args(), overlap_args(), threads_args(), bs_args()]
394
+
395
+ parser = argparse.ArgumentParser(
396
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
397
+ parents=parents,
398
+ )
399
+
400
+ parser.add_argument(
401
+ "-i",
402
+ "--input",
403
+ dest="audio_input",
404
+ help="Path to input file or folder.",
405
+ )
406
+
407
+ return parser
408
+
409
+
410
+ def search_parser():
411
+ """
412
+ Creates and returns an argument parser for searching BirdNET embeddings.
413
+
414
+ The parser includes the following arguments:
415
+ - -q, --queryfile: Path to the query file.
416
+ - -o, --output: Path to the output folder.
417
+ - --n_results: Number of results to return.
418
+ - --score_function: Scoring function to use. Choose 'cosine', 'euclidean' or 'dot'. Defaults to 'cosine'.
419
+ - --crop_mode: Crop mode for the query sample. Can be 'center', 'first' or 'segments'.
420
+
421
+ The parser also includes arguments from the following parent parsers:
422
+ - overlap_args(): Handles overlap arguments if segments is selected as crop mode.
423
+ - db_args(): Handles database arguments.
424
+ """
425
+
426
+ parents = [overlap_args(), db_args()]
427
+
428
+ parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, parents=parents)
429
+ parser.add_argument("-q", "--queryfile", help="Path to the query file.")
430
+ parser.add_argument("-o", "--output", help="Path to the output folder.")
431
+ parser.add_argument("--n_results", default=10, help="Number of results to return.")
432
+
433
+ # TODO: use choice argument.
434
+ parser.add_argument(
435
+ "--score_function",
436
+ default="cosine",
437
+ choices=["cosine", "euclidean", "dot"],
438
+ help="Scoring function to use. Choose 'cosine', 'euclidean' or 'dot'. Defaults to 'cosine'.",
439
+ )
440
+ parser.add_argument(
441
+ "--crop_mode",
442
+ default=cfg.SAMPLE_CROP_MODE,
443
+ choices=["center", "first", "segments"],
444
+ help="Crop mode for the query sample. Can be 'center', 'first' or 'segments'.",
445
+ )
446
+
447
+ return parser
448
+
449
+
450
+ def client_parser():
451
+ """
452
+ Creates and returns an argument parser for the client that queries an analyzer API endpoint server.
453
+ The parser includes the following arguments:
454
+ - --host: Host name or IP address of the API endpoint server (default: "localhost").
455
+ - -p, --port: Port of the API endpoint server (default: 8080).
456
+ - --pmode: Score pooling mode, with possible values 'avg' or 'max' (default: "avg").
457
+ - --num_results: Number of results per request (default: 5).
458
+ - --save: Flag to define if files should be stored on the server.
459
+ The parser also includes arguments from the following parent parsers:
460
+ - io_args()
461
+ - species_args()
462
+ - sigmoid_args()
463
+ - overlap_args()
464
+ Returns:
465
+ argparse.ArgumentParser: Configured argument parser for the client.
466
+ """
467
+ parser = argparse.ArgumentParser(
468
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
469
+ parents=[io_args(), species_args(), sigmoid_args(), overlap_args()],
470
+ )
471
+ parser.add_argument("--host", default="localhost", help="Host name or IP address of API endpoint server.")
472
+ parser.add_argument("-p", "--port", type=int, default=8080, help="Port of API endpoint server.")
473
+ parser.add_argument("--pmode", default="avg", help="Score pooling mode. Values in ['avg', 'max'].")
474
+ parser.add_argument("--num_results", type=int, default=5, help="Number of results per request.")
475
+ parser.add_argument(
476
+ "--save",
477
+ action="store_true",
478
+ help="Define if files should be stored on server.",
479
+ )
480
+
481
+ return parser
482
+
483
+
484
+ def segments_parser():
485
+ """
486
+ Creates an argument parser for extracting segments from audio files based on BirdNET detections.
487
+ Returns:
488
+ argparse.ArgumentParser: Configured argument parser with the following arguments:
489
+ - input (str): Path to folder containing audio files.
490
+ - results (str, optional): Path to folder containing result files. Defaults to the `input` path.
491
+ - output (str, optional): Output folder path for extracted segments. Defaults to the `input` path.
492
+ - max_segments (int, optional): Number of randomly extracted segments per species. Defaults to 100.
493
+ - seg_length (float, optional): Length of extracted segments in seconds. Defaults to cfg.SIG_LENGTH.
494
+ """
495
+ parser = argparse.ArgumentParser(
496
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
497
+ parents=[audio_speed_args(), threads_args(), min_conf_args()],
498
+ )
499
+ parser.add_argument("audio_input", metavar="INPUT", help="Path to folder containing audio files.")
500
+ parser.add_argument("-r", "--results", help="Path to folder containing result files. Defaults to the `input` path.")
501
+ parser.add_argument(
502
+ "-o", "--output", help="Output folder path for extracted segments. Defaults to the `input` path."
503
+ )
504
+ parser.add_argument(
505
+ "--max_segments",
506
+ type=lambda a: max(1, int(a)),
507
+ default=100,
508
+ help="Number of randomly extracted segments per species.",
509
+ )
510
+
511
+ parser.add_argument(
512
+ "--seg_length",
513
+ type=lambda a: max(1.0, float(a)),
514
+ default=cfg.SIG_LENGTH,
515
+ help="Minimum length of extracted segments in seconds. If a segment is shorter than this value, it will be padded with audio from the source file.",
516
+ )
517
+
518
+ return parser
519
+
520
+
521
+ def server_parser():
522
+ """
523
+ Creates and configures an argument parser for the API endpoint server.
524
+ The parser includes arguments for specifying the host, port, and storage path for uploaded files.
525
+ It also inherits arguments from `threads_args` and `locale_args`.
526
+ Returns:
527
+ argparse.ArgumentParser: Configured argument parser with server-specific options.
528
+ """
529
+ parser = argparse.ArgumentParser(
530
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
531
+ parents=[threads_args(), locale_args()],
532
+ )
533
+
534
+ parser.add_argument("--host", default="0.0.0.0", help="Host name or IP address of API endpoint server.")
535
+ parser.add_argument("-p", "--port", type=int, default=8080, help="Port of API endpoint server.")
536
+ parser.add_argument(
537
+ "--spath",
538
+ default="uploads/"
539
+ if os.environ.get("IS_GITHUB_RUNNER", "false").lower() == "true"
540
+ else os.path.join(SCRIPT_DIR, "uploads"),
541
+ help="Path to folder where uploaded files should be stored.",
542
+ )
543
+
544
+ return parser
545
+
546
+
547
+ def species_parser():
548
+ """
549
+ Creates an argument parser for retrieving a list of species for a given location using BirdNET.
550
+ The parser includes the following arguments:
551
+ - output: Path to the output file or folder. If a folder is provided, the file will be named 'species_list.txt'.
552
+ - --sortby: Optional argument to sort species by occurrence frequency ('freq') or alphabetically ('alpha'). Defaults to 'freq'.
553
+ Returns:
554
+ argparse.ArgumentParser: Configured argument parser for species retrieval.
555
+ """
556
+ parser = argparse.ArgumentParser(
557
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
558
+ parents=[species_args()],
559
+ )
560
+ parser.add_argument(
561
+ "output",
562
+ metavar="OUTPUT",
563
+ help="Path to output file or folder. If this is a folder, file will be named 'species_list.txt'.",
564
+ )
565
+
566
+ parser.add_argument(
567
+ "--sortby",
568
+ default="freq",
569
+ choices=["freq", "alpha"],
570
+ help="Sort species by occurrence frequency or alphabetically. Values in ['freq', 'alpha'].",
571
+ )
572
+
573
+ return parser
574
+
575
+
576
+ def train_parser():
577
+ """
578
+ Creates an argument parser for training a custom classifier with BirdNET.
579
+ The parser includes arguments for various training parameters such as input data path, crop mode,
580
+ output path, number of epochs, batch size, validation split ratio, learning rate, hidden units,
581
+ dropout rate, mixup, upsampling ratio and mode, model format, model save mode, cache mode and file,
582
+ and hyperparameter tuning options.
583
+ Returns:
584
+ argparse.ArgumentParser: Configured argument parser for training a custom classifier.
585
+ """
586
+ parser = argparse.ArgumentParser(
587
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
588
+ parents=[
589
+ bandpass_args(),
590
+ audio_speed_args(),
591
+ threads_args(),
592
+ bs_args(cfg.TRAIN_BATCH_SIZE),
593
+ overlap_args(help_string="Overlap of training data segments in seconds if crop_mode is 'segments'."),
594
+ ],
595
+ )
596
+ c = (
597
+ "checkpoints/custom/Custom_Classifier"
598
+ if os.environ.get("IS_GITHUB_RUNNER", "false").lower() == "true"
599
+ else os.path.join(SCRIPT_DIR, "checkpoints/custom/Custom_Classifier")
600
+ )
601
+ parser.add_argument(
602
+ "audio_input",
603
+ metavar="INPUT",
604
+ help="Path to training data folder. Subfolder names are used as labels.",
605
+ )
606
+ parser.add_argument(
607
+ "--test_data", help="Path to test data folder. If not specified, a random validation split will be used."
608
+ )
609
+ parser.add_argument(
610
+ "--crop_mode",
611
+ default=cfg.SAMPLE_CROP_MODE,
612
+ choices=["center", "first", "segments", "smart"],
613
+ help="Crop mode for training data. Can be 'center', 'first', 'segments' or 'smart'.",
614
+ )
615
+ parser.add_argument("-o", "--output", default=c, help="Path to trained classifier model output.")
616
+ parser.add_argument(
617
+ "--epochs",
618
+ type=int,
619
+ default=cfg.TRAIN_EPOCHS,
620
+ help="Number of training epochs.",
621
+ )
622
+ parser.add_argument(
623
+ "--val_split",
624
+ type=float,
625
+ default=cfg.TRAIN_VAL_SPLIT,
626
+ help="Validation split ratio. Will be ignored if test_data is set.",
627
+ )
628
+ parser.add_argument(
629
+ "--learning_rate",
630
+ type=float,
631
+ default=cfg.TRAIN_LEARNING_RATE,
632
+ help="Learning rate.",
633
+ )
634
+ parser.add_argument(
635
+ "--focal-loss",
636
+ action="store_true",
637
+ help="Use focal loss for training (helps with imbalanced classes and hard examples).",
638
+ )
639
+ parser.add_argument(
640
+ "--focal-loss-gamma",
641
+ default=cfg.FOCAL_LOSS_GAMMA,
642
+ type=float,
643
+ help="Focal loss gamma parameter (focusing parameter). Higher values give more weight to hard examples.",
644
+ )
645
+ parser.add_argument(
646
+ "--focal-loss-alpha",
647
+ default=cfg.FOCAL_LOSS_ALPHA,
648
+ type=float,
649
+ help="Focal loss alpha parameter (balancing parameter). Controls weight between positive and negative examples.",
650
+ )
651
+ parser.add_argument(
652
+ "--hidden_units",
653
+ type=int,
654
+ default=cfg.TRAIN_HIDDEN_UNITS,
655
+ help="Number of hidden units. If set to >0, a two-layer classifier is used.",
656
+ )
657
+ parser.add_argument(
658
+ "--dropout",
659
+ type=lambda a: min(max(0, float(a)), 0.9),
660
+ default=cfg.TRAIN_DROPOUT,
661
+ help="Dropout rate. Higher values result in more regularization. Values in [0.0, 0.9].",
662
+ )
663
+ parser.add_argument("--label_smoothing", action="store_true", help="Whether to use label smoothing for training.")
664
+ parser.add_argument("--mixup", action="store_true", help="Whether to use mixup for training.")
665
+ parser.add_argument(
666
+ "--upsampling_ratio",
667
+ type=lambda a: min(max(0, float(a)), 1),
668
+ default=cfg.UPSAMPLING_RATIO,
669
+ help="Balance train data and upsample minority classes. Values between 0 and 1.",
670
+ )
671
+ parser.add_argument(
672
+ "--upsampling_mode",
673
+ default=cfg.UPSAMPLING_MODE,
674
+ choices=["repeat", "linear", "mean", "smote"],
675
+ help="Upsampling mode.",
676
+ )
677
+ parser.add_argument(
678
+ "--model_format",
679
+ default=cfg.TRAINED_MODEL_OUTPUT_FORMAT,
680
+ choices=["tflite", "raven", "both"],
681
+ help="Model output format.",
682
+ )
683
+ parser.add_argument(
684
+ "--model_save_mode",
685
+ default=cfg.TRAINED_MODEL_SAVE_MODE,
686
+ choices=["replace", "append"],
687
+ help="Model save mode. 'replace' will overwrite the original classification layer and 'append' will combine the original classification layer with the new one.",
688
+ )
689
+ parser.add_argument("--cache_mode", choices=["load", "save"], help="Cache mode. Can be 'load' or 'save'.")
690
+ parser.add_argument("--cache_file", default=cfg.TRAIN_CACHE_FILE, help="Path to cache file.")
691
+ parser.add_argument(
692
+ "--autotune",
693
+ action="store_true",
694
+ help="Whether to use automatic hyperparameter tuning (this will execute multiple training runs to search for optimal hyperparameters).",
695
+ )
696
+ parser.add_argument(
697
+ "--autotune_trials",
698
+ type=int,
699
+ default=cfg.AUTOTUNE_TRIALS,
700
+ help="Number of training runs for hyperparameter tuning.",
701
+ )
702
+ parser.add_argument(
703
+ "--autotune_executions_per_trial",
704
+ type=int,
705
+ default=cfg.AUTOTUNE_EXECUTIONS_PER_TRIAL,
706
+ help="The number of times a training run with a set of hyperparameters is repeated during hyperparameter tuning (this reduces the variance).",
707
+ )
708
+
709
+ return parser