| import matplotlib.pyplot as plt |
| import numpy as np |
| import yasa |
| import io |
| from PIL import Image |
| from processing import butter_bandpass, filtfilt |
| import logging |
|
|
| |
| logging.basicConfig( |
| level=logging.INFO, |
| format='%(asctime)s - %(levelname)s - %(message)s', |
| handlers=[ |
| logging.FileHandler('neuronap.log'), |
| logging.StreamHandler() |
| ] |
| ) |
| logger = logging.getLogger(__name__) |
|
|
| def plot_eeg_signals(time_s, eeg_uv, eeg_uv_bp, eeg_uv_notched): |
| logger.info("Generating EEG signals plot") |
| plt.figure(figsize=(12, 8)) |
| plt.plot(time_s, eeg_uv, label='Raw EEG (µV)', alpha=0.5) |
| plt.plot(time_s, eeg_uv_bp, label='Bandpass EEG (0.5-30 Hz)', linewidth=2) |
| plt.plot(time_s, eeg_uv_notched, label='Notched EEG (50 Hz)', linewidth=2, linestyle='--') |
| plt.title("EEG Signal: Raw vs Filtered", fontsize=42) |
| plt.xlabel("Time (s)", fontsize=36) |
| plt.ylabel("Amplitude (µV)", fontsize=36) |
| plt.xticks(fontsize=28) |
| plt.yticks(fontsize=28) |
| plt.grid(True) |
| plt.legend(fontsize=18.5, loc='upper right') |
| plt.tight_layout() |
| plt.savefig("eeg_signal_plot.pdf", format='pdf', dpi=300) |
| logger.info("Saved EEG signals plot to eeg_signal_plot.pdf") |
| buf = io.BytesIO() |
| plt.savefig(buf, format='png') |
| buf.seek(0) |
| img = Image.open(buf) |
| plt.close() |
| return img |
|
|
| def plot_frequency_spectra(eeg_uv_notched, fs): |
| logger.info("Generating frequency spectra plot") |
| from scipy.signal import welch |
| f_welch, psd_welch = welch(eeg_uv_notched, fs, nperseg=1024, noverlap=512) |
| plt.figure(figsize=(12, 12)) |
| plt.subplot(2,1,1) |
| n = len(eeg_uv_notched) |
| freqs_fft = np.fft.fftfreq(n, d=1/fs) |
| fft_magnitude = np.abs(np.fft.fft(eeg_uv_notched)) / n |
| plt.plot(freqs_fft[:n//2], fft_magnitude[:n//2] * 2, linewidth=2) |
| plt.title("FFT Spectrum", fontsize=28) |
| plt.xlabel("Frequency (Hz)", fontsize=24) |
| plt.ylabel("Magnitude (µV)", fontsize=24) |
| plt.grid(True) |
| plt.subplot(2,1,2) |
| plt.semilogy(f_welch, psd_welch, linewidth=2) |
| plt.title("Welch PSD", fontsize=28) |
| plt.xlabel("Frequency (Hz)", fontsize=24) |
| plt.ylabel("Power/Frequency (µV²/Hz)", fontsize=24) |
| plt.grid(True) |
| plt.tight_layout() |
| plt.savefig("eeg_fft_welch.pdf", format='pdf', dpi=300) |
| logger.info("Saved frequency spectra plot to eeg_fft_welch.pdf") |
| buf = io.BytesIO() |
| plt.savefig(buf, format='png') |
| buf.seek(0) |
| img = Image.open(buf) |
| plt.close() |
| return img |
|
|
| def plot_band_waveforms(eeg_uv_notched, fs, start_s=3000, end_s=3020): |
| logger.info("Generating frequency bands plot") |
| time_s = np.arange(len(eeg_uv_notched)) / fs |
| mask = (time_s >= start_s) & (time_s <= end_s) |
| window = eeg_uv_notched[mask] |
| time_window = time_s[mask] |
| eeg_delta = filtfilt(*butter_bandpass(0.5, 4, fs), window) |
| eeg_theta = filtfilt(*butter_bandpass(4, 8, fs), window) |
| eeg_alpha = filtfilt(*butter_bandpass(8, 13, fs), window) |
| eeg_beta = filtfilt(*butter_bandpass(13, 30, fs), window) |
| plt.figure(figsize=(16, 18)) |
| plt.subplot(4,1,1); plt.plot(time_window, eeg_delta, linewidth=2); plt.title("Delta (0.5-4 Hz)", fontsize=28) |
| plt.subplot(4,1,2); plt.plot(time_window, eeg_theta, linewidth=2); plt.title("Theta (4-8 Hz)", fontsize=28) |
| plt.subplot(4,1,3); plt.plot(time_window, eeg_alpha, linewidth=2); plt.title("Alpha (8-13 Hz)", fontsize=28) |
| plt.subplot(4,1,4); plt.plot(time_window, eeg_beta, linewidth=2); plt.title("Beta (13-30 Hz)", fontsize=28) |
| plt.tight_layout() |
| plt.savefig("eeg_bands_plot.pdf", format='pdf', dpi=300) |
| logger.info("Saved frequency bands plot to eeg_bands_plot.pdf") |
| buf = io.BytesIO() |
| plt.savefig(buf, format='png') |
| buf.seek(0) |
| img = Image.open(buf) |
| plt.close() |
| return img |
|
|
| def plot_hypnogram(hypno): |
| logger.info("Generating hypnogram plot") |
| hypno_int = yasa.hypno_str_to_int(hypno) |
| time_minutes = np.arange(len(hypno_int)) * 0.5 |
| start_min, end_min = 40, 170 |
| start_epoch = int(start_min / 0.5) |
| end_epoch = int(end_min / 0.5) |
| window_time = time_minutes[start_epoch:end_epoch] |
| window_hypno = hypno_int[start_epoch:end_epoch] |
| plt.figure(figsize=(16, 6)) |
| plt.step(window_time, window_hypno, where='post', color='navy', linewidth=3) |
| plt.gca().invert_yaxis() |
| plt.yticks([4, 3, 2, 1, 0], ['W', 'N1', 'N2', 'N3', 'R'], fontsize=28) |
| plt.xlabel('Time (minutes)', fontsize=32) |
| plt.ylabel('Sleep Stage', fontsize=32) |
| plt.title(f'Hypnogram ({start_min}-{end_min} min)', fontsize=36) |
| plt.grid(axis='x', linestyle='--', alpha=0.5) |
| plt.tight_layout() |
| plt.savefig("hypnogram_plot.pdf", format='pdf', dpi=300) |
| logger.info("Saved hypnogram to hypnogram_plot.pdf") |
| buf = io.BytesIO() |
| plt.savefig(buf, format='png') |
| buf.seek(0) |
| img = Image.open(buf) |
| plt.close() |
| return img |