Prabin1 commited on
Commit
f0c0f28
·
verified ·
1 Parent(s): d38db8f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +199 -0
app.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy as np
3
+ import tensorflow as tf
4
+ import librosa
5
+ import gradio as gr
6
+
7
+ # -----------------------
8
+
9
+ # Config
10
+
11
+ # -----------------------
12
+
13
+ MODEL_PATH = "your_model.keras" # <-- replace if your model filename differs
14
+ SR = 16000
15
+ N_COEFFS = 20
16
+
17
+ # -----------------------
18
+
19
+ # Feature extraction (adapted from your code)
20
+
21
+ # -----------------------
22
+
23
+ def extract_mfcc(y, sr, n_mfcc=N_COEFFS):
24
+ mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=n_mfcc)
25
+ return mfccs
26
+
27
+ def extract_lfcc(y, sr, n_lfcc=N_COEFFS):
28
+ S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=n_lfcc, fmin=0, fmax=sr/2)
29
+ lfccs = librosa.power_to_db(S)
30
+ return lfccs
31
+
32
+ def extract_features_with_time_series(file_path, sr=SR, n_coeffs=N_COEFFS):
33
+ try:
34
+ y, _ = librosa.load(file_path, sr=sr, mono=True)
35
+ y, _ = librosa.effects.trim(y)
36
+
37
+ ```
38
+ # Normalize amplitude
39
+ if np.max(np.abs(y)) > 0:
40
+ y = y / np.max(np.abs(y))
41
+
42
+ # Extract features
43
+ mfccs = extract_mfcc(y, sr, n_mfcc=n_coeffs)
44
+ lfccs = extract_lfcc(y, sr, n_lfcc=n_coeffs)
45
+ chroma = librosa.feature.chroma_stft(y=y, sr=sr)
46
+ spec_centroid = librosa.feature.spectral_centroid(y=y, sr=sr)
47
+ spec_bandwidth = librosa.feature.spectral_bandwidth(y=y, sr=sr)
48
+ zcr = librosa.feature.zero_crossing_rate(y)
49
+
50
+ features_to_stack = [mfccs, lfccs, chroma, spec_centroid, spec_bandwidth, zcr]
51
+
52
+ # Pad/truncate along time axis (axis=1 for these matrices)
53
+ max_len = max([f.shape[1] for f in features_to_stack])
54
+ padded_features = []
55
+ for f in features_to_stack:
56
+ # librosa.util.fix_length works on axis=-1 by default; specify axis=1 for time axis
57
+ padded = librosa.util.fix_length(f, size=max_len, axis=1)
58
+ padded_features.append(padded)
59
+
60
+ stacked_features = np.vstack(padded_features).astype(np.float32) # shape: (feature_dim, time)
61
+ # transpose to (time, feature_dim)
62
+ return stacked_features.T
63
+
64
+ except Exception as e:
65
+ print(f"[extract_features] Error processing {file_path}: {e}")
66
+ return None
67
+ ```
68
+
69
+ # -----------------------
70
+
71
+ # Load model
72
+
73
+ # -----------------------
74
+
75
+ print(f"Loading model from {MODEL_PATH} ...")
76
+ model = tf.keras.models.load_model(MODEL_PATH)
77
+ print("Model loaded. input_shape =", model.input_shape)
78
+
79
+ # Helper to prepare features for model input
80
+
81
+ def prepare_input_for_model(features, model):
82
+ """
83
+ features: np.array of shape (time, feature_dim)
84
+ model: loaded keras model
85
+ Returns: np.array shaped as model expects, with batch dim
86
+ """
87
+ features = np.asarray(features, dtype=np.float32)
88
+ input_shape = model.input_shape # e.g. (None, T, D) or (None, some_flat_len)
89
+
90
+ ```
91
+ # Remove batch dim
92
+ target_shape = input_shape[1:]
93
+
94
+ if len(target_shape) == 2:
95
+ # model expects (timesteps, dim)
96
+ target_T, target_D = target_shape
97
+ # If the feature dim does not match, try transpose
98
+ if target_D is not None and target_D != features.shape[1]:
99
+ if target_D == features.shape[0]:
100
+ features = features.T
101
+ else:
102
+ raise ValueError(f"Model expects feature dim {target_D} but got {features.shape[1]}")
103
+
104
+ # Pad/truncate time axis if target_T is specified
105
+ if target_T is not None:
106
+ cur_T = features.shape[0]
107
+ if cur_T < target_T:
108
+ pad_amount = target_T - cur_T
109
+ pad_width = ((0, pad_amount), (0, 0))
110
+ features = np.pad(features, pad_width, mode="constant")
111
+ elif cur_T > target_T:
112
+ features = features[:target_T, :]
113
+
114
+ elif len(target_shape) == 1:
115
+ # model expects 1D input, flatten features
116
+ flat = features.flatten()
117
+ target_len = target_shape[0]
118
+ if target_len is not None:
119
+ if flat.shape[0] < target_len:
120
+ flat = np.pad(flat, (0, target_len - flat.shape[0]), mode="constant")
121
+ else:
122
+ flat = flat[:target_len]
123
+ features = flat
124
+
125
+ else:
126
+ raise ValueError(f"Unsupported model input shape: {input_shape}")
127
+
128
+ # Add batch dimension
129
+ return np.expand_dims(features, axis=0)
130
+ ```
131
+
132
+ # -----------------------
133
+
134
+ # Prediction function for Gradio
135
+
136
+ # -----------------------
137
+
138
+ def predict(audio_filepath):
139
+ """audio_filepath: path to uploaded audio (Gradio provides this when type='filepath')"""
140
+ try:
141
+ if audio_filepath is None:
142
+ return {"error": "No audio file provided."}
143
+
144
+ ```
145
+ feats = extract_features_with_time_series(audio_filepath)
146
+ if feats is None:
147
+ return {"error": "Feature extraction failed."}
148
+
149
+ X = prepare_input_for_model(feats, model)
150
+ raw_pred = model.predict(X)
151
+
152
+ # Interpret raw_pred to a probability for the 'Fake' class
153
+ raw = np.asarray(raw_pred).squeeze()
154
+ # If single value per sample
155
+ if raw.size == 1:
156
+ val = float(raw)
157
+ # If value already in [0,1], assume probability; otherwise pass through sigmoid
158
+ if 0.0 <= val <= 1.0:
159
+ prob_fake = val
160
+ else:
161
+ prob_fake = 1.0 / (1.0 + np.exp(-val))
162
+ else:
163
+ # multi-class: assume class index 1 == Fake if present
164
+ import tensorflow as _tf
165
+ probs = _tf.nn.softmax(raw).numpy()
166
+ if probs.size >= 2:
167
+ prob_fake = float(probs[1])
168
+ else:
169
+ prob_fake = float(probs.max())
170
+
171
+ prob_fake = float(np.clip(prob_fake, 0.0, 1.0))
172
+ prob_real = 1.0 - prob_fake
173
+ label = "Fake" if prob_fake > 0.5 else "Real"
174
+
175
+ # Return mapping suitable for gr.Label: {"Fake": prob, "Real": prob}
176
+ return {"Fake": prob_fake, "Real": prob_real}
177
+ except Exception as e:
178
+ return {"error": f"Prediction error: {e}"}
179
+ ```
180
+
181
+ # -----------------------
182
+
183
+ # Gradio UI
184
+
185
+ # -----------------------
186
+
187
+ title = "Deepfake Audio Detector"
188
+ description = "Upload an audio file (wav/mp3). The app runs the embedded preprocessing to extract MFCC/LFCC/etc., then runs your .keras model. The Space expects the model file named 'your_model.keras' in the repo root."
189
+
190
+ iface = gr.Interface(
191
+ fn=predict,
192
+ inputs=gr.Audio(source="upload", type="filepath", label="Upload audio file"),
193
+ outputs=gr.Label(num_top_classes=2, label="Prediction (probabilities)"),
194
+ title=title,
195
+ description=description,
196
+ )
197
+
198
+ if **name** == "**main**":
199
+ iface.launch()