eyupipler commited on
Commit
77d2492
·
verified ·
1 Parent(s): 371e87d

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +612 -1
README.md CHANGED
@@ -66,7 +66,7 @@ Fully compatible with Neuramax-6 Gen1.
66
  -------------
67
 
68
  <details>
69
- <summary>bai-6 Emotion v1</summary>
70
 
71
  # bai-6 Emotion v1 Yapısı / Structure
72
 
@@ -410,6 +410,617 @@ if __name__ == "__main__":
410
 
411
  </details>
412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
  -------------
414
  ## Lisans/License
415
  CC-BY-NC-SA-4.0
 
66
  -------------
67
 
68
  <details>
69
+ <summary><h3>bai-6 Emotion v1</h3></summary>
70
 
71
  # bai-6 Emotion v1 Yapısı / Structure
72
 
 
410
 
411
  </details>
412
 
413
+ <details>
414
+ <summary><h3>bai-6 Emotion v2</h3></summary>
415
+
416
+ # bai-6 Emotion v2 Yapısı / Structure
417
+
418
+ |Layer (type) | Output Shape | Param # |
419
+ | --- | --- | --- |
420
+ | dense_4 (Dense) | (None, 128) | 4,736 |
421
+ | batch_normalization_2 | (None, 128) | 512 |
422
+ | dropout_3 (Dropout) | (None, 128) | 0 |
423
+ | dense_5 (Dense) | (None, 64) | 8,256 |
424
+ | batch_normalization_3 | (None, 64) | 256 |
425
+ | dropout_4 (Dropout) | (None, 64) | 0 |
426
+ | dense_6 (Dense) | (None, 32) | 2,080 |
427
+ | dropout_5 (Dropout) | (None, 32) | 0 |
428
+ | dense_7 (Dense) | (None, 4) | 132 |
429
+
430
+ ### Total params: 15,972 (62.39 KB)
431
+
432
+ **Trainable params: 15,588 (60.89 KB)**
433
+
434
+ **Non-trainable params: 384 (1.50 KB)**
435
+
436
+ # Kullanım / Usage
437
+
438
+ ## Sentetik Veri ile / With Synthetic Data
439
+ ```python
440
+ import numpy as np
441
+ import joblib
442
+ import time
443
+ import matplotlib.pyplot as plt
444
+ from matplotlib.animation import FuncAnimation
445
+ from tensorflow.keras.models import load_model
446
+ from datetime import datetime
447
+ import mne
448
+ import warnings
449
+
450
+ warnings.filterwarnings('ignore')
451
+
452
+
453
+ class EEGEmotionMonitorOptimized:
454
+ def __init__(self, model_path, scaler_path, selector_path=None, pca_path=None):
455
+ self.emotion_labels = {
456
+ 0: "Mutlu (Happy)",
457
+ 1: "Kızgın (Angry)",
458
+ 2: "Üzgün (Sad)",
459
+ 3: "Sakin (Calm)"
460
+ }
461
+
462
+ self.emotion_colors = ['#FFD700', '#FF4444', '#4169E1', '#32CD32']
463
+
464
+ # Kanal isimleri ve parametreler / Channel names and parameters
465
+ self.ch_names = ['T7', 'C3', 'Cz', 'C4', 'T8', 'Pz']
466
+ self.fs = 128 # Örnekleme hızı / Sampling rate
467
+ self.buffer_size = 640
468
+ self.update_interval = 200
469
+
470
+ # Buffer'ları başlat / Initialize buffers
471
+ self.raw_buffer = np.zeros((6, self.buffer_size))
472
+ self.prediction_history = []
473
+ self.confidence_history = []
474
+ self.time_history = []
475
+ self.max_history = 30
476
+
477
+ # Performance metrics tracking
478
+ self.performance_metrics = {
479
+ 'total_predictions': 0,
480
+ 'high_confidence_predictions': 0, # >0.8 confidence
481
+ 'low_confidence_predictions': 0, # <0.5 confidence
482
+ 'prediction_times': [], # Processing time per prediction
483
+ 'emotion_transitions': 0, # Count of emotion changes
484
+ 'stability_score': 0.0, # How stable predictions are
485
+ 'average_confidence': 0.0,
486
+ 'confidence_trend': [], # Last 10 confidence values for trend analysis
487
+ 'processing_fps': 0.0, # Processing speed
488
+ 'last_prediction': None
489
+ }
490
+
491
+ self.metrics_text = ""
492
+ self.start_time = None
493
+
494
+ try:
495
+ self.model = load_model(model_path)
496
+ self.scaler = joblib.load(scaler_path)
497
+
498
+ self.selector = None
499
+ self.pca = None
500
+
501
+ if selector_path:
502
+ try:
503
+ self.selector = joblib.load(selector_path)
504
+ print("Feature selector loaded")
505
+ except:
506
+ print("Feature selector not found, using raw features")
507
+
508
+ if pca_path:
509
+ try:
510
+ self.pca = joblib.load(pca_path)
511
+ print("PCA reducer loaded")
512
+ except:
513
+ print("PCA reducer not found, skipping dimensionality reduction")
514
+
515
+ print("Model and preprocessors successfully loaded!")
516
+ print(f"Model input shape: {self.model.input_shape}")
517
+ print(f"Output classes: {len(self.emotion_labels)}")
518
+
519
+ # Elektrot pozisyonları (10-20 sistemi) / Electrode positions (10-20 system)
520
+ self.montage = mne.channels.make_standard_montage('standard_1020')
521
+
522
+ except Exception as e:
523
+ print(f"Model/preprocessor loading error: {e}")
524
+ raise
525
+
526
+ self.fig = plt.figure(figsize=(14, 8))
527
+ self.fig.suptitle('EEG Duygu Tanıma Sistemi / EEG Emotion Analysis System', fontsize=16, fontweight='bold')
528
+ self.setup_plots()
529
+
530
+ self.animation = None
531
+ self.is_running = False
532
+
533
+ def setup_plots(self):
534
+ """4 panelli görselleştirme arayüzünü hazırla (with performance metrics) / Setup 4-panel visualization interface (with performance metrics)"""
535
+
536
+ self.ax1 = self.fig.add_subplot(221)
537
+ self.ax1.set_title("Live EEG Signals", fontsize=10)
538
+ self.ax1.set_xlabel("Time (samples)", fontsize=9)
539
+ self.ax1.set_ylabel("Amplitude (µV)", fontsize=9)
540
+ self.ax1.grid(True, alpha=0.3)
541
+
542
+ self.ax2 = self.fig.add_subplot(222)
543
+ self.ax2.set_title("Emotion Probabilities", fontsize=10)
544
+ self.ax2.set_xlim(0, 1)
545
+
546
+ self.ax3 = self.fig.add_subplot(223)
547
+ self.ax3.set_title("Performance Metrics & Confidence Trend", fontsize=10)
548
+
549
+ self.ax4 = self.fig.add_subplot(224)
550
+ self.ax4.set_title("Electrode Contributions", fontsize=10)
551
+ self.ax4.set_xlim(0, 1)
552
+
553
+ plt.tight_layout(pad=1.0)
554
+
555
+ def generate_realistic_eeg_signal(self, emotion_bias=None):
556
+ noise = np.random.normal(0, 3e-6, (6, self.buffer_size))
557
+
558
+ t = np.linspace(0, self.buffer_size/self.fs, self.buffer_size)
559
+
560
+ alpha_freq = np.random.uniform(8, 12) # Alpha dominant
561
+ beta_freq = np.random.uniform(15, 25) # Beta
562
+
563
+ for ch in range(6):
564
+ if emotion_bias == 0: # Happy - higher beta
565
+ beta_amp = np.random.uniform(4e-6, 6e-6)
566
+ alpha_amp = np.random.uniform(2e-6, 3e-6)
567
+ elif emotion_bias == 1: # Angry - very high beta
568
+ beta_amp = np.random.uniform(5e-6, 7e-6)
569
+ alpha_amp = np.random.uniform(1e-6, 2e-6)
570
+ elif emotion_bias == 2: # Sad - lower activity
571
+ beta_amp = np.random.uniform(1e-6, 2e-6)
572
+ alpha_amp = np.random.uniform(3e-6, 5e-6)
573
+ elif emotion_bias == 3: # Calm - high alpha
574
+ beta_amp = np.random.uniform(1e-6, 3e-6)
575
+ alpha_amp = np.random.uniform(4e-6, 6e-6)
576
+ else: # Random
577
+ beta_amp = np.random.uniform(2e-6, 4e-6)
578
+ alpha_amp = np.random.uniform(2e-6, 4e-6)
579
+
580
+ noise[ch] += alpha_amp * np.sin(2 * np.pi * alpha_freq * t + np.random.random() * 2 * np.pi)
581
+ noise[ch] += beta_amp * np.sin(2 * np.pi * beta_freq * t + np.random.random() * 2 * np.pi)
582
+
583
+ return noise.astype(np.float32)
584
+
585
+ def update_buffer(self, new_data):
586
+ samples_to_add = min(new_data.shape[1], self.buffer_size // 4)
587
+ self.raw_buffer = np.roll(self.raw_buffer, -samples_to_add, axis=1)
588
+ self.raw_buffer[:, -samples_to_add:] = new_data[:, :samples_to_add]
589
+
590
+ def extract_lightweight_features(self, signal_data):
591
+ features = []
592
+
593
+ for channel_data in signal_data:
594
+ time_features = [
595
+ np.mean(channel_data),
596
+ np.std(channel_data),
597
+ np.ptp(channel_data),
598
+ np.median(channel_data),
599
+ np.mean(np.abs(channel_data)),
600
+ np.sqrt(np.mean(channel_data**2))
601
+ ]
602
+
603
+ try:
604
+ fft_vals = np.abs(np.fft.rfft(channel_data[::4]))
605
+ freqs = np.fft.rfftfreq(len(channel_data)//4, 4/self.fs)
606
+
607
+ delta_power = np.sum(fft_vals[(freqs >= 0.5) & (freqs <= 4)])
608
+ theta_power = np.sum(fft_vals[(freqs >= 4) & (freqs <= 8)])
609
+ alpha_power = np.sum(fft_vals[(freqs >= 8) & (freqs <= 13)])
610
+ beta_power = np.sum(fft_vals[(freqs >= 13) & (freqs <= 30)])
611
+ total_power = np.sum(fft_vals) + 1e-10
612
+
613
+ freq_features = [
614
+ delta_power / total_power,
615
+ theta_power / total_power,
616
+ alpha_power / total_power,
617
+ beta_power / total_power
618
+ ]
619
+ except:
620
+ freq_features = [0.25, 0.25, 0.25, 0.25]
621
+
622
+ nonlinear_features = [
623
+ np.std(np.diff(channel_data)) / (np.std(channel_data) + 1e-10),
624
+ np.mean(np.abs(np.diff(channel_data)))
625
+ ]
626
+
627
+ channel_features = time_features + freq_features + nonlinear_features
628
+ features.extend(channel_features)
629
+
630
+ return np.array(features, dtype=np.float32)
631
+
632
+ def calculate_channel_contributions(self, signal_data):
633
+ contributions = np.zeros(6)
634
+ for i in range(6):
635
+ contributions[i] = np.sqrt(np.mean(signal_data[i]**2))
636
+
637
+ total = np.sum(contributions) + 1e-10
638
+ return contributions / total
639
+
640
+ def update_performance_metrics(self, predicted_class, confidence, processing_time):
641
+ metrics = self.performance_metrics
642
+
643
+ metrics['total_predictions'] += 1
644
+
645
+ if confidence > 0.8:
646
+ metrics['high_confidence_predictions'] += 1
647
+ elif confidence < 0.5:
648
+ metrics['low_confidence_predictions'] += 1
649
+
650
+ metrics['prediction_times'].append(processing_time)
651
+ if len(metrics['prediction_times']) > 50:
652
+ metrics['prediction_times'].pop(0)
653
+
654
+ if metrics['prediction_times']:
655
+ avg_time = np.mean(metrics['prediction_times'])
656
+ metrics['processing_fps'] = 1.0 / max(avg_time, 0.001)
657
+
658
+ if metrics['last_prediction'] is not None and metrics['last_prediction'] != predicted_class:
659
+ metrics['emotion_transitions'] += 1
660
+ metrics['last_prediction'] = predicted_class
661
+
662
+ metrics['confidence_trend'].append(float(confidence))
663
+ if len(metrics['confidence_trend']) > 10:
664
+ metrics['confidence_trend'].pop(0)
665
+
666
+ if self.confidence_history:
667
+ metrics['average_confidence'] = np.mean(self.confidence_history)
668
+
669
+ if metrics['total_predictions'] > 1:
670
+ transition_rate = metrics['emotion_transitions'] / metrics['total_predictions']
671
+ metrics['stability_score'] = max(0, 1.0 - transition_rate)
672
+
673
+ def update_plot(self, frame):
674
+ if not self.is_running:
675
+ return
676
+
677
+ start_time = time.time()
678
+
679
+ if frame % 2 == 0:
680
+ if np.random.random() < 0.2:
681
+ emotion_bias = np.random.randint(0, 4)
682
+ else:
683
+ emotion_bias = None
684
+
685
+ new_samples = self.buffer_size // 8
686
+ new_data = self.generate_realistic_eeg_signal(emotion_bias)[:, :new_samples]
687
+ self.update_buffer(new_data)
688
+
689
+ prediction_start = time.time()
690
+ features = self.extract_lightweight_features(self.raw_buffer)
691
+
692
+ try:
693
+ scaled_features = self.scaler.transform([features])
694
+
695
+ if self.selector is not None:
696
+ scaled_features = self.selector.transform(scaled_features)
697
+
698
+ if self.pca is not None:
699
+ scaled_features = self.pca.transform(scaled_features)
700
+
701
+ probs = self.model.predict(scaled_features, verbose=0)[0]
702
+ predicted_class = np.argmax(probs)
703
+ confidence = np.max(probs)
704
+
705
+ except Exception as e:
706
+ print(f"Prediction error: {e}")
707
+ probs = np.array([0.25, 0.25, 0.25, 0.25])
708
+ predicted_class = 0
709
+ confidence = 0.25
710
+
711
+ prediction_time = time.time() - prediction_start
712
+
713
+ self.update_performance_metrics(predicted_class, confidence, prediction_time)
714
+
715
+ self.prediction_history.append(predicted_class)
716
+ self.confidence_history.append(confidence)
717
+ self.time_history.append(datetime.now())
718
+
719
+ if len(self.prediction_history) > self.max_history:
720
+ self.prediction_history.pop(0)
721
+ self.confidence_history.pop(0)
722
+ self.time_history.pop(0)
723
+
724
+ contributions = self.calculate_channel_contributions(self.raw_buffer)
725
+
726
+ self.update_eeg_plot()
727
+ self.update_probabilities(probs)
728
+ self.update_performance_plot()
729
+ self.update_contributions(contributions)
730
+
731
+ emotion_name = self.emotion_labels[predicted_class]
732
+ metrics = self.performance_metrics
733
+ elapsed_time = time.time() - self.start_time if self.start_time else 0
734
+
735
+ print(f"\r{datetime.now().strftime('%H:%M:%S')} | "
736
+ f"Emotion: {emotion_name} | "
737
+ f"Conf: {confidence:.3f} | "
738
+ f"FPS: {metrics['processing_fps']:.1f} | "
739
+ f"Stab: {metrics['stability_score']:.2f} | "
740
+ f"Total: {metrics['total_predictions']} | "
741
+ f"Time: {elapsed_time:.0f}s", end='')
742
+
743
+ def update_eeg_plot(self):
744
+ self.ax1.clear()
745
+
746
+ colors = plt.cm.tab10(np.linspace(0, 1, 6))
747
+ display_samples = min(300, self.buffer_size) # Show fewer samples for performance
748
+
749
+ for i in range(6):
750
+ offset = i * 20e-6
751
+ signal = self.raw_buffer[i, -display_samples:] + offset
752
+ self.ax1.plot(signal, label=self.ch_names[i],
753
+ color=colors[i], linewidth=1.0, alpha=0.8)
754
+
755
+ self.ax1.set_title("Live EEG Signals", fontsize=12)
756
+ self.ax1.set_xlabel("Time (samples)")
757
+ self.ax1.set_ylabel("Amplitude (µV)")
758
+ self.ax1.legend(loc='upper right', fontsize=8)
759
+ self.ax1.grid(True, alpha=0.3)
760
+
761
+ def update_performance_plot(self):
762
+ self.ax3.clear()
763
+
764
+ metrics = self.performance_metrics
765
+
766
+ if metrics['total_predictions'] > 0:
767
+ high_conf_pct = (metrics['high_confidence_predictions'] / metrics['total_predictions']) * 100
768
+ low_conf_pct = (metrics['low_confidence_predictions'] / metrics['total_predictions']) * 100
769
+
770
+ metrics_text = f"""PERFORMANCE METRICS
771
+
772
+ Total Predictions: {metrics['total_predictions']}
773
+ Average Confidence: {metrics['average_confidence']:.3f}
774
+ High Confidence (>0.8): {high_conf_pct:.1f}%
775
+ Low Confidence (<0.5): {low_conf_pct:.1f}%
776
+
777
+ Processing Speed: {metrics['processing_fps']:.1f} FPS
778
+ Stability Score: {metrics['stability_score']:.3f}
779
+ Emotion Transitions: {metrics['emotion_transitions']}
780
+
781
+ Model Accuracy: {high_conf_pct:.1f}%
782
+ Response Time: {np.mean(metrics['prediction_times'])*1000:.1f}ms"""
783
+
784
+ self.ax3.text(0.02, 0.98, metrics_text,
785
+ transform=self.ax3.transAxes,
786
+ fontsize=8, verticalalignment='top',
787
+ fontfamily='monospace',
788
+ bbox=dict(boxstyle="round,pad=0.3", facecolor="lightblue", alpha=0.7))
789
+
790
+ if len(metrics['confidence_trend']) > 1:
791
+ trend_x = np.arange(len(metrics['confidence_trend']))
792
+ trend_data = np.array(metrics['confidence_trend'], dtype=np.float64)
793
+ self.ax3.plot(trend_x + 0.6, trend_data * 0.4 + 0.1,
794
+ 'g-o', markersize=3, linewidth=2, label='Confidence Trend')
795
+
796
+ # Add trend analysis
797
+ if len(metrics['confidence_trend']) > 3:
798
+ try:
799
+ x_data = np.array(range(len(trend_data[-5:])), dtype=np.float64)
800
+ y_data = np.array(trend_data[-5:], dtype=np.float64)
801
+ recent_trend = np.polyfit(x_data, y_data, 1)[0]
802
+ trend_direction = "↗" if recent_trend > 0.01 else "↘" if recent_trend < -0.01 else "→"
803
+ self.ax3.text(0.7, 0.9, f"Trend: {trend_direction}",
804
+ transform=self.ax3.transAxes, fontsize=10, fontweight='bold')
805
+ except:
806
+ # Fallback if polyfit fails
807
+ self.ax3.text(0.7, 0.9, f"Trend: →",
808
+ transform=self.ax3.transAxes, fontsize=10, fontweight='bold')
809
+
810
+ self.ax3.set_xlim(0, 1)
811
+ self.ax3.set_ylim(0, 1)
812
+ self.ax3.set_title("Performance Metrics & Confidence Trend", fontsize=10)
813
+
814
+ if metrics['average_confidence'] > 0.8:
815
+ title_color = 'green'
816
+ elif metrics['average_confidence'] > 0.6:
817
+ title_color = 'orange'
818
+ else:
819
+ title_color = 'red'
820
+ self.ax3.title.set_color(title_color)
821
+
822
+ def update_contributions(self, contributions):
823
+ self.ax4.clear()
824
+
825
+ colors = plt.cm.viridis(contributions)
826
+ bars = self.ax4.barh(self.ch_names, contributions, color=colors)
827
+
828
+ for i, (bar, v) in enumerate(zip(bars, contributions)):
829
+ if v > 0.05:
830
+ self.ax4.text(v + 0.02, i, f"{v*100:.1f}%",
831
+ va='center', fontsize=9)
832
+
833
+ self.ax4.set_title("Electrode Contributions", fontsize=10)
834
+ self.ax4.set_xlabel("Contribution Rate", fontsize=9)
835
+ self.ax4.set_xlim(0, 0.6)
836
+ self.ax4.grid(True, alpha=0.3, axis='x')
837
+
838
+ def update_probabilities(self, probs):
839
+ self.ax2.clear()
840
+
841
+ emotions = [self.emotion_labels[i] for i in range(4)]
842
+ bars = self.ax2.barh(emotions, probs, color=self.emotion_colors)
843
+
844
+ max_idx = np.argmax(probs)
845
+ bars[max_idx].set_edgecolor('black')
846
+ bars[max_idx].set_linewidth(2)
847
+
848
+ for bar, prob in zip(bars, probs):
849
+ width = bar.get_width()
850
+ if width > 0.05:
851
+ self.ax2.text(width + 0.02, bar.get_y() + bar.get_height()/2,
852
+ f"{width*100:.1f}%", ha='left', va='center',
853
+ fontsize=9, fontweight='bold')
854
+
855
+ self.ax2.set_title("Emotion Probabilities", fontsize=12)
856
+ self.ax2.set_xlabel("Probability")
857
+ self.ax2.set_xlim(0, 1)
858
+ self.ax2.grid(True, alpha=0.3, axis='x')
859
+
860
+ current_emotion = emotions[max_idx]
861
+ confidence = probs[max_idx]
862
+ self.ax2.text(0.5, 1.05, f"Current: {current_emotion} ({confidence*100:.1f}%)",
863
+ transform=self.ax2.transAxes, ha='center',
864
+ fontsize=10, fontweight='bold', color=self.emotion_colors[max_idx])
865
+
866
+ def start_monitoring(self):
867
+ print("\n" + "="*60)
868
+ print(" OPTIMIZED EEG EMOTION RECOGNITION MONITOR")
869
+ print("="*60)
870
+ print("\nPress 'X' to close the window...")
871
+ print("Real-time performance metrics will be displayed")
872
+ print("-"*60)
873
+
874
+ self.is_running = True
875
+ self.start_time = time.time()
876
+
877
+ self.animation = FuncAnimation(
878
+ self.fig,
879
+ self.update_plot,
880
+ interval=self.update_interval,
881
+ blit=False,
882
+ cache_frame_data=False
883
+ )
884
+
885
+ plt.show()
886
+
887
+ self.is_running = False
888
+ print("\n\nMonitoring stopped.")
889
+
890
+ if self.prediction_history:
891
+ self.print_summary_statistics()
892
+
893
+ def print_summary_statistics(self):
894
+ print("\n" + "="*80)
895
+ print(" DETAILED PERFORMANCE & STATISTICS SUMMARY")
896
+ print("="*80)
897
+
898
+ if not self.prediction_history:
899
+ print("No data collected.")
900
+ return
901
+
902
+ metrics = self.performance_metrics
903
+ total_time = time.time() - self.start_time if self.start_time else 0
904
+
905
+ print("\n📊 PERFORMANCE METRICS:")
906
+ print(f" Total Predictions: {metrics['total_predictions']}")
907
+ print(f" Total Runtime: {total_time:.1f} seconds")
908
+ print(f" Average Processing Speed: {metrics['processing_fps']:.1f} FPS")
909
+ print(f" Average Response Time: {np.mean(metrics['prediction_times'])*1000:.1f}ms")
910
+ print(f" Model Stability Score: {metrics['stability_score']:.3f} (0-1, higher=better)")
911
+ print(f" Emotion Transitions: {metrics['emotion_transitions']}")
912
+
913
+ print(f"\n🎯 CONFIDENCE ANALYSIS:")
914
+ total = len(self.prediction_history)
915
+ high_conf_count = metrics['high_confidence_predictions']
916
+ low_conf_count = metrics['low_confidence_predictions']
917
+ medium_conf_count = max(0, total - high_conf_count - low_conf_count)
918
+
919
+ print(f" Average Confidence: {metrics['average_confidence']:.3f}")
920
+ print(f" Confidence Std Dev: {np.std(self.confidence_history):.3f}")
921
+ print(f" High Confidence (>0.8): {high_conf_count} ({high_conf_count/total*100:.1f}%)")
922
+ print(f" Medium Confidence (0.5-0.8): {medium_conf_count} ({medium_conf_count/total*100:.1f}%)")
923
+ print(f" Low Confidence (<0.5): {low_conf_count} ({low_conf_count/total*100:.1f}%)")
924
+
925
+ accuracy_score = high_conf_count / total * 100 if total > 0 else 0
926
+ print(f"\n🏆 MODEL QUALITY ASSESSMENT:")
927
+ print(f" Estimated Accuracy: {accuracy_score:.1f}% (based on high confidence predictions)")
928
+
929
+ if accuracy_score >= 80:
930
+ quality = "EXCELLENT 🌟"
931
+ elif accuracy_score >= 70:
932
+ quality = "GOOD ✅"
933
+ elif accuracy_score >= 60:
934
+ quality = "FAIR ⚠️"
935
+ else:
936
+ quality = "POOR ❌"
937
+ print(f" Model Quality Rating: {quality}")
938
+
939
+ emotion_counts = {i: 0 for i in range(4)}
940
+ for pred in self.prediction_history:
941
+ emotion_counts[pred] += 1
942
+
943
+ print(f"\n😊 EMOTION DISTRIBUTION:")
944
+ for emotion_id, count in emotion_counts.items():
945
+ percentage = (count / total) * 100
946
+ bar = "█" * int(percentage / 5)
947
+ print(f" {self.emotion_labels[emotion_id]:<15}: {count:>3} ({percentage:>5.1f}%) {bar}")
948
+
949
+ dominant_emotion = max(emotion_counts, key=emotion_counts.get)
950
+ dominant_percentage = emotion_counts[dominant_emotion] / total * 100
951
+ print(f"\n Dominant Emotion: {self.emotion_labels[dominant_emotion]} ({dominant_percentage:.1f}%)")
952
+
953
+ if len(metrics['confidence_trend']) > 3:
954
+ try:
955
+ x_data = np.array(range(len(metrics['confidence_trend'])), dtype=np.float64)
956
+ y_data = np.array(metrics['confidence_trend'], dtype=np.float64)
957
+ trend_slope = np.polyfit(x_data, y_data, 1)[0]
958
+ print(f"\n📈 TREND ANALYSIS:")
959
+ if trend_slope > 0.01:
960
+ trend_desc = "IMPROVING ↗"
961
+ elif trend_slope < -0.01:
962
+ trend_desc = "DECLINING ↘"
963
+ else:
964
+ trend_desc = "STABLE →"
965
+ print(f" Recent Confidence Trend: {trend_desc} (slope: {trend_slope:.4f})")
966
+ except Exception as e:
967
+ print(f"\n📈 TREND ANALYSIS:")
968
+ print(f" Recent Confidence Trend: STABLE → (analysis unavailable)")
969
+
970
+ print(f"\n💡 RECOMMENDATIONS:")
971
+ if accuracy_score < 70:
972
+ print(" • Consider retraining the model with more data")
973
+ print(" • Check data quality and preprocessing steps")
974
+ if metrics['stability_score'] < 0.7:
975
+ print(" • Model predictions are unstable - review signal quality")
976
+ if metrics['processing_fps'] < 5:
977
+ print(" • Processing speed is slow - consider model optimization")
978
+ if accuracy_score >= 80 and metrics['stability_score'] >= 0.8:
979
+ print(" • Model performance is excellent! ✨")
980
+
981
+ print("\n" + "="*80)
982
+
983
+
984
+ def main():
985
+ model_path = 'path/to/bai-6 EmotionOptimized.h5'
986
+ scaler_path = 'path/to/bai-6 ScalerOptimized.pkl'
987
+ selector_path = 'path/to/bai-6_feature_selector_opt.pkl'
988
+ pca_path = 'path/to/bai-6_pca_reducer_opt.pkl'
989
+
990
+ try:
991
+ monitor = EEGEmotionMonitorOptimized(
992
+ model_path, scaler_path, selector_path, pca_path
993
+ )
994
+
995
+ monitor.start_monitoring()
996
+
997
+ except FileNotFoundError as e:
998
+ print(f"Model or preprocessor file not found: {e}")
999
+ print("Please ensure the model has been trained and saved.")
1000
+ print("Available fallback: Using basic model without feature selection/PCA")
1001
+
1002
+ try:
1003
+ basic_model_path = 'path/to/bai-6 EmotionOptimized.h5'
1004
+ basic_scaler_path = 'path/to/bai-6 ScalerOptimized.pkl'
1005
+
1006
+ monitor = EEGEmotionMonitorOptimized(basic_model_path, basic_scaler_path)
1007
+ monitor.start_monitoring()
1008
+
1009
+ except Exception as e2:
1010
+ print(f"Fallback also failed: {e2}")
1011
+
1012
+ except Exception as e:
1013
+ print(f"Error: {e}")
1014
+ import traceback
1015
+ traceback.print_exc()
1016
+
1017
+
1018
+ if __name__ == "__main__":
1019
+ main()
1020
+ ```
1021
+
1022
+ </details>
1023
+
1024
  -------------
1025
  ## Lisans/License
1026
  CC-BY-NC-SA-4.0