profplate commited on
Commit
5023401
·
verified ·
1 Parent(s): 467b49c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +330 -0
app.py ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from transformers import pipeline
3
+ import numpy as np
4
+ from datetime import datetime
5
+
6
+ # Load the audio classification model
7
+ classifier = pipeline(
8
+ "audio-classification",
9
+ model="dima806/bird_sounds_classification",
10
+ device=-1,
11
+ )
12
+
13
+ # Bird information database (15 species with descriptions)
14
+ BIRD_INFO = {
15
+ "Great Tinamou": {
16
+ "habitat": "Tropical and subtropical lowland forests from southern Mexico to northern South America.",
17
+ "song": "A series of tremulous, haunting whistles that echo through the forest - one of the most recognizable sounds of the neotropical lowlands.",
18
+ "range": "Southern Mexico through Central America to northern South America, including Brazil and Peru.",
19
+ "fun_fact": "Despite being a ground-dwelling bird, the Great Tinamou roosts in trees at night. Its eggs are among the most beautiful in the bird world - glossy turquoise-blue.",
20
+ },
21
+ "Plain Chachalaca": {
22
+ "habitat": "Brushy woodland edges, thickets, and riparian areas. The only chachalaca regularly found in the United States (southern Texas).",
23
+ "song": "A loud, raucous CHA-cha-LAC repeated in chorus by groups - unmistakable once you've heard it. Often called at dawn.",
24
+ "range": "Southern Texas through Mexico to Costa Rica.",
25
+ "fun_fact": "Plain Chachalacas are one of the few species in this model's list that can actually be seen in the US - in the Rio Grande Valley of Texas.",
26
+ },
27
+ "Crested Guan": {
28
+ "habitat": "Mountain forests and cloud forests, typically at elevations of 500-2,500 meters.",
29
+ "song": "A variety of honking and trumpeting calls, especially loud during breeding season.",
30
+ "range": "Southern Mexico through Central America to western South America.",
31
+ "fun_fact": "Crested Guans are important seed dispersers for many tropical tree species. They swallow fruits whole and spread seeds through the forest.",
32
+ },
33
+ "Andean Guan": {
34
+ "habitat": "Cloud forests and humid montane forests of the Andes, typically between 1,500 and 3,500 meters elevation.",
35
+ "song": "Deep honking calls that carry through mountain valleys, often given in the early morning.",
36
+ "range": "Andes from Venezuela south through Colombia, Ecuador, Peru, and Bolivia.",
37
+ "fun_fact": "The Andean Guan is a canopy specialist that rarely descends to the ground, moving through the treetops to feed on fruit and leaves.",
38
+ },
39
+ "Little Tinamou": {
40
+ "habitat": "Dense undergrowth of tropical forests. Extremely secretive and almost never seen despite being common.",
41
+ "song": "A long, tremulous whistle that rises and falls - one of the most frequently heard but least seen birds in its range.",
42
+ "range": "Southern Mexico through Central America to South America, as far south as Brazil.",
43
+ "fun_fact": "Little Tinamous are heard far more often than seen. They freeze when threatened and rely on their camouflage, only flushing at the last moment.",
44
+ },
45
+ "Solitary Tinamou": {
46
+ "habitat": "Interior of humid tropical forests, usually on the forest floor.",
47
+ "song": "A mournful, descending whistle that sounds almost electronic - quite eerie when heard in the deep forest.",
48
+ "range": "Central America through northern South America.",
49
+ "fun_fact": "True to its name, the Solitary Tinamou is almost always found alone. Males incubate the eggs and raise the chicks by themselves.",
50
+ },
51
+ "Highland Tinamou": {
52
+ "habitat": "Cloud forests and montane forests, from 1,200 to 3,000 meters elevation.",
53
+ "song": "A clear, descending series of whistles. Sometimes described as sounding like someone playing a slow scale on a flute.",
54
+ "range": "Mountains of Costa Rica and Panama through the Andes to Bolivia.",
55
+ "fun_fact": "Highland Tinamous have been recorded at higher elevations than almost any other tinamou species.",
56
+ },
57
+ "Grey-headed Chachalaca": {
58
+ "habitat": "Dry forests, forest edges, and agricultural areas with scattered trees.",
59
+ "song": "Loud, harsh calls similar to other chachalacas, often given by groups in noisy choruses at dawn and dusk.",
60
+ "range": "Honduras through Central America to northern Colombia.",
61
+ "fun_fact": "Chachalacas get their name from the sound of their call - cha-cha-lac-a - repeated over and over.",
62
+ },
63
+ "Band-tailed Guan": {
64
+ "habitat": "Humid mountain forests and cloud forests.",
65
+ "song": "A series of deep, resonant honking sounds, especially vocal during the breeding season.",
66
+ "range": "Andes from Colombia and Venezuela south to Bolivia.",
67
+ "fun_fact": "Band-tailed Guans travel in small family groups and are surprisingly acrobatic for their size, leaping between branches to reach fruit.",
68
+ },
69
+ "Black-capped Tinamou": {
70
+ "habitat": "Forests from lowlands to lower montane elevations.",
71
+ "song": "A bubbling, accelerating series of whistles - sounds like a bouncing ball slowing to a stop, but in reverse.",
72
+ "range": "Central Peru to Bolivia, in the eastern Andean slopes.",
73
+ "fun_fact": "Like other tinamous, the Black-capped Tinamou can fly but strongly prefers to walk. When it does fly, it's in short, explosive bursts.",
74
+ },
75
+ "Spotted Nothura": {
76
+ "habitat": "Grasslands and open areas, including agricultural fields and pastures.",
77
+ "song": "A series of sharp, staccato whistles, often given from the ground in open grassland.",
78
+ "range": "Central South America - Brazil, Paraguay, Argentina, Uruguay.",
79
+ "fun_fact": "Nothuras are grassland tinamous - unlike their forest-dwelling relatives, they live in open habitats and look somewhat like partridges.",
80
+ },
81
+ "Red-winged Tinamou": {
82
+ "habitat": "Grasslands, scrublands, and agricultural areas in southern South America.",
83
+ "song": "A melodious, flute-like whistle that rises and falls, often heard at dawn and dusk across the pampas.",
84
+ "range": "Southern Brazil, Paraguay, Uruguay, Argentina.",
85
+ "fun_fact": "Named for the rufous-red color visible on its wings in flight - one of the few times you'll see this secretive bird in the open.",
86
+ },
87
+ "Australian Brushturkey": {
88
+ "habitat": "Rainforests, scrublands, and suburban gardens in eastern Australia.",
89
+ "song": "Deep booming calls during breeding. Otherwise relatively quiet compared to other species in this list.",
90
+ "range": "Eastern Australia, from Cape York to southern New South Wales.",
91
+ "fun_fact": "Males build enormous mound nests (up to 4 meters wide) out of decomposing vegetation. The heat from decomposition incubates the eggs - no body heat required.",
92
+ },
93
+ "Dusky Megapode": {
94
+ "habitat": "Tropical forests on islands in the western Pacific, often near volcanic areas.",
95
+ "song": "Loud wailing calls, especially at night. Some species in this family are among the noisiest birds in their habitat.",
96
+ "range": "Islands of Indonesia and Papua New Guinea.",
97
+ "fun_fact": "Some megapodes use volcanic heat to incubate their eggs, burying them in volcanically warmed soil - the only birds known to use geothermal energy for reproduction.",
98
+ },
99
+ "Tataupa Tinamou": {
100
+ "habitat": "Forest edges, secondary growth, and dense undergrowth in tropical and subtropical regions.",
101
+ "song": "A rich, mellow series of whistles. Has been described as one of the most beautiful bird songs in South America.",
102
+ "range": "Eastern South America from Brazil to Argentina.",
103
+ "fun_fact": "The Tataupa Tinamou is more tolerant of disturbed habitats than many of its relatives, which helps it survive in areas affected by deforestation.",
104
+ },
105
+ }
106
+
107
+ ALL_SPECIES = sorted(set(classifier.model.config.id2label.values()))
108
+
109
+ # Sighting log (in-memory)
110
+ sighting_log = []
111
+
112
+
113
+ def classify_bird(audio):
114
+ if audio is None:
115
+ return "Please upload or record an audio file.", gr.update(), gr.update()
116
+
117
+ sr, y = audio
118
+
119
+ if y.dtype == np.int16:
120
+ y = y.astype(np.float32) / 32768.0
121
+ elif y.dtype == np.int32:
122
+ y = y.astype(np.float32) / 2147483648.0
123
+ elif y.dtype != np.float32:
124
+ y = y.astype(np.float32)
125
+
126
+ if len(y.shape) > 1:
127
+ y = y[:, 0]
128
+
129
+ if sr != 16000:
130
+ duration = len(y) / sr
131
+ new_length = int(duration * 16000)
132
+ y = np.interp(
133
+ np.linspace(0, len(y) - 1, new_length),
134
+ np.arange(len(y)),
135
+ y,
136
+ )
137
+ sr = 16000
138
+
139
+ results = classifier({"sampling_rate": sr, "raw": y}, top_k=3)
140
+
141
+ lines = []
142
+ top_species = results[0]["label"]
143
+ top_score = results[0]["score"]
144
+
145
+ if top_score < 0.40:
146
+ lines.append("Not confident - this may not be a recognizable bird song,")
147
+ lines.append("or the species may not be in this model's training data.\n")
148
+
149
+ for i, pred in enumerate(results, 1):
150
+ score = pred["score"]
151
+ label = pred["label"]
152
+
153
+ # Color-coded confidence
154
+ if score >= 0.70:
155
+ indicator = "HIGH"
156
+ elif score >= 0.40:
157
+ indicator = "MED"
158
+ else:
159
+ indicator = "LOW"
160
+
161
+ bar_length = int(score * 20)
162
+ bar = "#" * bar_length + "." * (20 - bar_length)
163
+ lines.append(f"[{indicator}] {i}. {label}")
164
+ lines.append(f" {bar} {score:.1%}\n")
165
+
166
+ return "\n".join(lines), gr.update(value=top_species), gr.update()
167
+
168
+
169
+ def get_bird_info(species):
170
+ if species in BIRD_INFO:
171
+ info = BIRD_INFO[species]
172
+ text = f"## {species}\n\n"
173
+ text += f"**Habitat:** {info['habitat']}\n\n"
174
+ text += f"**Song:** {info['song']}\n\n"
175
+ text += f"**Range:** {info['range']}\n\n"
176
+ text += f"**Fun fact:** {info['fun_fact']}"
177
+ return text
178
+ else:
179
+ return (
180
+ f"## {species}\n\n"
181
+ f"No detailed description available for this species yet. "
182
+ f"I've written descriptions for 15 of the 50 species in this model. "
183
+ f"Try searching [Xeno-Canto](https://xeno-canto.org/) or the "
184
+ f"[Cornell Lab](https://www.allaboutbirds.org/) for more information."
185
+ )
186
+
187
+
188
+ def add_sighting(species, location, notes):
189
+ if not species:
190
+ return format_log()
191
+
192
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
193
+ entry = {
194
+ "species": species,
195
+ "date": timestamp,
196
+ "location": location or "Not specified",
197
+ "notes": notes or "",
198
+ }
199
+ sighting_log.append(entry)
200
+ return format_log()
201
+
202
+
203
+ def format_log():
204
+ if not sighting_log:
205
+ return "No sightings logged yet. Identify a bird and add it to your log!"
206
+
207
+ lines = [f"### Sighting Log ({len(sighting_log)} entries)\n"]
208
+ for i, entry in enumerate(sighting_log, 1):
209
+ lines.append(f"**{i}. {entry['species']}**")
210
+ lines.append(f" {entry['date']} | {entry['location']}")
211
+ if entry["notes"]:
212
+ lines.append(f" Notes: {entry['notes']}")
213
+ lines.append("")
214
+
215
+ lines.append("---")
216
+ lines.append(
217
+ "*This log is stored in memory and will reset when the Space restarts. "
218
+ "Copy your log if you want to save it!*"
219
+ )
220
+ return "\n".join(lines)
221
+
222
+
223
+ # Build the interface with tabs
224
+ with gr.Blocks(theme=gr.themes.Soft(), title="The Backyard Birder") as demo:
225
+ gr.Markdown(
226
+ """
227
+ # The Backyard Birder
228
+ A multi-feature birding assistant. Identify birds from audio recordings,
229
+ learn about the species, and keep a sighting log.
230
+
231
+ **Model:** `dima806/bird_sounds_classification` - 50 species (Tinamous, Guans, Chachalacas, and relatives).
232
+ These are neotropical birds, not typical North American backyard species. The tool demonstrates how
233
+ audio classification pipelines work; with a different model (like BirdNET), it could identify local birds too.
234
+
235
+ ---
236
+ """
237
+ )
238
+
239
+ # State for passing species between tabs
240
+ current_species = gr.State("")
241
+
242
+ with gr.Tabs():
243
+ # Tab 1: Identify
244
+ with gr.Tab("Identify"):
245
+ gr.Markdown("Upload a bird recording to identify the species. Best results with clean recordings of 3+ seconds.")
246
+ with gr.Row():
247
+ with gr.Column():
248
+ audio_input = gr.Audio(
249
+ label="Upload or Record Audio",
250
+ type="numpy",
251
+ )
252
+ classify_btn = gr.Button("Identify Bird", variant="primary")
253
+ with gr.Column():
254
+ classification_output = gr.Textbox(
255
+ label="Top 3 Predictions",
256
+ lines=10,
257
+ interactive=False,
258
+ )
259
+ species_display = gr.Textbox(
260
+ label="Top prediction",
261
+ visible=False,
262
+ )
263
+
264
+ # Tab 2: Learn
265
+ with gr.Tab("Learn"):
266
+ gr.Markdown("Select a species to learn about it. Descriptions available for 15 of the 50 species.")
267
+ species_dropdown = gr.Dropdown(
268
+ choices=ALL_SPECIES,
269
+ label="Select a Species",
270
+ value="Great Tinamou",
271
+ )
272
+ bird_info_output = gr.Markdown(
273
+ value=get_bird_info("Great Tinamou"),
274
+ )
275
+
276
+ # Tab 3: Log
277
+ with gr.Tab("Log Sightings"):
278
+ gr.Markdown("Keep track of what you hear. Add species to your sighting log with location and notes.")
279
+ with gr.Row():
280
+ with gr.Column():
281
+ log_species = gr.Dropdown(
282
+ choices=ALL_SPECIES,
283
+ label="Species",
284
+ allow_custom_value=True,
285
+ )
286
+ log_location = gr.Textbox(
287
+ label="Location",
288
+ placeholder="e.g., Backyard, Local park, Trail near school...",
289
+ )
290
+ log_notes = gr.Textbox(
291
+ label="Notes",
292
+ placeholder="e.g., Heard at dawn, two birds calling back and forth...",
293
+ lines=2,
294
+ )
295
+ log_btn = gr.Button("Add to Log", variant="primary")
296
+ with gr.Column():
297
+ log_output = gr.Markdown(value=format_log())
298
+
299
+ # Wire up events
300
+ classify_btn.click(
301
+ fn=classify_bird,
302
+ inputs=[audio_input],
303
+ outputs=[classification_output, species_dropdown, species_display],
304
+ )
305
+
306
+ species_dropdown.change(
307
+ fn=get_bird_info,
308
+ inputs=[species_dropdown],
309
+ outputs=[bird_info_output],
310
+ )
311
+
312
+ log_btn.click(
313
+ fn=add_sighting,
314
+ inputs=[log_species, log_location, log_notes],
315
+ outputs=[log_output],
316
+ )
317
+
318
+ gr.Markdown(
319
+ """
320
+ ---
321
+ *Riley's Space 3 - AI + Research Level 2*
322
+
323
+ **How this works:** The Identify tab uses an audio classification model to predict which species
324
+ is singing. The Learn tab shows hand-written descriptions for 15 species. The Log tab lets you
325
+ track your sightings during this session. This is a multi-model pipeline: audio classification
326
+ feeds species information, and errors in identification propagate to wrong descriptions.
327
+ """
328
+ )
329
+
330
+ demo.launch()