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)