Views
No views yet
1"model_summary":
2"Model: Total params: 5,046 (19.71 KB)
3 Trainable params: 5,044 (19.70 KB)
4 Non-trainable params: 0 (0.00 B)
5 Optimizer params: 2 (12.00 B)",
6 "layers": [
7 {
8 "name": "dense",
9 "trainable": true,
10 "count_params": 2368
11 },
12 {
13 "name": "dropout",
14 "trainable": true,
15 "count_params": 0
16 },
17 {
18 "name": "dense_1",
19 "trainable": true,
20 "count_params": 2080
21 },
22 {
23 "name": "dropout_1",
24 "trainable": true,
25 "count_params": 0
26 },
27 {
28 "name": "dense_2",
29 "trainable": true,
30 "count_params": 528
31 },
32 {
33 "name": "dense_3",
34 "trainable": true,
35 "count_params": 68
36 }
37 ]1import numpy as np
2import matplotlib.pyplot as plt
3import mne
4from matplotlib.animation import FuncAnimation
5from tensorflow.keras.models import load_model
6import joblib
7
8
9class EEGMonitor:
10 def __init__(self, model_path, scaler_path):
11 self.model = load_model(model_path)
12 self.scaler = joblib.load(scaler_path)
13 self.ch_names = ['T7', 'C3', 'Cz', 'C4', 'T8', 'Pz']
14 self.fs = 1000 # Örnekleme frekansı / Sampling frequency
15 self.buffer_size = 1000 # 1 saniyelik buffer / 1 second buffer
16
17 self.raw_buffer = np.zeros((6, self.buffer_size))
18 self.feature_contributions = {ch: [] for ch in self.ch_names}
19
20 # Elektrot pozisyonları (10-20 sistemi) / Electrode positions (10-20 system)
21 self.montage = mne.channels.make_standard_montage('standard_1020')
22
23 self.fig = plt.figure(figsize=(15, 10))
24 self.setup_plots()
25
26 def setup_plots(self):
27 self.ax1 = self.fig.add_subplot(223)
28 self.ax1.set_title("Canlı EEG Sinyalleri / Live EEG Signals")
29 self.ax1.set_xlabel("Zaman (ms) / Time (ms)")
30 self.ax1.set_ylabel("Amplitüd (µV) / Amplitude (µV)")
31
32 self.ax2 = self.fig.add_subplot(221)
33 self.ax2.set_title("Elektrot Konumları / Electrode Locations")
34
35 self.ax3 = self.fig.add_subplot(224)
36 self.ax3.set_title("Elektrot Katkı Oranları / Electrode Contribution Ratios")
37 self.ax3.set_ylim(0, 1)
38
39 self.ax4 = self.fig.add_subplot(222)
40 self.ax4.set_title("Duygu Tahmin Olasılıkları / Emotion Prediction Probabilities")
41 self.ax4.set_ylim(0, 1)
42
43 plt.tight_layout()
44
45 def generate_synthetic_data(self):
46 """Sentetik EEG verisi üretir (6 kanal x 1000 örnek) / Generates synthetic EEG data (6 channels x 1000 samples)"""
47 noise = np.random.normal(0, 5e-6, (6, self.buffer_size))
48
49 t = np.linspace(0, 1, self.buffer_size)
50 noise[1] += 2e-6 * np.sin(2 * np.pi * 10 * t)
51
52 return noise
53
54 def update_buffer(self, new_data):
55 """Buffer'ı kaydırmalı olarak günceller / Updates the buffer with new data by rolling"""
56 self.raw_buffer = np.roll(self.raw_buffer, -new_data.shape[1], axis=1)
57 self.raw_buffer[:, -new_data.shape[1]:] = new_data
58
59 def calculate_channel_contributions(self, features):
60 """Her elektrotun tahmindeki katkısını hesaplar / Calculates the contribution of each electrode to the prediction"""
61 contributions = np.zeros(6)
62 for i in range(6):
63 channel_weights = self.model.layers[0].get_weights()[0][i * 6:(i + 1) * 6]
64 contributions[i] = np.mean(np.abs(channel_weights))
65
66 return contributions / np.sum(contributions)
67
68 def update_plot(self, frame):
69 new_data = self.generate_synthetic_data()
70 self.update_buffer(new_data)
71
72 features = self.extract_features(self.raw_buffer)
73 scaled_features = self.scaler.transform([features])
74 probs = self.model.predict(scaled_features, verbose=0)[0]
75
76 contributions = self.calculate_channel_contributions(features)
77
78 self.update_eeg_plot()
79 self.update_topomap()
80 self.update_contributions(contributions)
81 self.update_probabilities(probs)
82
83 def update_eeg_plot(self):
84 self.ax1.clear()
85 for i in range(6):
86 offset = i * 20e-6
87 self.ax1.plot(self.raw_buffer[i] + offset, label=self.ch_names[i])
88 self.ax1.legend(loc='upper right')
89
90 def update_topomap(self):
91 self.ax2.clear()
92 info = mne.create_info(self.ch_names, self.fs, 'eeg')
93 evoked = mne.EvokedArray(self.raw_buffer.mean(axis=1, keepdims=True), info)
94 evoked.set_montage(self.montage)
95 mne.viz.plot_topomap(evoked.data[:, 0], evoked.info, axes=self.ax2, show=False)
96
97 def update_contributions(self, contributions):
98 self.ax3.clear()
99 self.ax3.barh(self.ch_names, contributions, color='skyblue')
100 for i, v in enumerate(contributions):
101 self.ax3.text(v, i, f"{v * 100:.1f}%", color='black')
102
103 def update_probabilities(self, probs):
104 emotions = ['Mutlu / Happy', 'Kızgın / Angry', 'Üzgün / Sad', 'Sakin / Calm']
105 self.ax4.clear()
106 bars = self.ax4.barh(emotions, probs, color=['green', 'red', 'blue', 'purple'])
107 for bar in bars:
108 width = bar.get_width()
109 self.ax4.text(width, bar.get_y() + 0.2, f"{width * 100:.1f}%", ha='left')
110
111 def extract_features(self, data):
112 """6 kanal için özellik çıkarımı / Feature extraction for 6 channels"""
113 features = []
114 for channel in data:
115 features.extend([
116 np.mean(channel),
117 np.std(channel),
118 np.ptp(channel),
119 np.sum(np.abs(np.diff(channel))),
120 np.median(channel),
121 np.percentile(np.abs(channel), 95)
122 ])
123 return np.array(features)
124
125 def start_monitoring(self):
126 anim = FuncAnimation(self.fig, self.update_plot, interval=100)
127 plt.show()
128
129
130if __name__ == "__main__":
131 monitor = EEGMonitor(
132 model_path='model/path/bai-6 Emotion.h5',
133 scaler_path='scaler/path/bai-6_scaler.save'
134 )
135 monitor.start_monitoring()1import numpy as np
2import matplotlib.pyplot as plt
3import mne
4from matplotlib.animation import FuncAnimation
5from tensorflow.keras.models import load_model
6import joblib
7import os
8
9
10class EEGMonitor:
11 def __init__(self, model_path, scaler_path, data_path):
12 self.model = load_model(model_path)
13 self.scaler = joblib.load(scaler_path)
14 self.data_path = data_path
15 self.ch_names = ['T7', 'C3', 'Cz', 'C4', 'T8', 'Pz']
16 self.fs = 1000 # Örnekleme frekansı / Sampling frequency
17 self.buffer_size = 1000 # 1 saniyelik buffer / 1 second buffer
18
19 self.raw_buffer = np.zeros((6, self.buffer_size))
20 self.feature_contributions = {ch: [] for ch in self.ch_names}
21
22 # Elektrot pozisyonları / Electrode positions (10-20 system)
23 self.montage = mne.channels.make_standard_montage('standard_1020')
24
25 self.fig = plt.figure(figsize=(15, 10))
26 self.setup_plots()
27
28 self.dataset = self.load_dataset(self.data_path)
29 self.current_index = 0
30
31 def setup_plots(self):
32 self.ax1 = self.fig.add_subplot(223)
33 self.ax1.set_title("Canlı EEG Sinyalleri / Live EEG Signals")
34 self.ax1.set_xlabel("Zaman (ms) / Time (ms)")
35 self.ax1.set_ylabel("Amplitüd (µV) / Amplitude (µV)")
36
37 self.ax2 = self.fig.add_subplot(221)
38 self.ax2.set_title("Elektrot Konumları / Electrode Locations")
39
40 self.ax3 = self.fig.add_subplot(224)
41 self.ax3.set_title("Elektrot Katkı Oranları / Electrode Contribution Ratios")
42 self.ax3.set_ylim(0, 1)
43
44 self.ax4 = self.fig.add_subplot(222)
45 self.ax4.set_title("Duygu Tahmin Olasılıkları / Emotion Prediction Probabilities")
46 self.ax4.set_ylim(0, 1)
47
48 plt.tight_layout()
49
50 def load_dataset(self, path):
51 """Desteklenen veri formatları: .npy (numpy), .csv / Supported data formats: .npy (numpy), .csv"""
52 if not os.path.exists(path):
53 raise FileNotFoundError(f"Veri seti bulunamadı / Not found dataset: {path}")
54
55 if path.endswith(".npy"):
56 data = np.load(path)
57 elif path.endswith(".csv"):
58 data = np.loadtxt(path, delimiter=',')
59 else:
60 raise ValueError("Desteklenmeyen dosya formatı. Yalnızca .npy veya .csv kullanılabilir. / Unsupported file format. Only .npy or .csv can be used.")
61
62 # Transpose gerekebilir: (n_channels, n_samples) / Transpose may be needed: (n_channels, n_samples)
63 if data.shape[0] != 6:
64 data = data.T
65 return data
66
67 def get_next_chunk(self):
68 """Veri setinden buffer_size uzunluğunda bir parça alır / Gets a chunk of length buffer_size from the dataset"""
69 if self.current_index + self.buffer_size >= self.dataset.shape[1]:
70 self.current_index = 0
71 chunk = self.dataset[:, self.current_index:self.current_index + self.buffer_size]
72 self.current_index += self.buffer_size
73 return chunk
74
75 def update_buffer(self, new_data):
76 self.raw_buffer = np.roll(self.raw_buffer, -new_data.shape[1], axis=1)
77 self.raw_buffer[:, -new_data.shape[1]:] = new_data
78
79 def calculate_channel_contributions(self, features):
80 contributions = np.zeros(6)
81 for i in range(6):
82 channel_weights = self.model.layers[0].get_weights()[0][i * 6:(i + 1) * 6]
83 contributions[i] = np.mean(np.abs(channel_weights))
84 return contributions / np.sum(contributions)
85
86 def update_plot(self, frame):
87 new_data = self.get_next_chunk()
88 self.update_buffer(new_data)
89
90 features = self.extract_features(self.raw_buffer)
91 scaled_features = self.scaler.transform([features])
92 probs = self.model.predict(scaled_features, verbose=0)[0]
93
94 contributions = self.calculate_channel_contributions(features)
95
96 self.update_eeg_plot()
97 self.update_topomap()
98 self.update_contributions(contributions)
99 self.update_probabilities(probs)
100
101 def update_eeg_plot(self):
102 self.ax1.clear()
103 for i in range(6):
104 offset = i * 20e-6
105 self.ax1.plot(self.raw_buffer[i] + offset, label=self.ch_names[i])
106 self.ax1.legend(loc='upper right')
107
108 def update_topomap(self):
109 self.ax2.clear()
110 info = mne.create_info(self.ch_names, self.fs, 'eeg')
111 evoked = mne.EvokedArray(self.raw_buffer.mean(axis=1, keepdims=True), info)
112 evoked.set_montage(self.montage)
113 mne.viz.plot_topomap(evoked.data[:, 0], evoked.info, axes=self.ax2, show=False)
114
115 def update_contributions(self, contributions):
116 self.ax3.clear()
117 self.ax3.barh(self.ch_names, contributions, color='skyblue')
118 for i, v in enumerate(contributions):
119 self.ax3.text(v, i, f"{v * 100:.1f}%", color='black')
120
121 def update_probabilities(self, probs):
122 emotions = ['Mutlu / Happy', 'Kızgın / Angry', 'Üzgün / Sad', 'Sakin / Calm']
123 self.ax4.clear()
124 bars = self.ax4.barh(emotions, probs, color=['green', 'red', 'blue', 'purple'])
125 for bar in bars:
126 width = bar.get_width()
127 self.ax4.text(width, bar.get_y() + 0.2, f"{width * 100:.1f}%", ha='left')
128
129 def extract_features(self, data):
130 features = []
131 for channel in data:
132 features.extend([
133 np.mean(channel),
134 np.std(channel),
135 np.ptp(channel),
136 np.sum(np.abs(np.diff(channel))),
137 np.median(channel),
138 np.percentile(np.abs(channel), 95)
139 ])
140 return np.array(features)
141
142 def start_monitoring(self):
143 anim = FuncAnimation(self.fig, self.update_plot, interval=1000)
144 plt.show()
145
146
147if __name__ == "__main__":
148 monitor = EEGMonitor(
149 model_path="model/path/bai-6 Emotion.h5",
150 scaler_path="scaler/path/bai-6_scaler.save",
151 data_path="data/path/npy/or/csv"
152 )
153 monitor.start_monitoring()| Layer (type) | Output Shape | Param # |
|---|---|---|
| dense_4 (Dense) | (None, 128) | 4,736 |
| batch_normalization_2 | (None, 128) | 512 |
| dropout_3 (Dropout) | (None, 128) | 0 |
| dense_5 (Dense) | (None, 64) | 8,256 |
| batch_normalization_3 | (None, 64) | 256 |
| dropout_4 (Dropout) | (None, 64) | 0 |
| dense_6 (Dense) | (None, 32) | 2,080 |
| dropout_5 (Dropout) | (None, 32) | 0 |
| dense_7 (Dense) | (None, 4) | 132 |
1import numpy as np
2import joblib
3import time
4import matplotlib.pyplot as plt
5from matplotlib.animation import FuncAnimation
6from tensorflow.keras.models import load_model
7from datetime import datetime
8import mne
9import warnings
10
11warnings.filterwarnings('ignore')
12
13
14class EEGEmotionMonitorOptimized:
15 def __init__(self, model_path, scaler_path, selector_path=None, pca_path=None):
16 self.emotion_labels = {
17 0: "Mutlu (Happy)",
18 1: "Kızgın (Angry)",
19 2: "Üzgün (Sad)",
20 3: "Sakin (Calm)"
21 }
22
23 self.emotion_colors = ['#FFD700', '#FF4444', '#4169E1', '#32CD32']
24
25 # Kanal isimleri ve parametreler / Channel names and parameters
26 self.ch_names = ['T7', 'C3', 'Cz', 'C4', 'T8', 'Pz']
27 self.fs = 128 # Örnekleme hızı / Sampling rate
28 self.buffer_size = 640
29 self.update_interval = 200
30
31 # Buffer'ları başlat / Initialize buffers
32 self.raw_buffer = np.zeros((6, self.buffer_size))
33 self.prediction_history = []
34 self.confidence_history = []
35 self.time_history = []
36 self.max_history = 30
37
38 # Performance metrics tracking
39 self.performance_metrics = {
40 'total_predictions': 0,
41 'high_confidence_predictions': 0, # >0.8 confidence
42 'low_confidence_predictions': 0, # <0.5 confidence
43 'prediction_times': [], # Processing time per prediction
44 'emotion_transitions': 0, # Count of emotion changes
45 'stability_score': 0.0, # How stable predictions are
46 'average_confidence': 0.0,
47 'confidence_trend': [], # Last 10 confidence values for trend analysis
48 'processing_fps': 0.0, # Processing speed
49 'last_prediction': None
50 }
51
52 self.metrics_text = ""
53 self.start_time = None
54
55 try:
56 self.model = load_model(model_path)
57 self.scaler = joblib.load(scaler_path)
58
59 self.selector = None
60 self.pca = None
61
62 if selector_path:
63 try:
64 self.selector = joblib.load(selector_path)
65 print("Feature selector loaded")
66 except:
67 print("Feature selector not found, using raw features")
68
69 if pca_path:
70 try:
71 self.pca = joblib.load(pca_path)
72 print("PCA reducer loaded")
73 except:
74 print("PCA reducer not found, skipping dimensionality reduction")
75
76 print("Model and preprocessors successfully loaded!")
77 print(f"Model input shape: {self.model.input_shape}")
78 print(f"Output classes: {len(self.emotion_labels)}")
79
80 # Elektrot pozisyonları (10-20 sistemi) / Electrode positions (10-20 system)
81 self.montage = mne.channels.make_standard_montage('standard_1020')
82
83 except Exception as e:
84 print(f"Model/preprocessor loading error: {e}")
85 raise
86
87 self.fig = plt.figure(figsize=(14, 8))
88 self.fig.suptitle('EEG Duygu Tanıma Sistemi / EEG Emotion Analysis System', fontsize=16, fontweight='bold')
89 self.setup_plots()
90
91 self.animation = None
92 self.is_running = False
93
94 def setup_plots(self):
95 """4 panelli görselleştirme arayüzünü hazırla (with performance metrics) / Setup 4-panel visualization interface (with performance metrics)"""
96
97 self.ax1 = self.fig.add_subplot(221)
98 self.ax1.set_title("Live EEG Signals", fontsize=10)
99 self.ax1.set_xlabel("Time (samples)", fontsize=9)
100 self.ax1.set_ylabel("Amplitude (µV)", fontsize=9)
101 self.ax1.grid(True, alpha=0.3)
102
103 self.ax2 = self.fig.add_subplot(222)
104 self.ax2.set_title("Emotion Probabilities", fontsize=10)
105 self.ax2.set_xlim(0, 1)
106
107 self.ax3 = self.fig.add_subplot(223)
108 self.ax3.set_title("Performance Metrics & Confidence Trend", fontsize=10)
109
110 self.ax4 = self.fig.add_subplot(224)
111 self.ax4.set_title("Electrode Contributions", fontsize=10)
112 self.ax4.set_xlim(0, 1)
113
114 plt.tight_layout(pad=1.0)
115
116 def generate_realistic_eeg_signal(self, emotion_bias=None):
117 noise = np.random.normal(0, 3e-6, (6, self.buffer_size))
118
119 t = np.linspace(0, self.buffer_size/self.fs, self.buffer_size)
120
121 alpha_freq = np.random.uniform(8, 12) # Alpha dominant
122 beta_freq = np.random.uniform(15, 25) # Beta
123
124 for ch in range(6):
125 if emotion_bias == 0: # Happy - higher beta
126 beta_amp = np.random.uniform(4e-6, 6e-6)
127 alpha_amp = np.random.uniform(2e-6, 3e-6)
128 elif emotion_bias == 1: # Angry - very high beta
129 beta_amp = np.random.uniform(5e-6, 7e-6)
130 alpha_amp = np.random.uniform(1e-6, 2e-6)
131 elif emotion_bias == 2: # Sad - lower activity
132 beta_amp = np.random.uniform(1e-6, 2e-6)
133 alpha_amp = np.random.uniform(3e-6, 5e-6)
134 elif emotion_bias == 3: # Calm - high alpha
135 beta_amp = np.random.uniform(1e-6, 3e-6)
136 alpha_amp = np.random.uniform(4e-6, 6e-6)
137 else: # Random
138 beta_amp = np.random.uniform(2e-6, 4e-6)
139 alpha_amp = np.random.uniform(2e-6, 4e-6)
140
141 noise[ch] += alpha_amp * np.sin(2 * np.pi * alpha_freq * t + np.random.random() * 2 * np.pi)
142 noise[ch] += beta_amp * np.sin(2 * np.pi * beta_freq * t + np.random.random() * 2 * np.pi)
143
144 return noise.astype(np.float32)
145
146 def update_buffer(self, new_data):
147 samples_to_add = min(new_data.shape[1], self.buffer_size // 4)
148 self.raw_buffer = np.roll(self.raw_buffer, -samples_to_add, axis=1)
149 self.raw_buffer[:, -samples_to_add:] = new_data[:, :samples_to_add]
150
151 def extract_lightweight_features(self, signal_data):
152 features = []
153
154 for channel_data in signal_data:
155 time_features = [
156 np.mean(channel_data),
157 np.std(channel_data),
158 np.ptp(channel_data),
159 np.median(channel_data),
160 np.mean(np.abs(channel_data)),
161 np.sqrt(np.mean(channel_data**2))
162 ]
163
164 try:
165 fft_vals = np.abs(np.fft.rfft(channel_data[::4]))
166 freqs = np.fft.rfftfreq(len(channel_data)//4, 4/self.fs)
167
168 delta_power = np.sum(fft_vals[(freqs >= 0.5) & (freqs <= 4)])
169 theta_power = np.sum(fft_vals[(freqs >= 4) & (freqs <= 8)])
170 alpha_power = np.sum(fft_vals[(freqs >= 8) & (freqs <= 13)])
171 beta_power = np.sum(fft_vals[(freqs >= 13) & (freqs <= 30)])
172 total_power = np.sum(fft_vals) + 1e-10
173
174 freq_features = [
175 delta_power / total_power,
176 theta_power / total_power,
177 alpha_power / total_power,
178 beta_power / total_power
179 ]
180 except:
181 freq_features = [0.25, 0.25, 0.25, 0.25]
182
183 nonlinear_features = [
184 np.std(np.diff(channel_data)) / (np.std(channel_data) + 1e-10),
185 np.mean(np.abs(np.diff(channel_data)))
186 ]
187
188 channel_features = time_features + freq_features + nonlinear_features
189 features.extend(channel_features)
190
191 return np.array(features, dtype=np.float32)
192
193 def calculate_channel_contributions(self, signal_data):
194 contributions = np.zeros(6)
195 for i in range(6):
196 contributions[i] = np.sqrt(np.mean(signal_data[i]**2))
197
198 total = np.sum(contributions) + 1e-10
199 return contributions / total
200
201 def update_performance_metrics(self, predicted_class, confidence, processing_time):
202 metrics = self.performance_metrics
203
204 metrics['total_predictions'] += 1
205
206 if confidence > 0.8:
207 metrics['high_confidence_predictions'] += 1
208 elif confidence < 0.5:
209 metrics['low_confidence_predictions'] += 1
210
211 metrics['prediction_times'].append(processing_time)
212 if len(metrics['prediction_times']) > 50:
213 metrics['prediction_times'].pop(0)
214
215 if metrics['prediction_times']:
216 avg_time = np.mean(metrics['prediction_times'])
217 metrics['processing_fps'] = 1.0 / max(avg_time, 0.001)
218
219 if metrics['last_prediction'] is not None and metrics['last_prediction'] != predicted_class:
220 metrics['emotion_transitions'] += 1
221 metrics['last_prediction'] = predicted_class
222
223 metrics['confidence_trend'].append(float(confidence))
224 if len(metrics['confidence_trend']) > 10:
225 metrics['confidence_trend'].pop(0)
226
227 if self.confidence_history:
228 metrics['average_confidence'] = np.mean(self.confidence_history)
229
230 if metrics['total_predictions'] > 1:
231 transition_rate = metrics['emotion_transitions'] / metrics['total_predictions']
232 metrics['stability_score'] = max(0, 1.0 - transition_rate)
233
234 def update_plot(self, frame):
235 if not self.is_running:
236 return
237
238 start_time = time.time()
239
240 if frame % 2 == 0:
241 if np.random.random() < 0.2:
242 emotion_bias = np.random.randint(0, 4)
243 else:
244 emotion_bias = None
245
246 new_samples = self.buffer_size // 8
247 new_data = self.generate_realistic_eeg_signal(emotion_bias)[:, :new_samples]
248 self.update_buffer(new_data)
249
250 prediction_start = time.time()
251 features = self.extract_lightweight_features(self.raw_buffer)
252
253 try:
254 scaled_features = self.scaler.transform([features])
255
256 if self.selector is not None:
257 scaled_features = self.selector.transform(scaled_features)
258
259 if self.pca is not None:
260 scaled_features = self.pca.transform(scaled_features)
261
262 probs = self.model.predict(scaled_features, verbose=0)[0]
263 predicted_class = np.argmax(probs)
264 confidence = np.max(probs)
265
266 except Exception as e:
267 print(f"Prediction error: {e}")
268 probs = np.array([0.25, 0.25, 0.25, 0.25])
269 predicted_class = 0
270 confidence = 0.25
271
272 prediction_time = time.time() - prediction_start
273
274 self.update_performance_metrics(predicted_class, confidence, prediction_time)
275
276 self.prediction_history.append(predicted_class)
277 self.confidence_history.append(confidence)
278 self.time_history.append(datetime.now())
279
280 if len(self.prediction_history) > self.max_history:
281 self.prediction_history.pop(0)
282 self.confidence_history.pop(0)
283 self.time_history.pop(0)
284
285 contributions = self.calculate_channel_contributions(self.raw_buffer)
286
287 self.update_eeg_plot()
288 self.update_probabilities(probs)
289 self.update_performance_plot()
290 self.update_contributions(contributions)
291
292 emotion_name = self.emotion_labels[predicted_class]
293 metrics = self.performance_metrics
294 elapsed_time = time.time() - self.start_time if self.start_time else 0
295
296 print(f"\r{datetime.now().strftime('%H:%M:%S')} | "
297 f"Emotion: {emotion_name} | "
298 f"Conf: {confidence:.3f} | "
299 f"FPS: {metrics['processing_fps']:.1f} | "
300 f"Stab: {metrics['stability_score']:.2f} | "
301 f"Total: {metrics['total_predictions']} | "
302 f"Time: {elapsed_time:.0f}s", end='')
303
304 def update_eeg_plot(self):
305 self.ax1.clear()
306
307 colors = plt.cm.tab10(np.linspace(0, 1, 6))
308 display_samples = min(300, self.buffer_size) # Show fewer samples for performance
309
310 for i in range(6):
311 offset = i * 20e-6
312 signal = self.raw_buffer[i, -display_samples:] + offset
313 self.ax1.plot(signal, label=self.ch_names[i],
314 color=colors[i], linewidth=1.0, alpha=0.8)
315
316 self.ax1.set_title("Live EEG Signals", fontsize=12)
317 self.ax1.set_xlabel("Time (samples)")
318 self.ax1.set_ylabel("Amplitude (µV)")
319 self.ax1.legend(loc='upper right', fontsize=8)
320 self.ax1.grid(True, alpha=0.3)
321
322 def update_performance_plot(self):
323 self.ax3.clear()
324
325 metrics = self.performance_metrics
326
327 if metrics['total_predictions'] > 0:
328 high_conf_pct = (metrics['high_confidence_predictions'] / metrics['total_predictions']) * 100
329 low_conf_pct = (metrics['low_confidence_predictions'] / metrics['total_predictions']) * 100
330
331 metrics_text = f"""PERFORMANCE METRICS
332
333Total Predictions: {metrics['total_predictions']}
334Average Confidence: {metrics['average_confidence']:.3f}
335High Confidence (>0.8): {high_conf_pct:.1f}%
336Low Confidence (<0.5): {low_conf_pct:.1f}%
337
338Processing Speed: {metrics['processing_fps']:.1f} FPS
339Stability Score: {metrics['stability_score']:.3f}
340Emotion Transitions: {metrics['emotion_transitions']}
341
342Model Accuracy: {high_conf_pct:.1f}%
343Response Time: {np.mean(metrics['prediction_times'])*1000:.1f}ms"""
344
345 self.ax3.text(0.02, 0.98, metrics_text,
346 transform=self.ax3.transAxes,
347 fontsize=8, verticalalignment='top',
348 fontfamily='monospace',
349 bbox=dict(boxstyle="round,pad=0.3", facecolor="lightblue", alpha=0.7))
350
351 if len(metrics['confidence_trend']) > 1:
352 trend_x = np.arange(len(metrics['confidence_trend']))
353 trend_data = np.array(metrics['confidence_trend'], dtype=np.float64)
354 self.ax3.plot(trend_x + 0.6, trend_data * 0.4 + 0.1,
355 'g-o', markersize=3, linewidth=2, label='Confidence Trend')
356
357 # Add trend analysis
358 if len(metrics['confidence_trend']) > 3:
359 try:
360 x_data = np.array(range(len(trend_data[-5:])), dtype=np.float64)
361 y_data = np.array(trend_data[-5:], dtype=np.float64)
362 recent_trend = np.polyfit(x_data, y_data, 1)[0]
363 trend_direction = "↗" if recent_trend > 0.01 else "↘" if recent_trend < -0.01 else "→"
364 self.ax3.text(0.7, 0.9, f"Trend: {trend_direction}",
365 transform=self.ax3.transAxes, fontsize=10, fontweight='bold')
366 except:
367 # Fallback if polyfit fails
368 self.ax3.text(0.7, 0.9, f"Trend: →",
369 transform=self.ax3.transAxes, fontsize=10, fontweight='bold')
370
371 self.ax3.set_xlim(0, 1)
372 self.ax3.set_ylim(0, 1)
373 self.ax3.set_title("Performance Metrics & Confidence Trend", fontsize=10)
374
375 if metrics['average_confidence'] > 0.8:
376 title_color = 'green'
377 elif metrics['average_confidence'] > 0.6:
378 title_color = 'orange'
379 else:
380 title_color = 'red'
381 self.ax3.title.set_color(title_color)
382
383 def update_contributions(self, contributions):
384 self.ax4.clear()
385
386 colors = plt.cm.viridis(contributions)
387 bars = self.ax4.barh(self.ch_names, contributions, color=colors)
388
389 for i, (bar, v) in enumerate(zip(bars, contributions)):
390 if v > 0.05:
391 self.ax4.text(v + 0.02, i, f"{v*100:.1f}%",
392 va='center', fontsize=9)
393
394 self.ax4.set_title("Electrode Contributions", fontsize=10)
395 self.ax4.set_xlabel("Contribution Rate", fontsize=9)
396 self.ax4.set_xlim(0, 0.6)
397 self.ax4.grid(True, alpha=0.3, axis='x')
398
399 def update_probabilities(self, probs):
400 self.ax2.clear()
401
402 emotions = [self.emotion_labels[i] for i in range(4)]
403 bars = self.ax2.barh(emotions, probs, color=self.emotion_colors)
404
405 max_idx = np.argmax(probs)
406 bars[max_idx].set_edgecolor('black')
407 bars[max_idx].set_linewidth(2)
408
409 for bar, prob in zip(bars, probs):
410 width = bar.get_width()
411 if width > 0.05:
412 self.ax2.text(width + 0.02, bar.get_y() + bar.get_height()/2,
413 f"{width*100:.1f}%", ha='left', va='center',
414 fontsize=9, fontweight='bold')
415
416 self.ax2.set_title("Emotion Probabilities", fontsize=12)
417 self.ax2.set_xlabel("Probability")
418 self.ax2.set_xlim(0, 1)
419 self.ax2.grid(True, alpha=0.3, axis='x')
420
421 current_emotion = emotions[max_idx]
422 confidence = probs[max_idx]
423 self.ax2.text(0.5, 1.05, f"Current: {current_emotion} ({confidence*100:.1f}%)",
424 transform=self.ax2.transAxes, ha='center',
425 fontsize=10, fontweight='bold', color=self.emotion_colors[max_idx])
426
427 def start_monitoring(self):
428 print("\n" + "="*60)
429 print(" OPTIMIZED EEG EMOTION RECOGNITION MONITOR")
430 print("="*60)
431 print("\nPress 'X' to close the window...")
432 print("Real-time performance metrics will be displayed")
433 print("-"*60)
434
435 self.is_running = True
436 self.start_time = time.time()
437
438 self.animation = FuncAnimation(
439 self.fig,
440 self.update_plot,
441 interval=self.update_interval,
442 blit=False,
443 cache_frame_data=False
444 )
445
446 plt.show()
447
448 self.is_running = False
449 print("\n\nMonitoring stopped.")
450
451 if self.prediction_history:
452 self.print_summary_statistics()
453
454 def print_summary_statistics(self):
455 print("\n" + "="*80)
456 print(" DETAILED PERFORMANCE & STATISTICS SUMMARY")
457 print("="*80)
458
459 if not self.prediction_history:
460 print("No data collected.")
461 return
462
463 metrics = self.performance_metrics
464 total_time = time.time() - self.start_time if self.start_time else 0
465
466 print("\n📊 PERFORMANCE METRICS:")
467 print(f" Total Predictions: {metrics['total_predictions']}")
468 print(f" Total Runtime: {total_time:.1f} seconds")
469 print(f" Average Processing Speed: {metrics['processing_fps']:.1f} FPS")
470 print(f" Average Response Time: {np.mean(metrics['prediction_times'])*1000:.1f}ms")
471 print(f" Model Stability Score: {metrics['stability_score']:.3f} (0-1, higher=better)")
472 print(f" Emotion Transitions: {metrics['emotion_transitions']}")
473
474 print(f"\n🎯 CONFIDENCE ANALYSIS:")
475 total = len(self.prediction_history)
476 high_conf_count = metrics['high_confidence_predictions']
477 low_conf_count = metrics['low_confidence_predictions']
478 medium_conf_count = max(0, total - high_conf_count - low_conf_count)
479
480 print(f" Average Confidence: {metrics['average_confidence']:.3f}")
481 print(f" Confidence Std Dev: {np.std(self.confidence_history):.3f}")
482 print(f" High Confidence (>0.8): {high_conf_count} ({high_conf_count/total*100:.1f}%)")
483 print(f" Medium Confidence (0.5-0.8): {medium_conf_count} ({medium_conf_count/total*100:.1f}%)")
484 print(f" Low Confidence (<0.5): {low_conf_count} ({low_conf_count/total*100:.1f}%)")
485
486 accuracy_score = high_conf_count / total * 100 if total > 0 else 0
487 print(f"\n🏆 MODEL QUALITY ASSESSMENT:")
488 print(f" Estimated Accuracy: {accuracy_score:.1f}% (based on high confidence predictions)")
489
490 if accuracy_score >= 80:
491 quality = "EXCELLENT 🌟"
492 elif accuracy_score >= 70:
493 quality = "GOOD ✅"
494 elif accuracy_score >= 60:
495 quality = "FAIR ⚠️"
496 else:
497 quality = "POOR ❌"
498 print(f" Model Quality Rating: {quality}")
499
500 emotion_counts = {i: 0 for i in range(4)}
501 for pred in self.prediction_history:
502 emotion_counts[pred] += 1
503
504 print(f"\n😊 EMOTION DISTRIBUTION:")
505 for emotion_id, count in emotion_counts.items():
506 percentage = (count / total) * 100
507 bar = "█" * int(percentage / 5)
508 print(f" {self.emotion_labels[emotion_id]:<15}: {count:>3} ({percentage:>5.1f}%) {bar}")
509
510 dominant_emotion = max(emotion_counts, key=emotion_counts.get)
511 dominant_percentage = emotion_counts[dominant_emotion] / total * 100
512 print(f"\n Dominant Emotion: {self.emotion_labels[dominant_emotion]} ({dominant_percentage:.1f}%)")
513
514 if len(metrics['confidence_trend']) > 3:
515 try:
516 x_data = np.array(range(len(metrics['confidence_trend'])), dtype=np.float64)
517 y_data = np.array(metrics['confidence_trend'], dtype=np.float64)
518 trend_slope = np.polyfit(x_data, y_data, 1)[0]
519 print(f"\n📈 TREND ANALYSIS:")
520 if trend_slope > 0.01:
521 trend_desc = "IMPROVING ↗"
522 elif trend_slope < -0.01:
523 trend_desc = "DECLINING ↘"
524 else:
525 trend_desc = "STABLE →"
526 print(f" Recent Confidence Trend: {trend_desc} (slope: {trend_slope:.4f})")
527 except Exception as e:
528 print(f"\n📈 TREND ANALYSIS:")
529 print(f" Recent Confidence Trend: STABLE → (analysis unavailable)")
530
531 print(f"\n💡 RECOMMENDATIONS:")
532 if accuracy_score < 70:
533 print(" • Consider retraining the model with more data")
534 print(" • Check data quality and preprocessing steps")
535 if metrics['stability_score'] < 0.7:
536 print(" • Model predictions are unstable - review signal quality")
537 if metrics['processing_fps'] < 5:
538 print(" • Processing speed is slow - consider model optimization")
539 if accuracy_score >= 80 and metrics['stability_score'] >= 0.8:
540 print(" • Model performance is excellent! ✨")
541
542 print("\n" + "="*80)
543
544
545def main():
546 model_path = 'path/to/bai-6 EmotionOptimized.h5'
547 scaler_path = 'path/to/bai-6 ScalerOptimized.pkl'
548 selector_path = 'path/to/bai-6_feature_selector_opt.pkl'
549 pca_path = 'path/to/bai-6_pca_reducer_opt.pkl'
550
551 try:
552 monitor = EEGEmotionMonitorOptimized(
553 model_path, scaler_path, selector_path, pca_path
554 )
555
556 monitor.start_monitoring()
557
558 except FileNotFoundError as e:
559 print(f"Model or preprocessor file not found: {e}")
560 print("Please ensure the model has been trained and saved.")
561 print("Available fallback: Using basic model without feature selection/PCA")
562
563 try:
564 basic_model_path = 'path/to/bai-6 EmotionOptimized.h5'
565 basic_scaler_path = 'path/to/bai-6 ScalerOptimized.pkl'
566
567 monitor = EEGEmotionMonitorOptimized(basic_model_path, basic_scaler_path)
568 monitor.start_monitoring()
569
570 except Exception as e2:
571 print(f"Fallback also failed: {e2}")
572
573 except Exception as e:
574 print(f"Error: {e}")
575 import traceback
576 traceback.print_exc()
577
578
579if __name__ == "__main__":
580 main()