Spaces:
Sleeping
Sleeping
File size: 10,224 Bytes
c44bb3e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | import os
import pickle
import numpy as np
import pandas as pd
import librosa
import gradio as gr
from scipy.io import wavfile
import soundfile as sf
import tempfile
import time
# Load the model
def load_model(model_path='cough_classification_model.pkl'):
with open(model_path, 'rb') as f:
components = pickle.load(f)
return components
# Extract features from audio
def extract_all_features(audio_path, sample_rate=None):
"""Extract comprehensive set of audio features"""
# Load audio file
y, sr = librosa.load(audio_path, sr=sample_rate)
# Basic features
features = {}
# Duration
features['duration'] = librosa.get_duration(y=y, sr=sr)
# RMS Energy
features['rms_mean'] = np.mean(librosa.feature.rms(y=y)[0])
features['rms_std'] = np.std(librosa.feature.rms(y=y)[0])
# Zero Crossing Rate
zcr = librosa.feature.zero_crossing_rate(y)[0]
features['zcr_mean'] = np.mean(zcr)
features['zcr_std'] = np.std(zcr)
# Spectral Features
# Spectral Centroid
centroid = librosa.feature.spectral_centroid(y=y, sr=sr)[0]
features['spectral_centroid_mean'] = np.mean(centroid)
features['spectral_centroid_std'] = np.std(centroid)
# Spectral Bandwidth
bandwidth = librosa.feature.spectral_bandwidth(y=y, sr=sr)[0]
features['spectral_bandwidth_mean'] = np.mean(bandwidth)
features['spectral_bandwidth_std'] = np.std(bandwidth)
# Spectral Contrast
contrast = librosa.feature.spectral_contrast(y=y, sr=sr)
features['spectral_contrast_mean'] = np.mean(contrast)
features['spectral_contrast_std'] = np.std(contrast)
# Spectral Rolloff
rolloff = librosa.feature.spectral_rolloff(y=y, sr=sr)[0]
features['rolloff_mean'] = np.mean(rolloff)
features['rolloff_std'] = np.std(rolloff)
# MFCCs
mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
for i in range(13):
features[f'mfcc{i+1}_mean'] = np.mean(mfccs[i])
features[f'mfcc{i+1}_std'] = np.std(mfccs[i])
# Chroma Features
chroma = librosa.feature.chroma_stft(y=y, sr=sr)
features['chroma_mean'] = np.mean(chroma)
features['chroma_std'] = np.std(chroma)
return features
# Function to segment cough from audio
def segment_cough(x, fs, cough_padding=0.2, min_cough_len=0.2, th_l_multiplier=0.1, th_h_multiplier=2):
"""Segment coughs from audio using a hysteresis comparator on the signal power"""
cough_mask = np.array([False]*len(x))
# Define hysteresis thresholds
rms = np.sqrt(np.mean(np.square(x)))
seg_th_l = th_l_multiplier * rms
seg_th_h = th_h_multiplier * rms
# Segment coughs
coughSegments = []
padding = round(fs*cough_padding)
min_cough_samples = round(fs*min_cough_len)
cough_start = 0
cough_end = 0
cough_in_progress = False
tolerance = round(0.01*fs)
below_th_counter = 0
for i, sample in enumerate(x**2):
if cough_in_progress:
if sample < seg_th_l:
below_th_counter += 1
if below_th_counter > tolerance:
cough_end = i+padding if (i+padding < len(x)) else len(x)-1
cough_in_progress = False
if (cough_end+1-cough_start-2*padding > min_cough_samples):
coughSegments.append(x[cough_start:cough_end+1])
cough_mask[cough_start:cough_end+1] = True
elif i == (len(x)-1):
cough_end = i
cough_in_progress = False
if (cough_end+1-cough_start-2*padding > min_cough_samples):
coughSegments.append(x[cough_start:cough_end+1])
else:
below_th_counter = 0
else:
if sample > seg_th_h:
cough_start = i-padding if (i-padding >= 0) else 0
cough_in_progress = True
return coughSegments, cough_mask
# Prediction function for uploaded audio
def predict_cough_health(audio_path, model_components=None):
"""Predict cough health status from audio file"""
if model_components is None:
model_components = load_model()
model = model_components['model']
scaler = model_components['scaler']
label_encoder = model_components['label_encoder']
feature_names = model_components['feature_names']
# Extract features
features = extract_all_features(audio_path)
# Convert to DataFrame with correct feature order
features_df = pd.DataFrame([features])
features_df = features_df[feature_names]
# Scale features
features_scaled = scaler.transform(features_df)
# Predict
prediction_idx = model.predict(features_scaled)[0]
prediction = label_encoder.inverse_transform([prediction_idx])[0]
# Get probabilities
probs = model.predict_proba(features_scaled)[0]
class_probs = {label_encoder.inverse_transform([i])[0]: float(prob) for i, prob in enumerate(probs)}
return prediction, class_probs
# Function to process uploaded audio file
def process_audio_file(audio_file):
"""Process uploaded audio file and return prediction"""
model_components = load_model()
prediction, probabilities = predict_cough_health(audio_file, model_components)
# Format the output
result = f"Prediction: {prediction}\n\nProbabilities:\n"
for cls, prob in probabilities.items():
result += f"{cls}: {prob:.4f}\n"
return result
# Function to process recorded audio
def process_recorded_audio(audio_array, sample_rate):
"""Process recorded audio and return prediction"""
# Save the recorded audio to a temporary file
with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_audio:
temp_filename = temp_audio.name
sf.write(temp_filename, audio_array, sample_rate)
model_components = load_model()
prediction, probabilities = predict_cough_health(temp_filename, model_components)
# Clean up the temporary file
os.unlink(temp_filename)
# Format the output
result = f"Prediction: {prediction}\n\nProbabilities:\n"
for cls, prob in probabilities.items():
result += f"{cls}: {prob:.4f}\n"
return result
# Function to detect coughs in live audio stream
def detect_coughs_in_stream(audio_array, sample_rate):
"""Detect coughs in live audio stream and analyze them"""
# Save the audio chunk to a temporary file
with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_audio:
temp_filename = temp_audio.name
sf.write(temp_filename, audio_array, sample_rate)
# Load the audio for processing
audio, sr = librosa.load(temp_filename, sr=None)
# Segment coughs
cough_segments, cough_mask = segment_cough(audio, sr)
# Clean up the temporary file
os.unlink(temp_filename)
if len(cough_segments) > 0:
# Save the first detected cough segment to a temporary file
with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as cough_file:
cough_filename = cough_file.name
sf.write(cough_filename, cough_segments[0], sr)
# Analyze the cough
model_components = load_model()
prediction, probabilities = predict_cough_health(cough_filename, model_components)
# Clean up the temporary file
os.unlink(cough_filename)
# Format the output
result = f"Cough detected! Prediction: {prediction}\n\nProbabilities:\n"
for cls, prob in probabilities.items():
result += f"{cls}: {prob:.4f}\n"
return result
else:
return "No cough detected in this audio segment."
# Create Gradio interface
def create_interface():
model_components = load_model()
with gr.Blocks(title="Cough Health Analyzer") as demo:
gr.Markdown("# Cough Health Analyzer")
gr.Markdown("Upload an audio file containing a cough or record a cough to analyze its health status.")
with gr.Tab("Upload Audio"):
with gr.Row():
audio_input = gr.Audio(type="filepath", label="Upload Audio File")
output_text = gr.Textbox(label="Prediction Result")
analyze_btn = gr.Button("Analyze Cough")
analyze_btn.click(fn=process_audio_file, inputs=audio_input, outputs=output_text)
with gr.Tab("Record Audio"):
with gr.Row():
audio_recorder = gr.Audio(type="numpy", source="microphone", label="Record Audio")
record_output = gr.Textbox(label="Prediction Result")
record_btn = gr.Button("Analyze Recording")
record_btn.click(fn=process_recorded_audio, inputs=[audio_recorder], outputs=record_output)
with gr.Tab("Live Cough Detection"):
gr.Markdown("This tab continuously monitors audio for coughs and analyzes them when detected.")
with gr.Row():
live_audio = gr.Audio(type="numpy", source="microphone", streaming=True, label="Live Audio Stream")
live_output = gr.Textbox(label="Detection Result")
live_audio.stream(fn=detect_coughs_in_stream, outputs=live_output)
gr.Markdown("## API Usage")
gr.Markdown("""
This Gradio app can be accessed via API calls. Example Python code:
```python
import requests
import json
# For file upload
files = {'audio': open('your_audio_file.wav', 'rb')}
response = requests.post('YOUR_GRADIO_URL/api/predict', files=files)
result = json.loads(response.content)
print(result)
```
""")
return demo
# Main function
if __name__ == "__main__":
demo = create_interface()
demo.launch(share=True) |