ftiiii commited on
Commit
a117a90
·
verified ·
1 Parent(s): 2c3ca13

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -65
app.py CHANGED
@@ -1,4 +1,4 @@
1
- import gradio as gr
2
  import numpy as np
3
  import librosa
4
  import librosa.display
@@ -6,14 +6,17 @@ import matplotlib
6
  matplotlib.use("Agg")
7
  import matplotlib.pyplot as plt
8
  import pywt
9
- import os
10
- import json
11
- import random
12
- import tempfile
13
  from PIL import Image
14
  from tensorflow.keras.models import load_model
15
  from sklearn.preprocessing import StandardScaler
 
 
 
16
 
 
 
 
17
  SAMPLE_RATE = 22050
18
  MAX_DURATION = 5
19
  TIME_STEPS = 20
@@ -36,6 +39,9 @@ with open("label_map.json", "r") as f:
36
  label_map = json.load(f)
37
  index_to_label = {v: k for k, v in label_map.items()}
38
 
 
 
 
39
  def denoise_wavelet(signal, wavelet='db8', level=4):
40
  coeffs = pywt.wavedec(signal, wavelet, level=level)
41
  sigma = np.median(np.abs(coeffs[-1])) / 0.6745
@@ -52,35 +58,38 @@ def cat_2s_ngau_nhien(y, sr, duration=2):
52
  start = random.randint(0, len(y) - duration * sr)
53
  return y[start:start + duration * sr]
54
 
 
 
 
 
 
 
 
 
 
 
 
55
  def tao_anh_mel(file_path):
56
  y, sr = librosa.load(file_path, sr=None, mono=True)
57
  y = cat_2s_ngau_nhien(y, sr)
58
  S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128)
59
  S_dB = librosa.power_to_db(S, ref=np.max)
60
  fig, ax = plt.subplots(figsize=(6, 3))
61
- img = librosa.display.specshow(S_dB, sr=sr, x_axis='time', y_axis='mel', ax=ax, cmap='magma')
62
  ax.set_title("Phổ tần Mel", fontsize=10)
63
- fig.colorbar(img, ax=ax)
64
- plt.tight_layout()
65
- path = os.path.join(tempfile.gettempdir(), "mel.png")
66
- fig.savefig(path, dpi=80)
67
- plt.close()
68
- return Image.open(path)
69
 
70
  def tao_wavelet_transform(file_path):
71
  y, sr = librosa.load(file_path, sr=None, mono=True)
72
  y = cat_2s_ngau_nhien(y, sr)
73
  coef, _ = pywt.cwt(y, scales=np.arange(1, 128), wavelet='morl', sampling_period=1/sr)
74
  fig, ax = plt.subplots(figsize=(6, 3))
75
- ax.imshow(np.abs(coef), extent=[0, len(y)/sr, 1, 128], cmap='plasma', aspect='auto', origin='lower')
 
76
  ax.set_title("Phổ sóng con (Wavelet)")
77
  ax.set_xlabel("Thời gian (s)")
78
  ax.set_ylabel("Tần số (scale)")
79
- plt.tight_layout()
80
- path = os.path.join(tempfile.gettempdir(), "wavelet.png")
81
- fig.savefig(path, dpi=80)
82
- plt.close()
83
- return Image.open(path)
84
 
85
  def tao_waveform_image(file_path):
86
  y, sr = librosa.load(file_path, sr=None, mono=True)
@@ -90,28 +99,80 @@ def tao_waveform_image(file_path):
90
  ax.set_title("Biểu đồ Sóng Âm (Waveform)")
91
  ax.set_xlabel("Thời gian (s)")
92
  ax.set_ylabel("Biên độ")
93
- plt.tight_layout()
94
- path = os.path.join(tempfile.gettempdir(), "waveform.png")
95
- fig.savefig(path, dpi=80)
96
- plt.close()
97
- return Image.open(path)
98
 
99
- def bao_san_sang(file_path):
100
- if not file_path:
101
- return ""
102
- return "<b style='color:green;'>✅ Âm thanh đã sẵn sàng. Nhấn kiểm tra ngay!</b>"
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
- def sinh_anh(file_path):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  if not file_path:
106
- return None, None, None
107
- mel_img = tao_anh_mel(file_path)
108
- wavelet_img = tao_wavelet_transform(file_path)
109
- waveform_img = tao_waveform_image(file_path)
110
- return mel_img, wavelet_img, waveform_img
 
 
 
111
 
112
  def du_doan(file_path):
113
  if not file_path:
114
- return "<b style='color:red;'>❌ Chưa có âm thanh.</b>"
115
 
116
  signal, sr = librosa.load(file_path, sr=SAMPLE_RATE, mono=True)
117
  signal, _ = librosa.effects.trim(signal)
@@ -125,10 +186,11 @@ def du_doan(file_path):
125
  X_input = create_sequences(mfcc, time_steps=TIME_STEPS)
126
 
127
  if len(X_input) == 0:
128
- return "<b style='color:red;'>⚠️ Âm thanh quá ngắn để phân tích.</b>"
129
 
130
  y_preds = model.predict(X_input, verbose=0)
131
  avg_probs = np.mean(y_preds, axis=0)
 
132
  pred_index = np.argmax(avg_probs)
133
  confidence = avg_probs[pred_index] * 100
134
  pred_label = "HƯ HỎNG KHÁC" if confidence < 50 else index_to_label[pred_index]
@@ -142,14 +204,18 @@ def du_doan(file_path):
142
  for i, prob in enumerate(avg_probs):
143
  html += f"<span style='color:#000'>- {index_to_label[i]}: {prob*100:.1f}%</span><br>"
144
  html += "</div>"
145
- return html
146
 
147
- def reset_output():
148
- return "", None, None, None, ""
149
 
150
- def chon_file(f1, f2):
151
- return f1 if f1 else f2
 
 
 
152
 
 
 
 
153
  with gr.Blocks(css="""
154
  #check-btn {
155
  background: #007acc;
@@ -200,33 +266,40 @@ with gr.Blocks(css="""
200
  output_html = gr.HTML()
201
 
202
  with gr.Accordion("📊 Phân tích Âm Thanh", open=False):
203
- mel_output = gr.Image(label="")
204
- wavelet_output = gr.Image(label="")
205
- waveform_output = gr.Image(label="")
206
-
207
- audio_file.change(fn=bao_san_sang, inputs=audio_file, outputs=thong_bao_ready)
208
- audio_mic.change(fn=bao_san_sang, inputs=audio_mic, outputs=thong_bao_ready)
209
-
210
- audio_file.change(fn=sinh_anh, inputs=audio_file, outputs=[mel_output, wavelet_output, waveform_output])
211
- audio_mic.change(fn=sinh_anh, inputs=audio_mic, outputs=[mel_output, wavelet_output, waveform_output])
212
-
213
- btn_check.click(fn=lambda f1, f2: du_doan(chon_file(f1, f2)),
214
- inputs=[audio_file, audio_mic],
215
- outputs=output_html)
216
-
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  audio_file.clear(fn=reset_output, outputs=[
218
- thong_bao_ready,
219
- mel_output,
220
- wavelet_output,
221
- waveform_output,
222
- output_html
223
  ])
224
  audio_mic.clear(fn=reset_output, outputs=[
225
- thong_bao_ready,
226
- mel_output,
227
- wavelet_output,
228
- waveform_output,
229
- output_html
230
  ])
231
 
232
- demo.launch()
 
1
+ import gradio as gr
2
  import numpy as np
3
  import librosa
4
  import librosa.display
 
6
  matplotlib.use("Agg")
7
  import matplotlib.pyplot as plt
8
  import pywt
9
+ import io
 
 
 
10
  from PIL import Image
11
  from tensorflow.keras.models import load_model
12
  from sklearn.preprocessing import StandardScaler
13
+ import json
14
+ import random
15
+ import plotly.express as px
16
 
17
+ # ================================
18
+ # CẤU HÌNH
19
+ # ================================
20
  SAMPLE_RATE = 22050
21
  MAX_DURATION = 5
22
  TIME_STEPS = 20
 
39
  label_map = json.load(f)
40
  index_to_label = {v: k for k, v in label_map.items()}
41
 
42
+ # ================================
43
+ # HÀM TIỀN XỬ LÝ
44
+ # ================================
45
  def denoise_wavelet(signal, wavelet='db8', level=4):
46
  coeffs = pywt.wavedec(signal, wavelet, level=level)
47
  sigma = np.median(np.abs(coeffs[-1])) / 0.6745
 
58
  start = random.randint(0, len(y) - duration * sr)
59
  return y[start:start + duration * sr]
60
 
61
+ # ================================
62
+ # VẼ ẢNH (numpy array)
63
+ # ================================
64
+ def fig_to_numpy(fig):
65
+ buf = io.BytesIO()
66
+ fig.savefig(buf, format="png", dpi=90, bbox_inches="tight")
67
+ buf.seek(0)
68
+ img = Image.open(buf)
69
+ plt.close(fig)
70
+ return np.array(img)
71
+
72
  def tao_anh_mel(file_path):
73
  y, sr = librosa.load(file_path, sr=None, mono=True)
74
  y = cat_2s_ngau_nhien(y, sr)
75
  S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128)
76
  S_dB = librosa.power_to_db(S, ref=np.max)
77
  fig, ax = plt.subplots(figsize=(6, 3))
78
+ librosa.display.specshow(S_dB, sr=sr, x_axis='time', y_axis='mel', ax=ax, cmap='magma')
79
  ax.set_title("Phổ tần Mel", fontsize=10)
80
+ return fig_to_numpy(fig)
 
 
 
 
 
81
 
82
  def tao_wavelet_transform(file_path):
83
  y, sr = librosa.load(file_path, sr=None, mono=True)
84
  y = cat_2s_ngau_nhien(y, sr)
85
  coef, _ = pywt.cwt(y, scales=np.arange(1, 128), wavelet='morl', sampling_period=1/sr)
86
  fig, ax = plt.subplots(figsize=(6, 3))
87
+ ax.imshow(np.abs(coef), extent=[0, len(y)/sr, 1, 128],
88
+ cmap='plasma', aspect='auto', origin='lower')
89
  ax.set_title("Phổ sóng con (Wavelet)")
90
  ax.set_xlabel("Thời gian (s)")
91
  ax.set_ylabel("Tần số (scale)")
92
+ return fig_to_numpy(fig)
 
 
 
 
93
 
94
  def tao_waveform_image(file_path):
95
  y, sr = librosa.load(file_path, sr=None, mono=True)
 
99
  ax.set_title("Biểu đồ Sóng Âm (Waveform)")
100
  ax.set_xlabel("Thời gian (s)")
101
  ax.set_ylabel("Biên độ")
102
+ return fig_to_numpy(fig)
 
 
 
 
103
 
104
+ def tao_waveform_denoise(file_path):
105
+ y, sr = librosa.load(file_path, sr=None, mono=True)
106
+ y = cat_2s_ngau_nhien(y, sr)
107
+ y_denoised = denoise_wavelet(y)
108
+
109
+ fig, ax = plt.subplots(3, 2, figsize=(10, 8))
110
+
111
+ # 1. Waveform
112
+ librosa.display.waveshow(y, sr=sr, ax=ax[0,0], color='red')
113
+ ax[0,0].set_title("Waveform - Trước lọc")
114
+ librosa.display.waveshow(y_denoised, sr=sr, ax=ax[0,1], color='green')
115
+ ax[0,1].set_title("Waveform - Sau lọc")
116
+
117
+ # 2. FFT
118
+ freqs = np.fft.rfftfreq(len(y), 1/sr)
119
+ fft_y = np.abs(np.fft.rfft(y))
120
+ fft_y_denoised = np.abs(np.fft.rfft(y_denoised))
121
 
122
+ ax[1,0].plot(freqs, fft_y, color='red')
123
+ ax[1,0].set_xlim(0, 8000)
124
+ ax[1,0].set_title("FFT - Trước lọc")
125
+
126
+ ax[1,1].plot(freqs, fft_y_denoised, color='green')
127
+ ax[1,1].set_xlim(0, 8000)
128
+ ax[1,1].set_title("FFT - Sau lọc")
129
+
130
+ # 3. Spectrogram
131
+ D1 = librosa.amplitude_to_db(np.abs(librosa.stft(y)), ref=np.max)
132
+ D2 = librosa.amplitude_to_db(np.abs(librosa.stft(y_denoised)), ref=np.max)
133
+
134
+ img1 = librosa.display.specshow(D1, sr=sr, x_axis='time', y_axis='log', ax=ax[2,0], cmap="magma")
135
+ ax[2,0].set_title("Spectrogram - Trước lọc")
136
+ fig.colorbar(img1, ax=ax[2,0], format="%+2.0f dB")
137
+
138
+ img2 = librosa.display.specshow(D2, sr=sr, x_axis='time', y_axis='log', ax=ax[2,1], cmap="magma")
139
+ ax[2,1].set_title("Spectrogram - Sau lọc")
140
+ fig.colorbar(img2, ax=ax[2,1], format="%+2.0f dB")
141
+
142
+ plt.tight_layout()
143
+ return fig_to_numpy(fig)
144
+
145
+ # ================================
146
+ # VẼ BIỂU ĐỒ Top-3 (Plotly)
147
+ # ================================
148
+ def ve_top3_chart(probs):
149
+ labels = [index_to_label[i] for i in range(len(probs))]
150
+ values = probs * 100
151
+ top_idx = np.argsort(values)[::-1][:3]
152
+ fig = px.pie(
153
+ values=[values[i] for i in top_idx],
154
+ names=[labels[i] for i in top_idx],
155
+ title="Top-3 dự đoán"
156
+ )
157
+ return fig
158
+
159
+ # ================================
160
+ # DỰ ĐOÁN
161
+ # ================================
162
+ def bao_san_sang(file_path):
163
  if not file_path:
164
+ return "", None, None, None, None
165
+ return (
166
+ "<b style='color:green;'>✅ Âm thanh đã sẵn sàng. Nhấn kiểm tra ngay!</b>",
167
+ tao_anh_mel(file_path),
168
+ tao_wavelet_transform(file_path),
169
+ tao_waveform_image(file_path),
170
+ tao_waveform_denoise(file_path)
171
+ )
172
 
173
  def du_doan(file_path):
174
  if not file_path:
175
+ return "<b style='color:red;'>❌ Chưa có âm thanh.</b>", None
176
 
177
  signal, sr = librosa.load(file_path, sr=SAMPLE_RATE, mono=True)
178
  signal, _ = librosa.effects.trim(signal)
 
186
  X_input = create_sequences(mfcc, time_steps=TIME_STEPS)
187
 
188
  if len(X_input) == 0:
189
+ return "<b style='color:red;'>⚠️ Âm thanh quá ngắn để phân tích.</b>", None
190
 
191
  y_preds = model.predict(X_input, verbose=0)
192
  avg_probs = np.mean(y_preds, axis=0)
193
+
194
  pred_index = np.argmax(avg_probs)
195
  confidence = avg_probs[pred_index] * 100
196
  pred_label = "HƯ HỎNG KHÁC" if confidence < 50 else index_to_label[pred_index]
 
204
  for i, prob in enumerate(avg_probs):
205
  html += f"<span style='color:#000'>- {index_to_label[i]}: {prob*100:.1f}%</span><br>"
206
  html += "</div>"
 
207
 
208
+ return html, ve_top3_chart(avg_probs)
 
209
 
210
+ # ================================
211
+ # RESET
212
+ # ================================
213
+ def reset_output():
214
+ return "", None, None, None, None, "", None
215
 
216
+ # ================================
217
+ # GIAO DIỆN
218
+ # ================================
219
  with gr.Blocks(css="""
220
  #check-btn {
221
  background: #007acc;
 
266
  output_html = gr.HTML()
267
 
268
  with gr.Accordion("📊 Phân tích Âm Thanh", open=False):
269
+ mel_output = gr.Image(label="Mel Spectrogram", type="numpy")
270
+ wavelet_output = gr.Image(label="Wavelet Transform", type="numpy")
271
+ waveform_output = gr.Image(label="Waveform", type="numpy")
272
+ waveform_denoise_output = gr.Image(label="So sánh chi tiết trước & sau lọc nhiễu (Waveform + FFT + Spectrogram)", type="numpy")
273
+ top3_chart = gr.Plot(label="Top-3 dự đoán")
274
+
275
+ # --- Upload/ghi âm → chỉ báo sẵn sàng + vẽ ảnh
276
+ audio_file.change(
277
+ fn=bao_san_sang,
278
+ inputs=audio_file,
279
+ outputs=[thong_bao_ready, mel_output, wavelet_output, waveform_output, waveform_denoise_output]
280
+ )
281
+
282
+ audio_mic.change(
283
+ fn=bao_san_sang,
284
+ inputs=audio_mic,
285
+ outputs=[thong_bao_ready, mel_output, wavelet_output, waveform_output, waveform_denoise_output]
286
+ )
287
+
288
+ # --- Nút kiểm tra → chỉ dự đoán
289
+ btn_check.click(
290
+ fn=lambda f1, f2: du_doan(f1 if f1 else f2),
291
+ inputs=[audio_file, audio_mic],
292
+ outputs=[output_html, top3_chart]
293
+ )
294
+
295
+ # --- Reset khi clear
296
  audio_file.clear(fn=reset_output, outputs=[
297
+ thong_bao_ready, mel_output, wavelet_output, waveform_output, waveform_denoise_output,
298
+ output_html, top3_chart
 
 
 
299
  ])
300
  audio_mic.clear(fn=reset_output, outputs=[
301
+ thong_bao_ready, mel_output, wavelet_output, waveform_output, waveform_denoise_output,
302
+ output_html, top3_chart
 
 
 
303
  ])
304
 
305
+ demo.launch()