Spaces:
Running
Running
| import gradio as gr | |
| from transformers import pipeline | |
| import numpy as np | |
| from datetime import datetime | |
| # Load the audio classification model | |
| classifier = pipeline( | |
| "audio-classification", | |
| model="dima806/bird_sounds_classification", | |
| device=-1, | |
| ) | |
| # Bird information database (15 species with descriptions) | |
| BIRD_INFO = { | |
| "Great Tinamou": { | |
| "habitat": "Tropical and subtropical lowland forests from southern Mexico to northern South America.", | |
| "song": "A series of tremulous, haunting whistles that echo through the forest - one of the most recognizable sounds of the neotropical lowlands.", | |
| "range": "Southern Mexico through Central America to northern South America, including Brazil and Peru.", | |
| "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.", | |
| }, | |
| "Plain Chachalaca": { | |
| "habitat": "Brushy woodland edges, thickets, and riparian areas. The only chachalaca regularly found in the United States (southern Texas).", | |
| "song": "A loud, raucous CHA-cha-LAC repeated in chorus by groups - unmistakable once you've heard it. Often called at dawn.", | |
| "range": "Southern Texas through Mexico to Costa Rica.", | |
| "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.", | |
| }, | |
| "Crested Guan": { | |
| "habitat": "Mountain forests and cloud forests, typically at elevations of 500-2,500 meters.", | |
| "song": "A variety of honking and trumpeting calls, especially loud during breeding season.", | |
| "range": "Southern Mexico through Central America to western South America.", | |
| "fun_fact": "Crested Guans are important seed dispersers for many tropical tree species. They swallow fruits whole and spread seeds through the forest.", | |
| }, | |
| "Andean Guan": { | |
| "habitat": "Cloud forests and humid montane forests of the Andes, typically between 1,500 and 3,500 meters elevation.", | |
| "song": "Deep honking calls that carry through mountain valleys, often given in the early morning.", | |
| "range": "Andes from Venezuela south through Colombia, Ecuador, Peru, and Bolivia.", | |
| "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.", | |
| }, | |
| "Little Tinamou": { | |
| "habitat": "Dense undergrowth of tropical forests. Extremely secretive and almost never seen despite being common.", | |
| "song": "A long, tremulous whistle that rises and falls - one of the most frequently heard but least seen birds in its range.", | |
| "range": "Southern Mexico through Central America to South America, as far south as Brazil.", | |
| "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.", | |
| }, | |
| "Solitary Tinamou": { | |
| "habitat": "Interior of humid tropical forests, usually on the forest floor.", | |
| "song": "A mournful, descending whistle that sounds almost electronic - quite eerie when heard in the deep forest.", | |
| "range": "Central America through northern South America.", | |
| "fun_fact": "True to its name, the Solitary Tinamou is almost always found alone. Males incubate the eggs and raise the chicks by themselves.", | |
| }, | |
| "Highland Tinamou": { | |
| "habitat": "Cloud forests and montane forests, from 1,200 to 3,000 meters elevation.", | |
| "song": "A clear, descending series of whistles. Sometimes described as sounding like someone playing a slow scale on a flute.", | |
| "range": "Mountains of Costa Rica and Panama through the Andes to Bolivia.", | |
| "fun_fact": "Highland Tinamous have been recorded at higher elevations than almost any other tinamou species.", | |
| }, | |
| "Grey-headed Chachalaca": { | |
| "habitat": "Dry forests, forest edges, and agricultural areas with scattered trees.", | |
| "song": "Loud, harsh calls similar to other chachalacas, often given by groups in noisy choruses at dawn and dusk.", | |
| "range": "Honduras through Central America to northern Colombia.", | |
| "fun_fact": "Chachalacas get their name from the sound of their call - cha-cha-lac-a - repeated over and over.", | |
| }, | |
| "Band-tailed Guan": { | |
| "habitat": "Humid mountain forests and cloud forests.", | |
| "song": "A series of deep, resonant honking sounds, especially vocal during the breeding season.", | |
| "range": "Andes from Colombia and Venezuela south to Bolivia.", | |
| "fun_fact": "Band-tailed Guans travel in small family groups and are surprisingly acrobatic for their size, leaping between branches to reach fruit.", | |
| }, | |
| "Black-capped Tinamou": { | |
| "habitat": "Forests from lowlands to lower montane elevations.", | |
| "song": "A bubbling, accelerating series of whistles - sounds like a bouncing ball slowing to a stop, but in reverse.", | |
| "range": "Central Peru to Bolivia, in the eastern Andean slopes.", | |
| "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.", | |
| }, | |
| "Spotted Nothura": { | |
| "habitat": "Grasslands and open areas, including agricultural fields and pastures.", | |
| "song": "A series of sharp, staccato whistles, often given from the ground in open grassland.", | |
| "range": "Central South America - Brazil, Paraguay, Argentina, Uruguay.", | |
| "fun_fact": "Nothuras are grassland tinamous - unlike their forest-dwelling relatives, they live in open habitats and look somewhat like partridges.", | |
| }, | |
| "Red-winged Tinamou": { | |
| "habitat": "Grasslands, scrublands, and agricultural areas in southern South America.", | |
| "song": "A melodious, flute-like whistle that rises and falls, often heard at dawn and dusk across the pampas.", | |
| "range": "Southern Brazil, Paraguay, Uruguay, Argentina.", | |
| "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.", | |
| }, | |
| "Australian Brushturkey": { | |
| "habitat": "Rainforests, scrublands, and suburban gardens in eastern Australia.", | |
| "song": "Deep booming calls during breeding. Otherwise relatively quiet compared to other species in this list.", | |
| "range": "Eastern Australia, from Cape York to southern New South Wales.", | |
| "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.", | |
| }, | |
| "Dusky Megapode": { | |
| "habitat": "Tropical forests on islands in the western Pacific, often near volcanic areas.", | |
| "song": "Loud wailing calls, especially at night. Some species in this family are among the noisiest birds in their habitat.", | |
| "range": "Islands of Indonesia and Papua New Guinea.", | |
| "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.", | |
| }, | |
| "Tataupa Tinamou": { | |
| "habitat": "Forest edges, secondary growth, and dense undergrowth in tropical and subtropical regions.", | |
| "song": "A rich, mellow series of whistles. Has been described as one of the most beautiful bird songs in South America.", | |
| "range": "Eastern South America from Brazil to Argentina.", | |
| "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.", | |
| }, | |
| } | |
| ALL_SPECIES = sorted(set(classifier.model.config.id2label.values())) | |
| # Sighting log (in-memory) | |
| sighting_log = [] | |
| def classify_bird(audio): | |
| if audio is None: | |
| return "Please upload or record an audio file.", gr.update(), gr.update() | |
| sr, y = audio | |
| if y.dtype == np.int16: | |
| y = y.astype(np.float32) / 32768.0 | |
| elif y.dtype == np.int32: | |
| y = y.astype(np.float32) / 2147483648.0 | |
| elif y.dtype != np.float32: | |
| y = y.astype(np.float32) | |
| if len(y.shape) > 1: | |
| y = y[:, 0] | |
| if sr != 16000: | |
| duration = len(y) / sr | |
| new_length = int(duration * 16000) | |
| y = np.interp( | |
| np.linspace(0, len(y) - 1, new_length), | |
| np.arange(len(y)), | |
| y, | |
| ) | |
| sr = 16000 | |
| results = classifier({"sampling_rate": sr, "raw": y}, top_k=3) | |
| lines = [] | |
| top_species = results[0]["label"] | |
| top_score = results[0]["score"] | |
| if top_score < 0.40: | |
| lines.append("Not confident - this may not be a recognizable bird song,") | |
| lines.append("or the species may not be in this model's training data.\n") | |
| for i, pred in enumerate(results, 1): | |
| score = pred["score"] | |
| label = pred["label"] | |
| # Color-coded confidence | |
| if score >= 0.70: | |
| indicator = "HIGH" | |
| elif score >= 0.40: | |
| indicator = "MED" | |
| else: | |
| indicator = "LOW" | |
| bar_length = int(score * 20) | |
| bar = "#" * bar_length + "." * (20 - bar_length) | |
| lines.append(f"[{indicator}] {i}. {label}") | |
| lines.append(f" {bar} {score:.1%}\n") | |
| return "\n".join(lines), gr.update(value=top_species), gr.update() | |
| def get_bird_info(species): | |
| if species in BIRD_INFO: | |
| info = BIRD_INFO[species] | |
| text = f"## {species}\n\n" | |
| text += f"**Habitat:** {info['habitat']}\n\n" | |
| text += f"**Song:** {info['song']}\n\n" | |
| text += f"**Range:** {info['range']}\n\n" | |
| text += f"**Fun fact:** {info['fun_fact']}" | |
| return text | |
| else: | |
| return ( | |
| f"## {species}\n\n" | |
| f"No detailed description available for this species yet. " | |
| f"I've written descriptions for 15 of the 50 species in this model. " | |
| f"Try searching [Xeno-Canto](https://xeno-canto.org/) or the " | |
| f"[Cornell Lab](https://www.allaboutbirds.org/) for more information." | |
| ) | |
| def add_sighting(species, location, notes): | |
| if not species: | |
| return format_log() | |
| timestamp = datetime.now().strftime("%Y-%m-%d %H:%M") | |
| entry = { | |
| "species": species, | |
| "date": timestamp, | |
| "location": location or "Not specified", | |
| "notes": notes or "", | |
| } | |
| sighting_log.append(entry) | |
| return format_log() | |
| def format_log(): | |
| if not sighting_log: | |
| return "No sightings logged yet. Identify a bird and add it to your log!" | |
| lines = [f"### Sighting Log ({len(sighting_log)} entries)\n"] | |
| for i, entry in enumerate(sighting_log, 1): | |
| lines.append(f"**{i}. {entry['species']}**") | |
| lines.append(f" {entry['date']} | {entry['location']}") | |
| if entry["notes"]: | |
| lines.append(f" Notes: {entry['notes']}") | |
| lines.append("") | |
| lines.append("---") | |
| lines.append( | |
| "*This log is stored in memory and will reset when the Space restarts. " | |
| "Copy your log if you want to save it!*" | |
| ) | |
| return "\n".join(lines) | |
| # Build the interface with tabs | |
| with gr.Blocks(theme=gr.themes.Soft(), title="The Backyard Birder") as demo: | |
| gr.Markdown( | |
| """ | |
| # The Backyard Birder | |
| A multi-feature birding assistant. Identify birds from audio recordings, | |
| learn about the species, and keep a sighting log. | |
| **Model:** `dima806/bird_sounds_classification` - 50 species (Tinamous, Guans, Chachalacas, and relatives). | |
| These are neotropical birds, not typical North American backyard species. The tool demonstrates how | |
| audio classification pipelines work; with a different model (like BirdNET), it could identify local birds too. | |
| --- | |
| """ | |
| ) | |
| # State for passing species between tabs | |
| current_species = gr.State("") | |
| with gr.Tabs(): | |
| # Tab 1: Identify | |
| with gr.Tab("Identify"): | |
| gr.Markdown("Upload a bird recording to identify the species. Best results with clean recordings of 3+ seconds.") | |
| with gr.Row(): | |
| with gr.Column(): | |
| audio_input = gr.Audio( | |
| label="Upload or Record Audio", | |
| type="numpy", | |
| ) | |
| classify_btn = gr.Button("Identify Bird", variant="primary") | |
| with gr.Column(): | |
| classification_output = gr.Textbox( | |
| label="Top 3 Predictions", | |
| lines=10, | |
| interactive=False, | |
| ) | |
| species_display = gr.Textbox( | |
| label="Top prediction", | |
| visible=False, | |
| ) | |
| # Tab 2: Learn | |
| with gr.Tab("Learn"): | |
| gr.Markdown("Select a species to learn about it. Descriptions available for 15 of the 50 species.") | |
| species_dropdown = gr.Dropdown( | |
| choices=ALL_SPECIES, | |
| label="Select a Species", | |
| value="Great Tinamou", | |
| ) | |
| bird_info_output = gr.Markdown( | |
| value=get_bird_info("Great Tinamou"), | |
| ) | |
| # Tab 3: Log | |
| with gr.Tab("Log Sightings"): | |
| gr.Markdown("Keep track of what you hear. Add species to your sighting log with location and notes.") | |
| with gr.Row(): | |
| with gr.Column(): | |
| log_species = gr.Dropdown( | |
| choices=ALL_SPECIES, | |
| label="Species", | |
| allow_custom_value=True, | |
| ) | |
| log_location = gr.Textbox( | |
| label="Location", | |
| placeholder="e.g., Backyard, Local park, Trail near school...", | |
| ) | |
| log_notes = gr.Textbox( | |
| label="Notes", | |
| placeholder="e.g., Heard at dawn, two birds calling back and forth...", | |
| lines=2, | |
| ) | |
| log_btn = gr.Button("Add to Log", variant="primary") | |
| with gr.Column(): | |
| log_output = gr.Markdown(value=format_log()) | |
| # Wire up events | |
| classify_btn.click( | |
| fn=classify_bird, | |
| inputs=[audio_input], | |
| outputs=[classification_output, species_dropdown, species_display], | |
| ) | |
| species_dropdown.change( | |
| fn=get_bird_info, | |
| inputs=[species_dropdown], | |
| outputs=[bird_info_output], | |
| ) | |
| log_btn.click( | |
| fn=add_sighting, | |
| inputs=[log_species, log_location, log_notes], | |
| outputs=[log_output], | |
| ) | |
| gr.Markdown( | |
| """ | |
| --- | |
| *Riley's Space 3 - AI + Research Level 2* | |
| **How this works:** The Identify tab uses an audio classification model to predict which species | |
| is singing. The Learn tab shows hand-written descriptions for 15 species. The Log tab lets you | |
| track your sightings during this session. This is a multi-model pipeline: audio classification | |
| feeds species information, and errors in identification propagate to wrong descriptions. | |
| """ | |
| ) | |
| demo.launch() |