jens328 commited on
Commit
735eee4
·
verified ·
1 Parent(s): 209569b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -78
app.py CHANGED
@@ -3,13 +3,15 @@ from transformers import pipeline
3
  import librosa
4
  import numpy as np
5
 
6
- # --------- MODELL: CLAP Zero-Shot Audio-Classification ----------
 
 
 
7
  classifier = pipeline(
8
  task="zero-shot-audio-classification",
9
  model="laion/clap-htsat-unfused"
10
  )
11
 
12
- # Labels, die uns interessieren
13
  CANDIDATE_LABELS = [
14
  "dog barking",
15
  "dog growling",
@@ -21,29 +23,26 @@ CANDIDATE_LABELS = [
21
  "silence",
22
  ]
23
 
24
- # --------- PARAMETER ----------
25
- ENERGY_FRAME_MS = 25 # Frame-Länge für Energie (ms)
26
- ENERGY_HOP_MS = 10 # Schrittweite (ms)
27
- ENERGY_QUANTILE = 0.80 # oberes 20%-Quantil als Schwellwert für "laut"
28
- MIN_EVENT_DURATION = 0.25 # minimale Dauer eines lauten Events (in Sekunden)
29
 
30
- BARK_PROB_THRESHOLD = 0.35 # ab welcher CLAP-Wahrscheinlichkeit "dog barking" zählt
31
- MAX_PAUSE_BETWEEN_BARKS = 3.0 # >3 s Pause = neues Bell-Ereignis
 
 
32
 
 
 
 
33
 
34
  def find_loud_events(y, sr):
35
- """
36
- Findet laute Segmente anhand der Signalenergie.
37
- Gibt eine Liste von (start_s, end_s) zurück.
38
- """
39
  frame_length = int(sr * ENERGY_FRAME_MS / 1000)
40
  hop_length = int(sr * ENERGY_HOP_MS / 1000)
41
- if frame_length <= 0:
42
- frame_length = 512
43
- if hop_length <= 0:
44
- hop_length = 160
45
 
46
- # RMS-Energie pro Frame
 
 
47
  rms = librosa.feature.rms(
48
  y=y,
49
  frame_length=frame_length,
@@ -56,7 +55,6 @@ def find_loud_events(y, sr):
56
  hop_length=hop_length
57
  )
58
 
59
- # Dynamischer Schwellwert: oberes Quantil
60
  thr = np.quantile(rms, ENERGY_QUANTILE)
61
  mask = rms > thr
62
 
@@ -66,18 +64,16 @@ def find_loud_events(y, sr):
66
 
67
  for i, is_loud in enumerate(mask):
68
  t = times[i]
 
69
  if is_loud and not in_event:
70
- # Start eines neuen lauten Events
71
  in_event = True
72
  start_t = t
73
  elif not is_loud and in_event:
74
- # Event endet
75
  end_t = t
76
  if end_t - start_t >= MIN_EVENT_DURATION:
77
  events.append((start_t, end_t))
78
  in_event = False
79
 
80
- # Falls das letzte Event bis zum Ende durchläuft
81
  if in_event:
82
  end_t = times[-1]
83
  if end_t - start_t >= MIN_EVENT_DURATION:
@@ -85,118 +81,141 @@ def find_loud_events(y, sr):
85
 
86
  return events
87
 
 
 
 
 
88
  def bark_probability_for_event(y, sr, start_s, end_s):
89
- """
90
- Berechnet nur für ein kurzes Segment die Wahrscheinlichkeit für 'dog barking'.
91
- """
92
  start_idx = int(start_s * sr)
93
  end_idx = int(end_s * sr)
94
 
95
- # Audiosegment (numpy.ndarray)
96
  segment = y[start_idx:end_idx]
97
 
98
- # Zu kurze Segmente ignorieren
99
  if len(segment) < int(0.15 * sr):
100
  return 0.0
101
 
102
- # WICHTIG: Pipeline direkt mit numpy-Array aufrufen, NICHT mit dict
103
  results = classifier(
104
  segment,
105
  candidate_labels=CANDIDATE_LABELS,
106
  multi_label=True,
107
  )
108
 
109
- # 'dog barking'-Score herausziehen
110
  for r in results:
111
  if r["label"].lower() == "dog barking":
112
  return float(r["score"])
113
 
114
  return 0.0
115
 
 
 
 
116
 
 
 
 
 
 
 
 
 
 
 
117
 
118
- def analyze_barking(audio_path):
119
- # --------- Audio laden ----------
120
- y, sr = librosa.load(audio_path, sr=16000, mono=True)
121
  duration = len(y) / sr
122
  if duration == 0:
123
- return "Keine gültige Audiodatei."
124
 
125
- # --------- 1. Schritt: laute Events finden (schnell) ----------
126
  loud_events = find_loud_events(y, sr)
127
-
128
  if not loud_events:
129
- return "Es wurden keine ausreichend lauten Ereignisse erkannt."
130
 
131
- # --------- 2. Schritt: nur diese Events mit CLAP auf 'dog barking' prüfen ----------
132
  bark_windows = []
133
- for (start_s, end_s) in loud_events:
134
- bark_score = bark_probability_for_event(y, sr, start_s, end_s)
135
- if bark_score >= BARK_PROB_THRESHOLD:
136
- bark_windows.append((start_s, end_s, bark_score))
 
 
 
 
 
137
 
138
  if not bark_windows:
139
  return (
140
  "Es wurde kein Hundebellen mit ausreichend hoher Sicherheit erkannt.\n\n"
141
- f"(Schwellwert für 'dog barking' = {BARK_PROB_THRESHOLD:.2f})"
142
  )
143
 
144
- # --------- 3. Schritt: Bell-Fenster zu Episoden zusammenfassen ----------
145
- bark_windows.sort(key=lambda x: x[0]) # nach Startzeit sortieren
146
 
147
  episodes = []
148
- current_start, current_end, _ = bark_windows[0]
149
 
150
- for start, end, _ in bark_windows[1:]:
151
- if start - current_end <= MAX_PAUSE_BETWEEN_BARKS:
152
- # gleiches Ereignis, Ende verlängern
153
- current_end = max(current_end, end)
154
  else:
155
- # neues Ereignis
156
- episodes.append((current_start, current_end))
157
- current_start, current_end = start, end
158
 
159
- episodes.append((current_start, current_end))
160
 
161
- # --------- Kennzahlen ----------
162
- count_episodes = len(episodes)
163
- total_bark_duration = sum(e_end - e_start for (e_start, e_end) in episodes)
164
 
165
  lines = []
166
- lines.append(f"**A: Anzahl der Bell-Ereignisse:** {count_episodes}")
167
- lines.append(f"**B: Gesamtdauer des Bellens:** {total_bark_duration:.1f} Sekunden")
168
- lines.append("")
169
  lines.append(
170
- f"_Regel: > {MAX_PAUSE_BETWEEN_BARKS:.0f} Sekunden Pause zwischen Bellen = neues Ereignis._"
 
171
  )
172
- lines.append("\n**Details je Bell-Ereignis:**")
173
-
174
- for i, (e_start, e_end) in enumerate(episodes, start=1):
175
- dur = e_end - e_start
176
- lines.append(
177
- f"- Ereignis {i}: von {e_start:.1f}s bis {e_end:.1f}s "
178
- f"(Dauer: {dur:.1f}s)"
179
- )
180
 
181
  return "\n".join(lines)
182
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
  demo = gr.Interface(
185
  fn=analyze_barking,
186
- inputs=gr.Audio(type="filepath", label="Audio hochladen (.wav, .mp3)"),
187
  outputs=gr.Markdown(),
188
- title="Barking Episode Analyzer (schnelle CLAP-Version)",
189
  description=(
190
- "Analysiert Hundebellen in einer Aufnahme.\n\n"
191
- "Vorgehen:\n"
192
- "- Zuerst werden nur laute Segmente über die Energie erkannt (sehr schnell).\n"
193
- "- Nur diese Segmente werden mit einem CLAP-Audiomodell auf 'dog barking' geprüft.\n"
194
- "- Bellen-Segmente, die weniger als 3 Sekunden auseinander liegen, zählen als ein Ereignis.\n"
195
- "- Ausgabe:\n"
196
- " A) Anzahl der Bell-Ereignisse\n"
197
- " B) Gesamtdauer des Bellens"
198
  ),
199
  )
200
 
201
  if __name__ == "__main__":
202
  demo.launch()
 
 
3
  import librosa
4
  import numpy as np
5
 
6
+ # ---------------------------------------------------------
7
+ # 1. AUDIO-MODELL LADEN
8
+ # ---------------------------------------------------------
9
+
10
  classifier = pipeline(
11
  task="zero-shot-audio-classification",
12
  model="laion/clap-htsat-unfused"
13
  )
14
 
 
15
  CANDIDATE_LABELS = [
16
  "dog barking",
17
  "dog growling",
 
23
  "silence",
24
  ]
25
 
26
+ # ---------------------------------------------------------
27
+ # 2. FESTE PARAMETER FÜR ENERGIE-ANALYSE
28
+ # ---------------------------------------------------------
 
 
29
 
30
+ ENERGY_FRAME_MS = 25 # Frame-Länge für Energie (ms)
31
+ ENERGY_HOP_MS = 10 # Schrittweite (ms)
32
+ ENERGY_QUANTILE = 0.80 # Lautheitsschwelle (oberes 20%-Quantil)
33
+ MIN_EVENT_DURATION = 0.25 # min. Dauer eines lauten Events (Sek.)
34
 
35
+ # ---------------------------------------------------------
36
+ # 3. FUNKTION: LAUTE EVENTS FINDEN
37
+ # ---------------------------------------------------------
38
 
39
  def find_loud_events(y, sr):
 
 
 
 
40
  frame_length = int(sr * ENERGY_FRAME_MS / 1000)
41
  hop_length = int(sr * ENERGY_HOP_MS / 1000)
 
 
 
 
42
 
43
+ frame_length = max(frame_length, 512)
44
+ hop_length = max(hop_length, 128)
45
+
46
  rms = librosa.feature.rms(
47
  y=y,
48
  frame_length=frame_length,
 
55
  hop_length=hop_length
56
  )
57
 
 
58
  thr = np.quantile(rms, ENERGY_QUANTILE)
59
  mask = rms > thr
60
 
 
64
 
65
  for i, is_loud in enumerate(mask):
66
  t = times[i]
67
+
68
  if is_loud and not in_event:
 
69
  in_event = True
70
  start_t = t
71
  elif not is_loud and in_event:
 
72
  end_t = t
73
  if end_t - start_t >= MIN_EVENT_DURATION:
74
  events.append((start_t, end_t))
75
  in_event = False
76
 
 
77
  if in_event:
78
  end_t = times[-1]
79
  if end_t - start_t >= MIN_EVENT_DURATION:
 
81
 
82
  return events
83
 
84
+ # ---------------------------------------------------------
85
+ # 4. FUNKTION: BELL-PROBABILITÄT FÜR EIN EVENT
86
+ # ---------------------------------------------------------
87
+
88
  def bark_probability_for_event(y, sr, start_s, end_s):
 
 
 
89
  start_idx = int(start_s * sr)
90
  end_idx = int(end_s * sr)
91
 
 
92
  segment = y[start_idx:end_idx]
93
 
 
94
  if len(segment) < int(0.15 * sr):
95
  return 0.0
96
 
 
97
  results = classifier(
98
  segment,
99
  candidate_labels=CANDIDATE_LABELS,
100
  multi_label=True,
101
  )
102
 
 
103
  for r in results:
104
  if r["label"].lower() == "dog barking":
105
  return float(r["score"])
106
 
107
  return 0.0
108
 
109
+ # ---------------------------------------------------------
110
+ # 5. HAUPT-ANALYSEFUNKTION (mit UI-Parametern)
111
+ # ---------------------------------------------------------
112
 
113
+ def analyze_barking(audio_path, max_pause_sec, bark_prob_threshold):
114
+ # 0. Upload prüfen
115
+ if audio_path is None or audio_path == "":
116
+ return "Es wurde keine Audiodatei hochgeladen."
117
+
118
+ # 1. Audio laden
119
+ try:
120
+ y, sr = librosa.load(audio_path, sr=16000, mono=True)
121
+ except Exception as e:
122
+ return f"Fehler beim Laden der Audiodatei: {e}"
123
 
 
 
 
124
  duration = len(y) / sr
125
  if duration == 0:
126
+ return "Die Audiodatei ist leer."
127
 
128
+ # 2. Laute Ereignisse finden
129
  loud_events = find_loud_events(y, sr)
 
130
  if not loud_events:
131
+ return "Keine lauten Ereignisse gefunden vermutlich kein Bellen."
132
 
133
+ # 3. Nur laute Events mit Modell prüfen
134
  bark_windows = []
135
+ for (s, e) in loud_events:
136
+ try:
137
+ score = bark_probability_for_event(y, sr, s, e)
138
+ except Exception as ex:
139
+ print(f"Fehler im Modellaufruf bei {s:.2f}-{e:.2f}s: {ex}")
140
+ continue
141
+
142
+ if score >= bark_prob_threshold:
143
+ bark_windows.append((s, e, score))
144
 
145
  if not bark_windows:
146
  return (
147
  "Es wurde kein Hundebellen mit ausreichend hoher Sicherheit erkannt.\n\n"
148
+ f"(Schwellwert für 'dog barking' = {bark_prob_threshold:.2f})"
149
  )
150
 
151
+ # 4. Episoden aus Bell-Segmenten bilden
152
+ bark_windows.sort(key=lambda x: x[0])
153
 
154
  episodes = []
155
+ cur_start, cur_end, _ = bark_windows[0]
156
 
157
+ for s, e, _ in bark_windows[1:]:
158
+ if s - cur_end <= max_pause_sec:
159
+ cur_end = max(cur_end, e)
 
160
  else:
161
+ episodes.append((cur_start, cur_end))
162
+ cur_start, cur_end = s, e
 
163
 
164
+ episodes.append((cur_start, cur_end))
165
 
166
+ # 5. Kennzahlen
167
+ count = len(episodes)
168
+ total_seconds = sum(e2 - e1 for (e1, e2) in episodes)
169
 
170
  lines = []
171
+ lines.append(f"**A: Anzahl der Bell-Ereignisse:** {count}")
172
+ lines.append(f"**B: Gesamtdauer des Bellens:** {total_seconds:.1f} Sekunden\n")
 
173
  lines.append(
174
+ f"_Regel_: > {max_pause_sec:.1f} Sekunden Pause = neues Ereignis\n"
175
+ f"_Schwellwert 'dog barking'_: {bark_prob_threshold:.2f}\n"
176
  )
177
+ lines.append("**Details:**")
178
+ for i, (s, e) in enumerate(episodes, start=1):
179
+ dur = e - s
180
+ lines.append(f"- Ereignis {i}: {s:.1f}s bis {e:.1f}s — Dauer: {dur:.1f}s")
 
 
 
 
181
 
182
  return "\n".join(lines)
183
 
184
+ # ---------------------------------------------------------
185
+ # 6. GRADIO UI – MIT SLIDERN
186
+ # ---------------------------------------------------------
187
+
188
+ audio_input = gr.Audio(type="filepath", label="Audio hochladen (.wav, .mp3)")
189
+
190
+ pause_slider = gr.Slider(
191
+ minimum=1.0,
192
+ maximum=10.0,
193
+ value=3.0,
194
+ step=0.5,
195
+ label="Maximale Pause zwischen Bellen (Sekunden)",
196
+ )
197
+
198
+ threshold_slider = gr.Slider(
199
+ minimum=0.1,
200
+ maximum=0.9,
201
+ value=0.35,
202
+ step=0.05,
203
+ label="Schwellwert für 'dog barking' (0–1)",
204
+ )
205
 
206
  demo = gr.Interface(
207
  fn=analyze_barking,
208
+ inputs=[audio_input, pause_slider, threshold_slider],
209
  outputs=gr.Markdown(),
210
+ title="Barking Episode Analyzer (mit Parametern)",
211
  description=(
212
+ "Erkennt Hundebellen in Aufnahmen.\n\n"
213
+ "Stell unten ein:\n"
214
+ "- wie lang die Pause sein darf, bevor ein neues Ereignis gezählt wird,\n"
215
+ "- ab welchem Schwellwert das Modell 'dog barking' als Bellen zählt."
 
 
 
 
216
  ),
217
  )
218
 
219
  if __name__ == "__main__":
220
  demo.launch()
221
+