ApurvaKondekar commited on
Commit
1fbf9b6
Β·
verified Β·
1 Parent(s): 0f9f205
Files changed (1) hide show
  1. app.py +645 -320
app.py CHANGED
@@ -1,5 +1,3 @@
1
-
2
-
3
  import gradio as gr
4
  import torch
5
  import torch.nn as nn
@@ -33,11 +31,13 @@ TEXT_MAX_LEN = 64
33
  LABELS = ["angry", "happy", "neutral", "sad"]
34
 
35
  gpu_status = (
36
- "GPU Enabled"
37
  if torch.cuda.is_available()
38
- else "Running on CPU"
39
  )
40
 
 
 
41
  # =========================================================
42
  # LOAD PROCESSORS
43
  # =========================================================
@@ -436,7 +436,7 @@ def predict_emotion(video_file):
436
  if video_file is None:
437
 
438
  return (
439
- "Please upload a video.",
440
  None,
441
  ""
442
  )
@@ -489,12 +489,21 @@ def predict_emotion(video_file):
489
 
490
  confidence = float(probs.max())
491
 
 
 
 
 
 
 
 
 
 
492
  result_text = f"""
493
- ## Predicted Emotion
494
-
495
- ### {predicted_emotion.upper()}
496
-
497
- Confidence Score: {confidence:.2%}
498
  """
499
 
500
  if os.path.exists(audio_path):
@@ -509,339 +518,643 @@ def predict_emotion(video_file):
509
  except Exception as e:
510
 
511
  return (
512
- f"Error: {str(e)}",
513
  None,
514
  ""
515
  )
516
 
517
  # =========================================================
518
- # CLEAN PROFESSIONAL CSS
519
  # =========================================================
520
 
521
  custom_css = """
522
-
523
- /* =======================================================
524
- BACKGROUND
525
- ======================================================= */
526
-
527
- body {
528
- background: linear-gradient(
529
- -45deg,
530
- #dbeafe,
531
- #bfdbfe,
532
- #e0f2fe,
533
- #f0f9ff
534
- );
535
-
536
- background-size: 400% 400%;
537
-
538
- animation: gradientMove 15s ease infinite;
539
-
540
- font-family: 'Segoe UI', sans-serif;
 
 
541
  }
542
 
543
- /* Animated background */
 
 
 
 
 
544
 
545
- @keyframes gradientMove {
 
 
 
 
 
 
 
546
 
547
- 0% {
548
- background-position: 0% 50%;
549
- }
 
 
 
 
 
 
 
 
 
 
550
 
551
- 50% {
552
- background-position: 100% 50%;
553
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
554
 
555
- 100% {
556
- background-position: 0% 50%;
557
- }
558
  }
559
 
560
- /* =======================================================
561
- MAIN CONTAINER
562
- ======================================================= */
 
563
 
 
 
 
564
  .gradio-container {
565
-
566
- max-width: 1300px !important;
567
-
568
- margin: auto;
569
-
570
- padding-top: 20px;
571
  }
572
 
573
- /* =======================================================
574
- TITLE
575
- ======================================================= */
576
-
577
- .main-title {
578
-
579
  text-align: center;
 
 
 
580
 
581
- font-size: 56px;
582
-
583
- font-weight: 800;
584
-
585
- color: #0f172a;
586
-
587
- margin-bottom: 10px;
588
-
589
- letter-spacing: 1px;
590
-
591
- animation: fadeInDown 1s ease;
 
 
 
 
 
 
 
 
 
592
  }
593
 
594
- .subtitle {
 
 
 
 
 
 
 
 
 
 
595
 
596
- text-align: center;
 
 
 
597
 
598
- font-size: 22px;
 
 
 
599
 
600
- color: #334155;
 
 
 
601
 
602
- margin-bottom: 35px;
 
 
 
 
 
 
 
 
603
 
604
- animation: fadeIn 2s ease;
 
 
 
 
 
 
 
605
  }
606
 
607
- /* =======================================================
608
- ANIMATIONS
609
- ======================================================= */
 
 
 
 
610
 
611
- @keyframes fadeInDown {
 
 
 
 
 
 
612
 
613
- from {
614
- opacity: 0;
615
- transform: translateY(-30px);
616
- }
617
 
618
- to {
619
- opacity: 1;
620
- transform: translateY(0px);
621
- }
 
 
 
 
622
  }
623
 
624
- @keyframes fadeIn {
 
 
 
 
 
 
 
 
 
 
 
 
625
 
626
- from {
627
- opacity: 0;
628
- }
 
 
 
 
 
629
 
630
- to {
631
- opacity: 1;
632
- }
633
  }
634
 
635
- @keyframes floating {
 
 
 
 
 
 
 
 
 
636
 
637
- 0% {
638
- transform: translateY(0px);
639
- }
 
 
 
 
 
 
640
 
641
- 50% {
642
- transform: translateY(-6px);
643
- }
 
 
 
 
 
644
 
645
- 100% {
646
- transform: translateY(0px);
647
- }
 
 
 
648
  }
649
 
650
- /* =======================================================
651
- CARDS / PANELS
652
- ======================================================= */
 
653
 
 
 
 
654
  .gr-group,
655
  .gr-box,
656
- .gr-panel {
657
-
658
- background: rgba(255,255,255,0.55) !important;
659
-
660
- backdrop-filter: blur(18px);
661
-
662
- border-radius: 24px !important;
663
-
664
- border: 1px solid rgba(255,255,255,0.35);
665
-
666
- box-shadow:
667
- 0 8px 32px rgba(31, 38, 135, 0.15);
668
-
669
- transition: all 0.35s ease;
670
-
671
- animation: floating 5s ease-in-out infinite;
672
  }
673
 
674
- /* Hover effect */
675
-
676
- .gr-group:hover,
677
- .gr-box:hover,
678
- .gr-panel:hover {
679
-
680
- transform: translateY(-6px);
681
-
682
- box-shadow:
683
- 0 15px 35px rgba(14,165,233,0.25);
684
  }
685
 
686
- /* =======================================================
687
- BUTTONS
688
- ======================================================= */
689
-
690
- .gr-button {
 
 
691
 
692
- background: linear-gradient(
693
- 135deg,
694
- #38bdf8,
695
- #0ea5e9
696
- ) !important;
697
 
698
- border: none !important;
 
 
 
699
 
700
- color: white !important;
 
 
 
 
 
 
 
 
 
701
 
 
 
 
 
 
 
 
 
702
  font-weight: 700 !important;
703
-
704
- border-radius: 14px !important;
705
-
 
 
 
 
 
 
706
  transition: all 0.3s ease !important;
707
-
708
- box-shadow:
709
- 0 4px 15px rgba(14,165,233,0.35);
710
  }
711
 
712
- /* Button hover */
713
-
714
- .gr-button:hover {
715
-
716
- transform: scale(1.05);
717
-
718
- box-shadow:
719
- 0 8px 25px rgba(14,165,233,0.45);
720
-
721
  background: linear-gradient(
722
- 135deg,
723
- #0ea5e9,
724
- #0284c7
 
725
  ) !important;
 
726
  }
727
 
728
- /* =======================================================
729
- TEXT
730
- ======================================================= */
731
-
732
- h1, h2, h3, h4 {
733
-
734
- color: #0f172a !important;
735
-
736
- font-weight: 700 !important;
737
  }
738
 
739
- label {
740
-
741
- color: #1e293b !important;
742
-
743
- font-weight: 600 !important;
 
 
 
744
  }
745
 
746
- /* =======================================================
747
- TEXTBOXES
748
- ======================================================= */
 
749
 
 
 
 
750
  textarea,
 
751
  input {
752
-
753
- border-radius: 14px !important;
754
-
755
- border: 1px solid #cbd5e1 !important;
756
-
757
- transition: all 0.3s ease !important;
 
 
758
  }
759
 
760
- /* Glow effect */
761
-
762
  textarea:focus,
763
  input:focus {
764
-
765
- border: 1px solid #38bdf8 !important;
766
-
767
- box-shadow:
768
- 0 0 15px rgba(56,189,248,0.35) !important;
769
  }
770
 
771
- /* =======================================================
772
- VIDEO COMPONENT
773
- ======================================================= */
774
-
775
  video {
776
-
777
- border-radius: 20px !important;
778
-
779
- overflow: hidden;
780
-
781
  box-shadow:
782
- 0 10px 25px rgba(0,0,0,0.15);
 
783
  }
784
 
785
- /* =======================================================
786
- RESULT LABEL
787
- ======================================================= */
788
-
789
- .gr-label {
 
 
 
790
 
791
- background: rgba(255,255,255,0.65) !important;
 
 
 
 
792
 
793
- border-radius: 16px !important;
 
 
 
 
 
 
 
 
 
794
 
795
- padding: 10px;
 
 
 
 
 
 
 
 
 
 
 
796
  }
797
 
798
- /* =======================================================
799
- ACCORDION
800
- ======================================================= */
 
 
 
 
 
 
 
 
 
 
 
 
801
 
802
- .gr-accordion {
 
 
 
 
803
 
804
- border-radius: 18px !important;
 
 
805
 
806
- overflow: hidden;
 
 
807
  }
808
 
809
- /* =======================================================
810
- FOOTER
811
- ======================================================= */
 
 
 
 
 
 
 
 
 
 
812
 
813
- .footer {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
814
 
 
 
 
 
815
  text-align: center;
 
 
 
 
 
 
 
 
 
 
816
 
817
- color: #334155;
818
-
819
- margin-top: 35px;
820
-
821
- font-size: 15px;
 
 
 
 
822
 
823
- opacity: 0.85;
 
 
 
 
824
  }
825
 
826
- /* =======================================================
827
- SCROLLBAR
828
- ======================================================= */
 
 
 
 
 
 
829
 
830
- ::-webkit-scrollbar {
 
 
 
831
 
832
- width: 10px;
 
 
833
  }
834
 
835
- ::-webkit-scrollbar-thumb {
 
 
 
 
 
 
 
 
 
836
 
837
- background: #7dd3fc;
 
 
 
 
 
 
 
838
 
839
- border-radius: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
840
  }
841
 
842
- ::-webkit-scrollbar-track {
 
 
 
 
 
 
 
 
843
 
844
- background: transparent;
 
 
 
 
 
845
  }
846
  """
847
 
@@ -851,136 +1164,148 @@ SCROLLBAR
851
 
852
  with gr.Blocks(
853
  title="Multimodal Emotion Recognition",
854
- theme=gr.themes.Glass(),
 
 
 
 
855
  css=custom_css
856
  ) as demo:
857
 
858
- # HEADER
859
-
860
  gr.HTML("""
861
- <div class="main-title">
862
- MULTIMODAL EMOTION RECOGNITION
 
 
 
 
 
 
 
 
 
 
 
863
  </div>
 
864
 
865
- <div class="subtitle">
866
- Real-Time AI Emotion Detection using Audio, Text and Video Fusion
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
867
  </div>
868
  """)
869
 
870
- # STATUS
871
-
872
- gr.Markdown(
873
- f"### System Status: {gpu_status}"
874
- )
875
-
876
- # INFO PANELS
877
-
878
- with gr.Row():
879
-
880
- with gr.Column():
881
-
882
- with gr.Group():
883
-
884
- gr.Markdown("""
885
- ### Modalities Used
886
-
887
- - Audio Analysis
888
- - Speech Transcription
889
- - Facial Expression Analysis
890
- """)
891
-
892
- with gr.Column():
893
-
894
- with gr.Group():
895
-
896
- gr.Markdown("""
897
- ### Models Used
898
-
899
- - Wav2Vec2
900
- - BERT
901
- - ResNet18
902
- - Whisper
903
- """)
904
-
905
- gr.Markdown("---")
906
 
907
- # MAIN SECTION
908
 
 
909
  with gr.Row(equal_height=True):
910
 
911
- # LEFT
912
-
913
  with gr.Column(scale=1):
914
 
915
- gr.Markdown("## Upload Video")
916
 
917
  video_input = gr.Video(
918
- label="Input Video",
919
- height=400
920
  )
921
 
922
  predict_btn = gr.Button(
923
- "Analyze Emotion",
924
  variant="primary",
925
  size="lg"
926
  )
927
 
928
- # RIGHT
929
-
930
  with gr.Column(scale=1):
931
 
932
- gr.Markdown("## Results")
933
 
934
  result_text = gr.Markdown(
935
- value="Upload a video and click Analyze Emotion."
 
 
936
  )
937
 
938
  result_output = gr.Label(
939
- label="Emotion Probabilities",
940
  num_top_classes=4
941
  )
942
 
943
  transcription_output = gr.Textbox(
944
- label="Transcribed Text",
945
- lines=6,
946
- interactive=False
 
947
  )
948
 
949
- gr.Markdown("---")
950
 
951
- # ABOUT SECTION
952
-
953
- with gr.Accordion(
954
- "About the Model",
955
- open=False
956
- ):
957
 
958
  gr.Markdown("""
959
- This multimodal system combines:
960
-
961
- - Audio features using Wav2Vec2
962
- - Text understanding using BERT
963
- - Video feature extraction using ResNet18
964
- - Hybrid Fusion Block for final prediction
965
-
966
- Supported emotions:
967
-
968
- - Angry
969
- - Happy
970
- - Neutral
971
- - Sad
972
- """)
973
 
974
- # FOOTER
975
 
 
 
 
 
 
 
 
 
 
 
 
976
  gr.HTML("""
977
- <div class="footer">
978
- Built using PyTorch, Transformers, Whisper and Gradio
979
  </div>
980
  """)
981
 
982
- # BUTTON ACTION
983
-
984
  predict_btn.click(
985
  fn=predict_emotion,
986
  inputs=[video_input],
 
 
 
1
  import gradio as gr
2
  import torch
3
  import torch.nn as nn
 
31
  LABELS = ["angry", "happy", "neutral", "sad"]
32
 
33
  gpu_status = (
34
+ "GPU ONLINE"
35
  if torch.cuda.is_available()
36
+ else "CPU MODE"
37
  )
38
 
39
+ gpu_color = "#00ff88" if torch.cuda.is_available() else "#ff6b35"
40
+
41
  # =========================================================
42
  # LOAD PROCESSORS
43
  # =========================================================
 
436
  if video_file is None:
437
 
438
  return (
439
+ "⚠ NO INPUT DETECTED β€” UPLOAD A VIDEO FILE TO BEGIN ANALYSIS.",
440
  None,
441
  ""
442
  )
 
489
 
490
  confidence = float(probs.max())
491
 
492
+ emotion_icons = {
493
+ "angry": "⚑",
494
+ "happy": "β—ˆ",
495
+ "neutral": "β—Ž",
496
+ "sad": "β—‡"
497
+ }
498
+
499
+ icon = emotion_icons.get(predicted_emotion, "β—†")
500
+
501
  result_text = f"""
502
+ ## {icon} EMOTION DETECTED
503
+
504
+ ### `{predicted_emotion.upper()}`
505
+
506
+ **Neural Confidence:** `{confidence:.2%}`
507
  """
508
 
509
  if os.path.exists(audio_path):
 
518
  except Exception as e:
519
 
520
  return (
521
+ f"⚠ SYSTEM ERROR: {str(e)}",
522
  None,
523
  ""
524
  )
525
 
526
  # =========================================================
527
+ # FUTURISTIC CYBERPUNK CSS
528
  # =========================================================
529
 
530
  custom_css = """
531
+ /* =====================================================
532
+ IMPORT FONTS
533
+ ===================================================== */
534
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700;900&family=Share+Tech+Mono&family=Rajdhani:wght@300;400;600;700&display=swap');
535
+
536
+ /* =====================================================
537
+ CSS VARIABLES
538
+ ===================================================== */
539
+ :root {
540
+ --neon-cyan: #00f5ff;
541
+ --neon-green: #00ff88;
542
+ --neon-pink: #ff006e;
543
+ --neon-purple: #bf00ff;
544
+ --neon-orange: #ff6b35;
545
+ --dark-bg: #020b18;
546
+ --dark-panel: #040f1e;
547
+ --dark-card: #071525;
548
+ --border-glow: rgba(0, 245, 255, 0.25);
549
+ --text-primary: #e0f7ff;
550
+ --text-dim: #4a7a8a;
551
+ --grid-color: rgba(0, 245, 255, 0.04);
552
  }
553
 
554
+ /* =====================================================
555
+ GLOBAL RESET & BASE
556
+ ===================================================== */
557
+ * {
558
+ box-sizing: border-box;
559
+ }
560
 
561
+ body, .gradio-container {
562
+ background-color: var(--dark-bg) !important;
563
+ font-family: 'Rajdhani', sans-serif !important;
564
+ color: var(--text-primary) !important;
565
+ min-height: 100vh;
566
+ position: relative;
567
+ overflow-x: hidden;
568
+ }
569
 
570
+ /* Animated grid background */
571
+ body::before {
572
+ content: '';
573
+ position: fixed;
574
+ inset: 0;
575
+ background-image:
576
+ linear-gradient(var(--grid-color) 1px, transparent 1px),
577
+ linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
578
+ background-size: 40px 40px;
579
+ z-index: 0;
580
+ pointer-events: none;
581
+ animation: gridDrift 20s linear infinite;
582
+ }
583
 
584
+ /* Scanline overlay */
585
+ body::after {
586
+ content: '';
587
+ position: fixed;
588
+ inset: 0;
589
+ background: repeating-linear-gradient(
590
+ 0deg,
591
+ transparent,
592
+ transparent 2px,
593
+ rgba(0, 245, 255, 0.015) 2px,
594
+ rgba(0, 245, 255, 0.015) 4px
595
+ );
596
+ pointer-events: none;
597
+ z-index: 1;
598
+ animation: scanlines 8s linear infinite;
599
+ }
600
 
601
+ @keyframes gridDrift {
602
+ 0% { background-position: 0 0; }
603
+ 100% { background-position: 40px 40px; }
604
  }
605
 
606
+ @keyframes scanlines {
607
+ 0% { background-position: 0 0; }
608
+ 100% { background-position: 0 100px; }
609
+ }
610
 
611
+ /* =====================================================
612
+ CONTAINER
613
+ ===================================================== */
614
  .gradio-container {
615
+ max-width: 1400px !important;
616
+ margin: auto !important;
617
+ padding: 30px 24px !important;
618
+ position: relative;
619
+ z-index: 2;
 
620
  }
621
 
622
+ /* =====================================================
623
+ HEADER
624
+ ===================================================== */
625
+ .cyber-header {
 
 
626
  text-align: center;
627
+ padding: 40px 20px 20px;
628
+ position: relative;
629
+ }
630
 
631
+ .cyber-title {
632
+ font-family: 'Orbitron', monospace !important;
633
+ font-size: clamp(28px, 5vw, 58px) !important;
634
+ font-weight: 900 !important;
635
+ letter-spacing: 6px !important;
636
+ text-transform: uppercase !important;
637
+ color: transparent !important;
638
+ background: linear-gradient(
639
+ 90deg,
640
+ var(--neon-cyan) 0%,
641
+ #ffffff 40%,
642
+ var(--neon-purple) 70%,
643
+ var(--neon-cyan) 100%
644
+ ) !important;
645
+ background-size: 200% auto !important;
646
+ -webkit-background-clip: text !important;
647
+ background-clip: text !important;
648
+ animation: titleShimmer 4s linear infinite, fadeSlideDown 0.8s ease both;
649
+ text-shadow: none !important;
650
+ position: relative;
651
  }
652
 
653
+ .cyber-title::after {
654
+ content: attr(data-text);
655
+ position: absolute;
656
+ left: 0; right: 0;
657
+ top: 0;
658
+ color: var(--neon-cyan);
659
+ filter: blur(18px);
660
+ opacity: 0.35;
661
+ animation: titlePulse 3s ease-in-out infinite;
662
+ z-index: -1;
663
+ }
664
 
665
+ @keyframes titleShimmer {
666
+ 0% { background-position: 0% center; }
667
+ 100% { background-position: 200% center; }
668
+ }
669
 
670
+ @keyframes titlePulse {
671
+ 0%, 100% { opacity: 0.25; }
672
+ 50% { opacity: 0.5; }
673
+ }
674
 
675
+ @keyframes fadeSlideDown {
676
+ from { opacity: 0; transform: translateY(-24px); }
677
+ to { opacity: 1; transform: translateY(0); }
678
+ }
679
 
680
+ .cyber-subtitle {
681
+ font-family: 'Share Tech Mono', monospace !important;
682
+ font-size: 14px !important;
683
+ color: var(--text-dim) !important;
684
+ letter-spacing: 3px !important;
685
+ text-transform: uppercase !important;
686
+ margin-top: 10px !important;
687
+ animation: fadeSlideDown 1s ease 0.3s both;
688
+ }
689
 
690
+ /* Decorative line under title */
691
+ .cyber-divider {
692
+ display: flex;
693
+ align-items: center;
694
+ gap: 12px;
695
+ margin: 20px auto;
696
+ max-width: 600px;
697
+ animation: fadeSlideDown 1s ease 0.5s both;
698
  }
699
 
700
+ .cyber-divider::before,
701
+ .cyber-divider::after {
702
+ content: '';
703
+ flex: 1;
704
+ height: 1px;
705
+ background: linear-gradient(90deg, transparent, var(--neon-cyan), transparent);
706
+ }
707
 
708
+ .cyber-divider-dot {
709
+ width: 6px; height: 6px;
710
+ background: var(--neon-cyan);
711
+ border-radius: 50%;
712
+ box-shadow: 0 0 10px var(--neon-cyan);
713
+ animation: dotPulse 2s ease-in-out infinite;
714
+ }
715
 
716
+ @keyframes dotPulse {
717
+ 0%, 100% { transform: scale(1); opacity: 1; }
718
+ 50% { transform: scale(1.6); opacity: 0.6; }
719
+ }
720
 
721
+ /* =====================================================
722
+ STATUS BADGE
723
+ ===================================================== */
724
+ .status-bar {
725
+ display: flex;
726
+ justify-content: center;
727
+ margin: 10px 0 24px;
728
+ animation: fadeSlideDown 1s ease 0.6s both;
729
  }
730
 
731
+ .status-badge {
732
+ font-family: 'Share Tech Mono', monospace;
733
+ font-size: 12px;
734
+ letter-spacing: 2px;
735
+ padding: 6px 20px;
736
+ border: 1px solid var(--neon-green);
737
+ color: var(--neon-green);
738
+ background: rgba(0, 255, 136, 0.06);
739
+ border-radius: 2px;
740
+ position: relative;
741
+ overflow: hidden;
742
+ text-transform: uppercase;
743
+ }
744
 
745
+ .status-badge::before {
746
+ content: '';
747
+ position: absolute;
748
+ top: 0; left: -100%;
749
+ width: 100%; height: 100%;
750
+ background: linear-gradient(90deg, transparent, rgba(0,255,136,0.15), transparent);
751
+ animation: scanSweep 3s linear infinite;
752
+ }
753
 
754
+ @keyframes scanSweep {
755
+ 0% { left: -100%; }
756
+ 100% { left: 100%; }
757
  }
758
 
759
+ /* =====================================================
760
+ INFO CARDS (Modalities / Models)
761
+ ===================================================== */
762
+ .info-grid {
763
+ display: grid;
764
+ grid-template-columns: 1fr 1fr;
765
+ gap: 16px;
766
+ margin-bottom: 24px;
767
+ animation: fadeSlideDown 1s ease 0.7s both;
768
+ }
769
 
770
+ .cyber-card {
771
+ background: var(--dark-card) !important;
772
+ border: 1px solid var(--border-glow) !important;
773
+ border-radius: 4px !important;
774
+ padding: 20px 24px !important;
775
+ position: relative;
776
+ overflow: hidden;
777
+ transition: border-color 0.3s, box-shadow 0.3s;
778
+ }
779
 
780
+ .cyber-card::before {
781
+ content: '';
782
+ position: absolute;
783
+ top: 0; left: 0;
784
+ width: 3px; height: 100%;
785
+ background: linear-gradient(180deg, var(--neon-cyan), var(--neon-purple));
786
+ box-shadow: 0 0 12px var(--neon-cyan);
787
+ }
788
 
789
+ .cyber-card::after {
790
+ content: '';
791
+ position: absolute;
792
+ top: 0; right: 0;
793
+ width: 60px; height: 60px;
794
+ background: radial-gradient(circle, rgba(0,245,255,0.08) 0%, transparent 70%);
795
  }
796
 
797
+ .cyber-card:hover {
798
+ border-color: rgba(0, 245, 255, 0.5) !important;
799
+ box-shadow: 0 0 30px rgba(0, 245, 255, 0.1) !important;
800
+ }
801
 
802
+ /* =====================================================
803
+ GR PANELS (override Gradio)
804
+ ===================================================== */
805
  .gr-group,
806
  .gr-box,
807
+ .gr-panel,
808
+ .gr-form,
809
+ div[class*="panel"],
810
+ div[class*="block"] {
811
+ background: var(--dark-card) !important;
812
+ border: 1px solid var(--border-glow) !important;
813
+ border-radius: 4px !important;
814
+ box-shadow: none !important;
815
+ backdrop-filter: none !important;
 
 
 
 
 
 
 
816
  }
817
 
818
+ /* =====================================================
819
+ MARKDOWN HEADINGS
820
+ ===================================================== */
821
+ h1, h2, h3, h4 {
822
+ font-family: 'Orbitron', monospace !important;
823
+ font-weight: 700 !important;
824
+ letter-spacing: 2px !important;
825
+ text-transform: uppercase !important;
 
 
826
  }
827
 
828
+ h2 {
829
+ font-size: 16px !important;
830
+ color: var(--neon-cyan) !important;
831
+ border-bottom: 1px solid rgba(0,245,255,0.2) !important;
832
+ padding-bottom: 8px !important;
833
+ margin-bottom: 16px !important;
834
+ }
835
 
836
+ h3 {
837
+ font-size: 14px !important;
838
+ color: var(--text-primary) !important;
839
+ }
 
840
 
841
+ h4 {
842
+ font-size: 12px !important;
843
+ color: var(--text-dim) !important;
844
+ }
845
 
846
+ /* =====================================================
847
+ LABELS / TEXT
848
+ ===================================================== */
849
+ label, .gr-label span, p, li {
850
+ font-family: 'Rajdhani', sans-serif !important;
851
+ color: var(--text-primary) !important;
852
+ font-size: 14px !important;
853
+ font-weight: 600 !important;
854
+ letter-spacing: 1px !important;
855
+ }
856
 
857
+ /* =====================================================
858
+ ANALYZE BUTTON
859
+ ===================================================== */
860
+ .gr-button,
861
+ button[class*="primary"],
862
+ button {
863
+ font-family: 'Orbitron', monospace !important;
864
+ font-size: 13px !important;
865
  font-weight: 700 !important;
866
+ letter-spacing: 3px !important;
867
+ text-transform: uppercase !important;
868
+ background: transparent !important;
869
+ border: 1px solid var(--neon-cyan) !important;
870
+ color: var(--neon-cyan) !important;
871
+ border-radius: 3px !important;
872
+ padding: 14px 32px !important;
873
+ position: relative !important;
874
+ overflow: hidden !important;
875
  transition: all 0.3s ease !important;
876
+ cursor: pointer !important;
 
 
877
  }
878
 
879
+ .gr-button::before,
880
+ button::before {
881
+ content: '' !important;
882
+ position: absolute !important;
883
+ top: 0; left: -100% !important;
884
+ width: 100%; height: 100% !important;
 
 
 
885
  background: linear-gradient(
886
+ 90deg,
887
+ transparent,
888
+ rgba(0, 245, 255, 0.2),
889
+ transparent
890
  ) !important;
891
+ transition: left 0.5s ease !important;
892
  }
893
 
894
+ .gr-button:hover::before,
895
+ button:hover::before {
896
+ left: 100% !important;
 
 
 
 
 
 
897
  }
898
 
899
+ .gr-button:hover,
900
+ button:hover {
901
+ background: rgba(0, 245, 255, 0.1) !important;
902
+ box-shadow:
903
+ 0 0 20px rgba(0, 245, 255, 0.4),
904
+ 0 0 60px rgba(0, 245, 255, 0.15),
905
+ inset 0 0 20px rgba(0, 245, 255, 0.05) !important;
906
+ transform: translateY(-2px) !important;
907
  }
908
 
909
+ .gr-button:active,
910
+ button:active {
911
+ transform: translateY(0px) !important;
912
+ }
913
 
914
+ /* =====================================================
915
+ INPUT / TEXTAREA
916
+ ===================================================== */
917
  textarea,
918
+ input[type="text"],
919
  input {
920
+ font-family: 'Share Tech Mono', monospace !important;
921
+ font-size: 13px !important;
922
+ background: var(--dark-panel) !important;
923
+ border: 1px solid rgba(0, 245, 255, 0.15) !important;
924
+ border-radius: 3px !important;
925
+ color: var(--neon-cyan) !important;
926
+ transition: all 0.3s !important;
927
+ letter-spacing: 0.5px !important;
928
  }
929
 
 
 
930
  textarea:focus,
931
  input:focus {
932
+ border-color: var(--neon-cyan) !important;
933
+ box-shadow: 0 0 15px rgba(0, 245, 255, 0.2) !important;
934
+ outline: none !important;
 
 
935
  }
936
 
937
+ /* =====================================================
938
+ VIDEO COMPONENT
939
+ ===================================================== */
 
940
  video {
941
+ border-radius: 3px !important;
942
+ border: 1px solid rgba(0, 245, 255, 0.2) !important;
 
 
 
943
  box-shadow:
944
+ 0 0 30px rgba(0, 245, 255, 0.1),
945
+ inset 0 0 30px rgba(0, 0, 0, 0.5) !important;
946
  }
947
 
948
+ /* Upload zone */
949
+ .upload-zone,
950
+ div[class*="upload"] {
951
+ border: 1px dashed rgba(0, 245, 255, 0.3) !important;
952
+ background: rgba(0, 245, 255, 0.03) !important;
953
+ border-radius: 4px !important;
954
+ transition: all 0.3s !important;
955
+ }
956
 
957
+ .upload-zone:hover,
958
+ div[class*="upload"]:hover {
959
+ border-color: var(--neon-cyan) !important;
960
+ background: rgba(0, 245, 255, 0.07) !important;
961
+ }
962
 
963
+ /* =====================================================
964
+ LABEL (EMOTION PROBABILITIES)
965
+ ===================================================== */
966
+ .gr-label,
967
+ div[class*="label"] {
968
+ background: var(--dark-panel) !important;
969
+ border: 1px solid rgba(0, 245, 255, 0.12) !important;
970
+ border-radius: 4px !important;
971
+ padding: 12px !important;
972
+ }
973
 
974
+ /* Label bars */
975
+ div[class*="confidence"] span,
976
+ div[class*="bar"],
977
+ .label-bar,
978
+ [class*="Confidence"] {
979
+ background: linear-gradient(
980
+ 90deg,
981
+ rgba(0, 245, 255, 0.15),
982
+ rgba(0, 245, 255, 0.05)
983
+ ) !important;
984
+ border-left: 2px solid var(--neon-cyan) !important;
985
+ border-radius: 0 !important;
986
  }
987
 
988
+ /* =====================================================
989
+ ACCORDION
990
+ ===================================================== */
991
+ .gr-accordion,
992
+ details,
993
+ summary {
994
+ background: var(--dark-card) !important;
995
+ border: 1px solid rgba(0,245,255,0.12) !important;
996
+ border-radius: 4px !important;
997
+ font-family: 'Orbitron', monospace !important;
998
+ font-size: 12px !important;
999
+ letter-spacing: 2px !important;
1000
+ color: var(--text-dim) !important;
1001
+ text-transform: uppercase !important;
1002
+ }
1003
 
1004
+ details summary {
1005
+ padding: 14px 20px !important;
1006
+ cursor: pointer !important;
1007
+ transition: color 0.3s !important;
1008
+ }
1009
 
1010
+ details summary:hover {
1011
+ color: var(--neon-cyan) !important;
1012
+ }
1013
 
1014
+ details[open] summary {
1015
+ color: var(--neon-cyan) !important;
1016
+ border-bottom: 1px solid rgba(0,245,255,0.12) !important;
1017
  }
1018
 
1019
+ /* =====================================================
1020
+ RESULT TEXT (Markdown output)
1021
+ ===================================================== */
1022
+ .result-markdown code,
1023
+ code {
1024
+ font-family: 'Share Tech Mono', monospace !important;
1025
+ background: rgba(0, 245, 255, 0.08) !important;
1026
+ border: 1px solid rgba(0, 245, 255, 0.2) !important;
1027
+ border-radius: 2px !important;
1028
+ color: var(--neon-cyan) !important;
1029
+ padding: 2px 8px !important;
1030
+ font-size: 15px !important;
1031
+ }
1032
 
1033
+ /* =====================================================
1034
+ SEPARATOR / HR
1035
+ ===================================================== */
1036
+ hr {
1037
+ border: none !important;
1038
+ height: 1px !important;
1039
+ background: linear-gradient(
1040
+ 90deg,
1041
+ transparent,
1042
+ rgba(0, 245, 255, 0.3),
1043
+ rgba(191, 0, 255, 0.3),
1044
+ transparent
1045
+ ) !important;
1046
+ margin: 24px 0 !important;
1047
+ }
1048
 
1049
+ /* =====================================================
1050
+ FOOTER
1051
+ ===================================================== */
1052
+ .cyber-footer {
1053
  text-align: center;
1054
+ font-family: 'Share Tech Mono', monospace;
1055
+ font-size: 11px;
1056
+ letter-spacing: 3px;
1057
+ color: var(--text-dim);
1058
+ margin-top: 40px;
1059
+ padding: 20px;
1060
+ text-transform: uppercase;
1061
+ border-top: 1px solid rgba(0,245,255,0.08);
1062
+ position: relative;
1063
+ }
1064
 
1065
+ .cyber-footer::before {
1066
+ content: 'β—ˆ β—ˆ β—ˆ';
1067
+ display: block;
1068
+ color: rgba(0,245,255,0.2);
1069
+ margin-bottom: 10px;
1070
+ letter-spacing: 6px;
1071
+ font-size: 8px;
1072
+ animation: dotPulse 3s ease-in-out infinite;
1073
+ }
1074
 
1075
+ /* =====================================================
1076
+ CORNER DECORATION (applied to main columns)
1077
+ ===================================================== */
1078
+ .corner-decor {
1079
+ position: relative;
1080
  }
1081
 
1082
+ .corner-decor::before,
1083
+ .corner-decor::after {
1084
+ content: '';
1085
+ position: absolute;
1086
+ width: 12px; height: 12px;
1087
+ border-color: var(--neon-cyan);
1088
+ border-style: solid;
1089
+ opacity: 0.5;
1090
+ }
1091
 
1092
+ .corner-decor::before {
1093
+ top: -1px; left: -1px;
1094
+ border-width: 2px 0 0 2px;
1095
+ }
1096
 
1097
+ .corner-decor::after {
1098
+ bottom: -1px; right: -1px;
1099
+ border-width: 0 2px 2px 0;
1100
  }
1101
 
1102
+ /* =====================================================
1103
+ ANIMATED PARTICLES (pure CSS)
1104
+ ===================================================== */
1105
+ .particle-field {
1106
+ position: fixed;
1107
+ inset: 0;
1108
+ pointer-events: none;
1109
+ z-index: 0;
1110
+ overflow: hidden;
1111
+ }
1112
 
1113
+ .particle {
1114
+ position: absolute;
1115
+ width: 2px; height: 2px;
1116
+ background: var(--neon-cyan);
1117
+ border-radius: 50%;
1118
+ opacity: 0;
1119
+ animation: floatParticle linear infinite;
1120
+ }
1121
 
1122
+ .particle:nth-child(1) { left: 10%; animation-duration: 12s; animation-delay: 0s; width: 1px; height: 1px; }
1123
+ .particle:nth-child(2) { left: 20%; animation-duration: 18s; animation-delay: 2s; background: var(--neon-purple); }
1124
+ .particle:nth-child(3) { left: 35%; animation-duration: 14s; animation-delay: 4s; }
1125
+ .particle:nth-child(4) { left: 50%; animation-duration: 20s; animation-delay: 1s; background: var(--neon-green); width: 1px; height: 1px; }
1126
+ .particle:nth-child(5) { left: 65%; animation-duration: 16s; animation-delay: 6s; }
1127
+ .particle:nth-child(6) { left: 78%; animation-duration: 13s; animation-delay: 3s; background: var(--neon-pink); }
1128
+ .particle:nth-child(7) { left: 88%; animation-duration: 19s; animation-delay: 5s; width: 1px; height: 1px; }
1129
+ .particle:nth-child(8) { left: 45%; animation-duration: 15s; animation-delay: 7s; background: var(--neon-purple); }
1130
+ .particle:nth-child(9) { left: 55%; animation-duration: 17s; animation-delay: 0.5s; }
1131
+ .particle:nth-child(10) { left: 72%; animation-duration: 11s; animation-delay: 8s; background: var(--neon-green); width: 1px; height: 1px; }
1132
+ .particle:nth-child(11) { left: 5%; animation-duration: 22s; animation-delay: 2.5s; background: var(--neon-cyan); }
1133
+ .particle:nth-child(12) { left: 92%; animation-duration: 14s; animation-delay: 9s; background: var(--neon-pink); width: 1px; height: 1px; }
1134
+
1135
+ @keyframes floatParticle {
1136
+ 0% { bottom: -10px; opacity: 0; transform: translateX(0); }
1137
+ 10% { opacity: 0.6; }
1138
+ 90% { opacity: 0.3; }
1139
+ 100% { bottom: 105vh; opacity: 0; transform: translateX(30px); }
1140
  }
1141
 
1142
+ /* =====================================================
1143
+ SCROLLBAR
1144
+ ===================================================== */
1145
+ ::-webkit-scrollbar { width: 6px; }
1146
+ ::-webkit-scrollbar-track { background: var(--dark-bg); }
1147
+ ::-webkit-scrollbar-thumb {
1148
+ background: linear-gradient(180deg, var(--neon-cyan), var(--neon-purple));
1149
+ border-radius: 3px;
1150
+ }
1151
 
1152
+ /* =====================================================
1153
+ RESPONSIVE TWEAKS
1154
+ ===================================================== */
1155
+ @media (max-width: 768px) {
1156
+ .info-grid { grid-template-columns: 1fr; }
1157
+ .cyber-title { font-size: 26px !important; letter-spacing: 3px !important; }
1158
  }
1159
  """
1160
 
 
1164
 
1165
  with gr.Blocks(
1166
  title="Multimodal Emotion Recognition",
1167
+ theme=gr.themes.Base(
1168
+ primary_hue="cyan",
1169
+ neutral_hue="slate",
1170
+ font=[gr.themes.GoogleFont("Rajdhani"), "sans-serif"],
1171
+ ),
1172
  css=custom_css
1173
  ) as demo:
1174
 
1175
+ # ── Floating particles ──────────────────────────────
 
1176
  gr.HTML("""
1177
+ <div class="particle-field">
1178
+ <div class="particle"></div>
1179
+ <div class="particle"></div>
1180
+ <div class="particle"></div>
1181
+ <div class="particle"></div>
1182
+ <div class="particle"></div>
1183
+ <div class="particle"></div>
1184
+ <div class="particle"></div>
1185
+ <div class="particle"></div>
1186
+ <div class="particle"></div>
1187
+ <div class="particle"></div>
1188
+ <div class="particle"></div>
1189
+ <div class="particle"></div>
1190
  </div>
1191
+ """)
1192
 
1193
+ # ── Header ──────────────────────────────────────────
1194
+ gr.HTML(f"""
1195
+ <div class="cyber-header">
1196
+ <div class="cyber-title" data-text="MULTIMODAL EMOTION RECOGNITION">
1197
+ MULTIMODAL EMOTION RECOGNITION
1198
+ </div>
1199
+ <div class="cyber-subtitle">
1200
+ Real-Time Neural Analysis &nbsp;Β·&nbsp; Audio &nbsp;/&nbsp; Text &nbsp;/&nbsp; Video Fusion
1201
+ </div>
1202
+ <div class="cyber-divider">
1203
+ <div class="cyber-divider-dot"></div>
1204
+ <div class="cyber-divider-dot" style="animation-delay:0.4s"></div>
1205
+ <div class="cyber-divider-dot" style="animation-delay:0.8s"></div>
1206
+ </div>
1207
+ <div class="status-bar">
1208
+ <div class="status-badge" style="border-color:{gpu_color}; color:{gpu_color}; background:rgba(0,255,136,0.06);">
1209
+ β—‰ &nbsp; SYSTEM STATUS: {gpu_status}
1210
+ </div>
1211
+ </div>
1212
  </div>
1213
  """)
1214
 
1215
+ # ── Info Cards ──────────────────────────────────────
1216
+ gr.HTML("""
1217
+ <div class="info-grid">
1218
+ <div class="cyber-card">
1219
+ <h4 style="font-family:'Orbitron',monospace;font-size:10px;letter-spacing:3px;color:#4a7a8a;margin:0 0 12px;">MODALITIES</h4>
1220
+ <div style="font-family:'Share Tech Mono',monospace;font-size:13px;line-height:2;color:#e0f7ff;">
1221
+ <div>β—ˆ &nbsp;Audio Waveform Analysis</div>
1222
+ <div>β—ˆ &nbsp;Speech Transcription (Whisper)</div>
1223
+ <div>β—ˆ &nbsp;Facial Expression Mapping</div>
1224
+ </div>
1225
+ </div>
1226
+ <div class="cyber-card" style="--neon-cyan:#bf00ff;">
1227
+ <h4 style="font-family:'Orbitron',monospace;font-size:10px;letter-spacing:3px;color:#4a7a8a;margin:0 0 12px;">NEURAL MODELS</h4>
1228
+ <div style="font-family:'Share Tech Mono',monospace;font-size:13px;line-height:2;color:#e0f7ff;">
1229
+ <div>β—ˆ &nbsp;Wav2Vec2 &nbsp;Β·&nbsp; BERT &nbsp;Β·&nbsp; ResNet18</div>
1230
+ <div>β—ˆ &nbsp;Whisper ASR</div>
1231
+ <div>β—ˆ &nbsp;Hybrid Bimodal Fusion (HBF)</div>
1232
+ </div>
1233
+ </div>
1234
+ </div>
1235
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1236
 
1237
+ gr.HTML('<hr/>')
1238
 
1239
+ # ── Main Analysis Section ───────────────────────────
1240
  with gr.Row(equal_height=True):
1241
 
1242
+ # LEFT: Upload
 
1243
  with gr.Column(scale=1):
1244
 
1245
+ gr.Markdown("## β—§ INPUT STREAM")
1246
 
1247
  video_input = gr.Video(
1248
+ label="Video Input",
1249
+ height=360
1250
  )
1251
 
1252
  predict_btn = gr.Button(
1253
+ "⬑ INITIALIZE ANALYSIS",
1254
  variant="primary",
1255
  size="lg"
1256
  )
1257
 
1258
+ # RIGHT: Results
 
1259
  with gr.Column(scale=1):
1260
 
1261
+ gr.Markdown("## β—¨ ANALYSIS OUTPUT")
1262
 
1263
  result_text = gr.Markdown(
1264
+ value="""
1265
+ > `AWAITING INPUT` β€” Upload a video file and trigger analysis to begin neural processing.
1266
+ """
1267
  )
1268
 
1269
  result_output = gr.Label(
1270
+ label="Emotion Probability Distribution",
1271
  num_top_classes=4
1272
  )
1273
 
1274
  transcription_output = gr.Textbox(
1275
+ label="β—ˆ Transcribed Speech",
1276
+ lines=5,
1277
+ interactive=False,
1278
+ placeholder="Speech transcription will appear here after analysis..."
1279
  )
1280
 
1281
+ gr.HTML('<hr/>')
1282
 
1283
+ # ── About Accordion ─────────────────────────────────
1284
+ with gr.Accordion("β—† SYSTEM ARCHITECTURE", open=False):
 
 
 
 
1285
 
1286
  gr.Markdown("""
1287
+ ### Neural Pipeline
 
 
 
 
 
 
 
 
 
 
 
 
 
1288
 
1289
+ This system fuses three independent encoders through a **Hybrid Bimodal Fusion (HBF)** block:
1290
 
1291
+ - **Wav2Vec2** β€” Extracts deep acoustic features from the raw audio waveform
1292
+ - **BERT** β€” Understands semantic and emotional tone from transcribed speech
1293
+ - **ResNet18** β€” Captures facial micro-expressions across sampled video frames
1294
+ - **HBF Block** β€” Iteratively aligns all three modalities over 6 fusion layers
1295
+
1296
+ ### Detectable Emotions
1297
+
1298
+ `ANGRY` &nbsp; `HAPPY` &nbsp; `NEUTRAL` &nbsp; `SAD`
1299
+ """)
1300
+
1301
+ # ── Footer ──────────────────────────────────────────
1302
  gr.HTML("""
1303
+ <div class="cyber-footer">
1304
+ Built on PyTorch &nbsp;Β·&nbsp; HuggingFace Transformers &nbsp;Β·&nbsp; OpenAI Whisper &nbsp;Β·&nbsp; Gradio
1305
  </div>
1306
  """)
1307
 
1308
+ # ── Button Action ────────────────────────────────────
 
1309
  predict_btn.click(
1310
  fn=predict_emotion,
1311
  inputs=[video_input],