koyelog commited on
Commit
72e9984
Β·
verified Β·
1 Parent(s): 2c6864e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +461 -269
app.py CHANGED
@@ -1,6 +1,7 @@
1
  # ==========================================
2
- # BACKEND: Emotion Detection API
3
- # Fetches model from koyelog/face
 
4
  # ==========================================
5
 
6
  import gradio as gr
@@ -8,377 +9,568 @@ import torch
8
  from transformers import ViTForImageClassification, ViTImageProcessor
9
  from PIL import Image
10
  import numpy as np
 
11
 
12
- print("="*60)
13
- print("🎭 EMOTION DETECTION BACKEND")
14
- print("="*60)
15
 
16
  # ===== CONFIGURATION =====
17
- MODEL_ID = "koyelog/face" # βœ… Your HuggingFace model
18
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
19
 
20
- print(f"πŸ“¦ Loading model from: {MODEL_ID}")
21
  print(f"πŸ–₯️ Device: {DEVICE}")
 
 
 
 
22
 
23
- # ===== LOAD MODEL =====
24
  try:
25
- model = ViTForImageClassification.from_pretrained(MODEL_ID)
26
- processor = ViTImageProcessor.from_pretrained(MODEL_ID)
 
 
 
 
 
 
27
  model.to(DEVICE)
28
  model.eval()
29
  print("βœ… Model loaded successfully!")
 
 
30
  except Exception as e:
31
- print(f"❌ Error loading model: {e}")
32
  raise
33
 
34
- # ===== EMOTION MAPPING =====
35
  EMOTIONS = {
36
- 0: {'name': 'Angry', 'emoji': '😠', 'color': '#ff4444'},
37
- 1: {'name': 'Disgust', 'emoji': '🀒', 'color': '#44ff44'},
38
- 2: {'name': 'Fear', 'emoji': '😨', 'color': '#9944ff'},
39
- 3: {'name': 'Happy', 'emoji': '😊', 'color': '#ffdd44'},
40
- 4: {'name': 'Sad', 'emoji': '😒', 'color': '#4444ff'},
41
- 5: {'name': 'Surprise', 'emoji': '😲', 'color': '#ff44ff'},
42
- 6: {'name': 'Neutral', 'emoji': '😐', 'color': '#888888'}
43
  }
44
 
45
- # ===== CORE PREDICTION FUNCTION =====
 
 
 
 
 
46
  def predict_emotion(image):
47
  """
48
- Main prediction function
49
- Input: PIL Image or numpy array
50
- Output: emotion_dict, html_result
 
 
 
51
  """
52
 
53
  if image is None:
54
- return {}, "<p style='color: red;'>⚠️ No image provided!</p>"
 
 
 
 
 
55
 
56
  try:
57
- # Convert to PIL Image if numpy array
58
  if isinstance(image, np.ndarray):
59
  image = Image.fromarray(image)
60
 
61
- # Ensure RGB format
62
  if image.mode != 'RGB':
63
  image = image.convert('RGB')
64
 
65
- print(f"πŸ“Έ Processing image: {image.size}")
 
66
 
67
- # Preprocess image
68
  inputs = processor(images=image, return_tensors="pt")
69
  inputs = {k: v.to(DEVICE) for k, v in inputs.items()}
70
 
71
- # Run inference
72
- with torch.no_grad():
73
- outputs = model(**inputs)
74
- logits = outputs.logits
75
- probs = torch.nn.functional.softmax(logits, dim=-1)[0].cpu()
76
 
77
  # Get predictions
78
  predicted_id = torch.argmax(probs).item()
79
  confidence = probs[predicted_id].item()
80
 
81
- # Get emotion info
82
- emotion_info = EMOTIONS[predicted_id]
83
- emotion_name = emotion_info['name']
84
- emoji = emotion_info['emoji']
85
- color = emotion_info['color']
86
 
87
- print(f"🎯 Prediction: {emoji} {emotion_name} ({confidence*100:.2f}%)")
 
 
 
 
 
88
 
89
- # Create results dictionary for Gradio Label component
90
  results = {
91
  f"{EMOTIONS[i]['emoji']} {EMOTIONS[i]['name']}": float(probs[i])
92
  for i in range(len(EMOTIONS))
93
  }
94
 
95
- # Create beautiful HTML output
96
- html_output = create_result_html(emotion_name, emoji, color, confidence, probs)
 
 
 
 
 
 
 
 
 
97
 
98
- return results, html_output
99
-
100
  except Exception as e:
101
- print(f"❌ Error during prediction: {e}")
102
- return {}, f"<p style='color: red;'>❌ Error: {str(e)}</p>"
103
-
 
 
 
 
 
 
 
 
 
104
 
105
- # ===== HTML RESULT GENERATOR =====
106
- def create_result_html(emotion_name, emoji, color, confidence, probs):
107
  """Generate beautiful HTML result display"""
108
 
109
- # Create probability bars
110
- prob_bars = ""
111
- for idx in range(len(EMOTIONS)):
112
  emo = EMOTIONS[idx]
113
  prob = probs[idx].item()
114
- bar_width = prob * 100
 
115
 
116
- prob_bars += f"""
117
- <div style="margin: 10px 0;">
118
- <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
119
- <span style="font-weight: 500;">{emo['emoji']} {emo['name']}</span>
120
- <span style="color: #666;">{prob*100:.1f}%</span>
 
 
 
121
  </div>
122
- <div style="width: 100%; background: #e0e0e0; border-radius: 10px; height: 8px; overflow: hidden;">
123
- <div style="width: {bar_width}%; background: {emo['color']}; height: 100%; transition: width 0.5s ease;"></div>
124
  </div>
125
  </div>
126
  """
127
 
 
128
  html = f"""
129
- <div style="font-family: 'Segoe UI', Arial, sans-serif;">
 
130
  <!-- Main Result Card -->
131
- <div style="
132
- text-align: center;
133
- padding: 40px;
134
- background: linear-gradient(135deg, {color}15, {color}30);
135
- border-radius: 20px;
136
- box-shadow: 0 8px 32px rgba(0,0,0,0.1);
137
  margin-bottom: 30px;
138
- ">
139
- <div style="font-size: 100px; margin: 0; animation: bounce 1s ease infinite;">
 
 
 
 
 
 
140
  {emoji}
141
  </div>
142
- <h2 style="
143
- color: {color};
144
- font-size: 42px;
145
- margin: 20px 0 10px 0;
146
- font-weight: bold;
147
- text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
148
- ">
149
- {emotion_name}
150
- </h2>
151
- <p style="font-size: 24px; color: #555; margin: 10px;">
152
- Confidence: <strong>{confidence*100:.2f}%</strong>
 
 
 
 
 
 
 
 
153
  </p>
154
 
155
- <!-- Confidence Bar -->
156
- <div style="
157
- width: 100%;
158
- background: #ddd;
159
- border-radius: 15px;
160
- margin: 20px auto;
161
- height: 40px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  overflow: hidden;
163
- box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);
164
- max-width: 400px;
165
- ">
166
- <div style="
167
- width: {confidence*100}%;
168
- background: linear-gradient(90deg, {color}, {color}cc);
169
- height: 100%;
170
- border-radius: 15px;
171
- transition: width 1s ease;
 
172
  display: flex;
173
  align-items: center;
174
  justify-content: center;
175
- color: white;
176
- font-weight: bold;
177
- ">
178
- {confidence*100:.1f}%
 
 
 
 
 
 
179
  </div>
180
  </div>
181
  </div>
182
 
183
- <!-- All Probabilities Section -->
184
- <div style="
185
- background: white;
186
- padding: 25px;
187
- border-radius: 15px;
188
- box-shadow: 0 4px 16px rgba(0,0,0,0.08);
189
- ">
190
- <h3 style="margin-top: 0; color: #333; font-size: 20px;">
191
- πŸ“Š Detailed Emotion Breakdown
192
- </h3>
193
- {prob_bars}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  </div>
195
  </div>
196
 
197
  <style>
198
- @keyframes bounce {{
199
- 0%, 100% {{ transform: translateY(0); }}
200
- 50% {{ transform: translateY(-15px); }}
 
 
 
 
 
 
 
 
 
 
 
 
201
  }}
202
  </style>
203
  """
204
 
205
  return html
206
 
207
-
208
  # ===== GRADIO INTERFACE =====
209
- def create_interface():
210
- """Create Gradio UI"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
- with gr.Blocks(
213
- theme=gr.themes.Soft(
214
- primary_hue="purple",
215
- secondary_hue="pink",
216
- ),
217
- css="""
218
- .gradio-container {
219
- font-family: 'Segoe UI', 'Arial', sans-serif !important;
220
- }
221
- .main-header {
222
- text-align: center;
223
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
224
- color: white;
225
- padding: 50px 20px;
226
- border-radius: 20px;
227
- margin-bottom: 30px;
228
- box-shadow: 0 10px 40px rgba(0,0,0,0.2);
229
- }
230
- .tab-nav button {
231
- font-size: 16px !important;
232
- font-weight: 600 !important;
233
- }
234
- footer {visibility: hidden;}
235
- """
236
- ) as demo:
237
-
238
- # Header
239
- gr.HTML("""
240
- <div class="main-header">
241
- <h1 style="font-size: 56px; margin: 0; font-weight: bold;">
242
- 🎭 AI Emotion Detector
243
- </h1>
244
- <p style="font-size: 22px; margin-top: 15px; opacity: 0.95;">
245
- Powered by Vision Transformer | 98.80% Accuracy
246
- </p>
247
- <p style="font-size: 16px; margin-top: 10px; opacity: 0.85;">
248
- Model: <strong>koyelog/face</strong> | 7 Emotions | Real-time Detection
249
- </p>
250
  </div>
251
- """)
 
 
 
252
 
253
- with gr.Tabs():
 
 
 
 
 
254
 
255
- # ===== TAB 1: WEBCAM =====
256
- with gr.Tab("πŸ“Ή Live Webcam", id="webcam"):
257
- gr.Markdown("""
258
- ### πŸŽ₯ Real-time Emotion Detection
259
- Click the camera button below to capture your face and detect your emotion!
260
- """)
261
-
262
- with gr.Row():
263
- with gr.Column(scale=1):
264
- webcam_input = gr.Image(
265
- sources=["webcam"],
266
- type="pil",
267
- label="πŸ“Έ Capture Your Face",
268
- streaming=False,
269
- mirror_webcam=True
270
- )
271
- webcam_btn = gr.Button(
272
- "πŸ” Detect My Emotion",
273
- variant="primary",
274
- size="lg"
275
- )
276
-
277
- with gr.Column(scale=1):
278
- webcam_html = gr.HTML(label="🎯 Emotion Result")
279
- webcam_label = gr.Label(
280
- label="πŸ“Š All Emotion Probabilities",
281
- num_top_classes=7
282
- )
283
 
284
- webcam_btn.click(
285
- fn=predict_emotion,
286
- inputs=webcam_input,
287
- outputs=[webcam_label, webcam_html]
288
- )
 
289
 
290
- # ===== TAB 2: IMAGE UPLOAD =====
291
- with gr.Tab("πŸ–ΌοΈ Upload Image", id="upload"):
292
- gr.Markdown("""
293
- ### πŸ“€ Upload or Drag & Drop
294
- Upload any face image to detect emotions instantly!
295
- """)
296
-
297
- with gr.Row():
298
- with gr.Column(scale=1):
299
- image_input = gr.Image(
300
- type="pil",
301
- label="πŸ–ΌοΈ Upload Face Image",
302
- sources=["upload", "clipboard"]
303
- )
304
- image_btn = gr.Button(
305
- "πŸ” Detect Emotion",
306
- variant="primary",
307
- size="lg"
308
- )
309
-
310
- with gr.Column(scale=1):
311
- image_html = gr.HTML(label="🎯 Emotion Result")
312
- image_label = gr.Label(
313
- label="πŸ“Š All Emotion Probabilities",
314
- num_top_classes=7
315
- )
316
-
317
- image_btn.click(
318
- fn=predict_emotion,
319
- inputs=image_input,
320
- outputs=[image_label, image_html]
321
- )
322
 
323
- # ===== FOOTER =====
324
- gr.HTML("""
325
- <div style="
326
- text-align: center;
327
- margin-top: 50px;
328
- padding: 40px;
329
- background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
330
- border-radius: 20px;
331
- ">
332
- <h3 style="color: #333; margin-bottom: 20px;">πŸ“Š Model Information</h3>
 
 
 
 
 
 
 
 
 
333
 
334
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0;">
335
- <div>
336
- <p style="font-weight: bold; color: #667eea;">Model</p>
337
- <p>koyelog/face</p>
338
- </div>
339
- <div>
340
- <p style="font-weight: bold; color: #667eea;">Architecture</p>
341
- <p>Vision Transformer (ViT)</p>
342
- </div>
343
- <div>
344
- <p style="font-weight: bold; color: #667eea;">Parameters</p>
345
- <p>85.8 Million</p>
346
- </div>
347
- <div>
348
- <p style="font-weight: bold; color: #667eea;">Accuracy</p>
349
- <p>Train: 99.29% | Val: 98.80%</p>
350
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  </div>
352
-
353
- <p style="margin: 20px 0; font-size: 18px;">
354
- <strong>Detectable Emotions:</strong><br>
355
- 😠 Angry | 🀒 Disgust | 😨 Fear | 😊 Happy | 😒 Sad | 😲 Surprise | 😐 Neutral
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  </p>
357
-
358
- <p style="color: #666; font-size: 14px; margin-top: 30px;">
359
- ⚠️ <strong>Best Results:</strong> Front-facing faces | Good lighting | Single person | Clear expressions
360
- </p>
361
-
362
- <p style="color: #999; font-size: 13px; margin-top: 25px;">
363
- Created by <strong>Koyeliya Ghosh</strong> | MIT License<br>
364
- <a href="https://huggingface.co/koyelog/face" target="_blank" style="color: #667eea;">View Model on HuggingFace</a>
365
  </p>
366
  </div>
367
- """)
368
-
369
- return demo
370
-
 
 
 
 
 
 
 
 
 
371
 
372
  # ===== LAUNCH =====
373
  if __name__ == "__main__":
374
- print("\n" + "="*60)
375
  print("πŸš€ LAUNCHING EMOTION DETECTION APP")
376
- print("="*60)
 
 
 
377
 
378
- demo = create_interface()
379
  demo.launch(
380
  server_name="0.0.0.0",
381
  server_port=7860,
382
  share=False,
383
- show_error=True
 
384
  )
 
1
  # ==========================================
2
+ # EMOTION DETECTION WEB APP
3
+ # Model: koyelog/face
4
+ # Backend + Frontend with Gradio
5
  # ==========================================
6
 
7
  import gradio as gr
 
9
  from transformers import ViTForImageClassification, ViTImageProcessor
10
  from PIL import Image
11
  import numpy as np
12
+ import os
13
 
14
+ print("="*70)
15
+ print("🎭 AI EMOTION DETECTOR - INITIALIZING")
16
+ print("="*70)
17
 
18
  # ===== CONFIGURATION =====
19
+ MODEL_ID = "koyelog/face"
20
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
21
 
22
+ print(f"\nπŸ“¦ Model ID: {MODEL_ID}")
23
  print(f"πŸ–₯️ Device: {DEVICE}")
24
+ print(f"πŸ’Ύ PyTorch Version: {torch.__version__}")
25
+
26
+ # ===== LOAD MODEL & PROCESSOR =====
27
+ print("\n⏳ Loading model from HuggingFace...")
28
 
 
29
  try:
30
+ model = ViTForImageClassification.from_pretrained(
31
+ MODEL_ID,
32
+ cache_dir="./model_cache"
33
+ )
34
+ processor = ViTImageProcessor.from_pretrained(
35
+ MODEL_ID,
36
+ cache_dir="./model_cache"
37
+ )
38
  model.to(DEVICE)
39
  model.eval()
40
  print("βœ… Model loaded successfully!")
41
+ print(f"πŸ“Š Model Parameters: {sum(p.numel() for p in model.parameters()):,}")
42
+
43
  except Exception as e:
44
+ print(f"❌ ERROR loading model: {e}")
45
  raise
46
 
47
+ # ===== EMOTION CONFIGURATION =====
48
  EMOTIONS = {
49
+ 0: {'name': 'Angry', 'emoji': '😠', 'color': '#ff4444', 'description': 'Showing anger or frustration'},
50
+ 1: {'name': 'Disgust', 'emoji': '🀒', 'color': '#44ff44', 'description': 'Expressing disgust or dislike'},
51
+ 2: {'name': 'Fear', 'emoji': '😨', 'color': '#9944ff', 'description': 'Showing fear or anxiety'},
52
+ 3: {'name': 'Happy', 'emoji': '😊', 'color': '#ffdd44', 'description': 'Expressing happiness or joy'},
53
+ 4: {'name': 'Sad', 'emoji': '😒', 'color': '#4444ff', 'description': 'Showing sadness or sorrow'},
54
+ 5: {'name': 'Surprise', 'emoji': '😲', 'color': '#ff44ff', 'description': 'Expressing surprise or shock'},
55
+ 6: {'name': 'Neutral', 'emoji': '😐', 'color': '#888888', 'description': 'No strong emotion detected'}
56
  }
57
 
58
+ print(f"\n🎭 Loaded {len(EMOTIONS)} emotion classes:")
59
+ for idx, emo in EMOTIONS.items():
60
+ print(f" {idx}: {emo['emoji']} {emo['name']}")
61
+
62
+ # ===== PREDICTION FUNCTION =====
63
+ @torch.no_grad()
64
  def predict_emotion(image):
65
  """
66
+ Predict emotion from image
67
+ Args:
68
+ image: PIL Image or numpy array
69
+ Returns:
70
+ results_dict: Dictionary for Gradio Label
71
+ html_output: Formatted HTML result
72
  """
73
 
74
  if image is None:
75
+ return None, """
76
+ <div style='text-align: center; padding: 40px; color: #ff4444;'>
77
+ <h2>⚠️ No Image Provided</h2>
78
+ <p>Please upload an image or use webcam to capture!</p>
79
+ </div>
80
+ """
81
 
82
  try:
83
+ # Convert numpy to PIL if needed
84
  if isinstance(image, np.ndarray):
85
  image = Image.fromarray(image)
86
 
87
+ # Convert to RGB
88
  if image.mode != 'RGB':
89
  image = image.convert('RGB')
90
 
91
+ original_size = image.size
92
+ print(f"\nπŸ“Έ Processing image: {original_size[0]}x{original_size[1]}")
93
 
94
+ # Preprocess
95
  inputs = processor(images=image, return_tensors="pt")
96
  inputs = {k: v.to(DEVICE) for k, v in inputs.items()}
97
 
98
+ # Inference
99
+ outputs = model(**inputs)
100
+ logits = outputs.logits
101
+ probs = torch.nn.functional.softmax(logits, dim=-1)[0].cpu()
 
102
 
103
  # Get predictions
104
  predicted_id = torch.argmax(probs).item()
105
  confidence = probs[predicted_id].item()
106
 
107
+ # Get emotion details
108
+ emotion = EMOTIONS[predicted_id]
 
 
 
109
 
110
+ print(f"🎯 Prediction: {emotion['emoji']} {emotion['name']}")
111
+ print(f"πŸ“Š Confidence: {confidence*100:.2f}%")
112
+ print(f"πŸ“ˆ Top 3 emotions:")
113
+ top3_indices = torch.topk(probs, 3).indices
114
+ for idx in top3_indices:
115
+ print(f" {EMOTIONS[idx.item()]['emoji']} {EMOTIONS[idx.item()]['name']}: {probs[idx]*100:.2f}%")
116
 
117
+ # Format results for Gradio Label component
118
  results = {
119
  f"{EMOTIONS[i]['emoji']} {EMOTIONS[i]['name']}": float(probs[i])
120
  for i in range(len(EMOTIONS))
121
  }
122
 
123
+ # Generate HTML output
124
+ html = generate_result_html(
125
+ emotion['name'],
126
+ emotion['emoji'],
127
+ emotion['color'],
128
+ emotion['description'],
129
+ confidence,
130
+ probs
131
+ )
132
+
133
+ return results, html
134
 
 
 
135
  except Exception as e:
136
+ print(f"❌ ERROR during prediction: {e}")
137
+ import traceback
138
+ traceback.print_exc()
139
+
140
+ error_html = f"""
141
+ <div style='text-align: center; padding: 40px; background: #ffe6e6; border-radius: 15px;'>
142
+ <h2 style='color: #ff4444;'>❌ Prediction Error</h2>
143
+ <p style='color: #666;'>{str(e)}</p>
144
+ <p style='color: #999; font-size: 0.9em;'>Please try a different image</p>
145
+ </div>
146
+ """
147
+ return None, error_html
148
 
149
+ # ===== HTML GENERATOR =====
150
+ def generate_result_html(name, emoji, color, description, confidence, probs):
151
  """Generate beautiful HTML result display"""
152
 
153
+ # Calculate probability bars HTML
154
+ bars_html = ""
155
+ for idx in sorted(range(len(EMOTIONS)), key=lambda i: probs[i], reverse=True):
156
  emo = EMOTIONS[idx]
157
  prob = probs[idx].item()
158
+ percentage = prob * 100
159
+ bar_width = min(percentage, 100)
160
 
161
+ bars_html += f"""
162
+ <div style='margin: 12px 0;'>
163
+ <div style='display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;'>
164
+ <div style='display: flex; align-items: center; gap: 10px;'>
165
+ <span style='font-size: 1.8em;'>{emo['emoji']}</span>
166
+ <span style='font-weight: 600; color: #333;'>{emo['name']}</span>
167
+ </div>
168
+ <span style='font-weight: 700; color: {emo['color']}; font-size: 1.1em;'>{percentage:.1f}%</span>
169
  </div>
170
+ <div style='width: 100%; background: #e9ecef; border-radius: 10px; height: 12px; overflow: hidden; box-shadow: inset 0 2px 4px rgba(0,0,0,0.06);'>
171
+ <div style='width: {bar_width}%; background: linear-gradient(90deg, {emo['color']}, {emo['color']}dd); height: 100%; transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1); border-radius: 10px;'></div>
172
  </div>
173
  </div>
174
  """
175
 
176
+ # Main HTML
177
  html = f"""
178
+ <div style='font-family: "Segoe UI", -apple-system, BlinkMacSystemFont, sans-serif; max-width: 100%;'>
179
+
180
  <!-- Main Result Card -->
181
+ <div style='
182
+ text-align: center;
183
+ padding: 50px 30px;
184
+ background: linear-gradient(135deg, {color}18 0%, {color}30 100%);
185
+ border-radius: 25px;
186
+ box-shadow: 0 10px 40px rgba(0,0,0,0.12);
187
  margin-bottom: 30px;
188
+ border: 2px solid {color}40;
189
+ '>
190
+ <div style='
191
+ font-size: 120px;
192
+ margin: 0 0 20px 0;
193
+ animation: bounceIn 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
194
+ display: inline-block;
195
+ '>
196
  {emoji}
197
  </div>
198
+
199
+ <h1 style='
200
+ color: {color};
201
+ font-size: 3.5em;
202
+ margin: 20px 0 10px 0;
203
+ font-weight: 800;
204
+ text-shadow: 2px 2px 8px rgba(0,0,0,0.1);
205
+ letter-spacing: -1px;
206
+ '>
207
+ {name}
208
+ </h1>
209
+
210
+ <p style='
211
+ font-size: 1.3em;
212
+ color: #555;
213
+ margin: 15px 0;
214
+ font-weight: 500;
215
+ '>
216
+ {description}
217
  </p>
218
 
219
+ <div style='
220
+ display: inline-flex;
221
+ align-items: center;
222
+ gap: 15px;
223
+ margin: 25px 0;
224
+ padding: 15px 35px;
225
+ background: white;
226
+ border-radius: 50px;
227
+ box-shadow: 0 4px 20px rgba(0,0,0,0.1);
228
+ '>
229
+ <span style='font-size: 1.2em; color: #666;'>Confidence:</span>
230
+ <span style='font-size: 2em; font-weight: 800; color: {color};'>{confidence*100:.1f}%</span>
231
+ </div>
232
+
233
+ <!-- Animated Confidence Bar -->
234
+ <div style='
235
+ width: 100%;
236
+ max-width: 500px;
237
+ height: 50px;
238
+ background: #e9ecef;
239
+ border-radius: 25px;
240
  overflow: hidden;
241
+ margin: 30px auto 0;
242
+ box-shadow: inset 0 4px 8px rgba(0,0,0,0.1);
243
+ position: relative;
244
+ '>
245
+ <div style='
246
+ width: {confidence*100}%;
247
+ height: 100%;
248
+ background: linear-gradient(90deg, {color}, {color}cc);
249
+ border-radius: 25px;
250
+ transition: width 1.5s cubic-bezier(0.4, 0, 0.2, 1);
251
  display: flex;
252
  align-items: center;
253
  justify-content: center;
254
+ box-shadow: 0 0 20px {color}80;
255
+ '>
256
+ <span style='
257
+ color: white;
258
+ font-weight: 800;
259
+ font-size: 1.3em;
260
+ text-shadow: 0 2px 4px rgba(0,0,0,0.3);
261
+ '>
262
+ {confidence*100:.1f}%
263
+ </span>
264
  </div>
265
  </div>
266
  </div>
267
 
268
+ <!-- Detailed Breakdown -->
269
+ <div style='
270
+ background: white;
271
+ padding: 35px;
272
+ border-radius: 20px;
273
+ box-shadow: 0 8px 32px rgba(0,0,0,0.08);
274
+ border: 1px solid #e9ecef;
275
+ '>
276
+ <h2 style='
277
+ margin: 0 0 25px 0;
278
+ color: #333;
279
+ font-size: 1.8em;
280
+ font-weight: 700;
281
+ display: flex;
282
+ align-items: center;
283
+ gap: 10px;
284
+ '>
285
+ πŸ“Š Detailed Emotion Analysis
286
+ </h2>
287
+
288
+ {bars_html}
289
+ </div>
290
+
291
+ <!-- Model Info Footer -->
292
+ <div style='
293
+ margin-top: 25px;
294
+ padding: 20px;
295
+ background: linear-gradient(135deg, #f8f9fa, #e9ecef);
296
+ border-radius: 15px;
297
+ text-align: center;
298
+ font-size: 0.9em;
299
+ color: #666;
300
+ '>
301
+ <p style='margin: 5px 0;'>
302
+ <strong>Model:</strong> koyelog/face (Vision Transformer) |
303
+ <strong>Accuracy:</strong> 98.80% |
304
+ <strong>Parameters:</strong> 85.8M
305
+ </p>
306
  </div>
307
  </div>
308
 
309
  <style>
310
+ @keyframes bounceIn {{
311
+ 0% {{
312
+ opacity: 0;
313
+ transform: scale(0.3) translateY(-50px);
314
+ }}
315
+ 50% {{
316
+ opacity: 1;
317
+ transform: scale(1.05);
318
+ }}
319
+ 70% {{
320
+ transform: scale(0.9);
321
+ }}
322
+ 100% {{
323
+ transform: scale(1);
324
+ }}
325
  }}
326
  </style>
327
  """
328
 
329
  return html
330
 
 
331
  # ===== GRADIO INTERFACE =====
332
+ print("\n🎨 Building Gradio interface...")
333
+
334
+ # Custom CSS
335
+ custom_css = """
336
+ .gradio-container {
337
+ font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif !important;
338
+ max-width: 1400px !important;
339
+ }
340
+
341
+ .main-header {
342
+ text-align: center;
343
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
344
+ color: white;
345
+ padding: 60px 30px;
346
+ border-radius: 25px;
347
+ margin-bottom: 40px;
348
+ box-shadow: 0 15px 50px rgba(102, 126, 234, 0.3);
349
+ }
350
+
351
+ .tab-nav button {
352
+ font-size: 18px !important;
353
+ font-weight: 600 !important;
354
+ padding: 18px 30px !important;
355
+ }
356
+
357
+ .gr-button-primary {
358
+ background: linear-gradient(135deg, #667eea, #764ba2) !important;
359
+ border: none !important;
360
+ font-size: 18px !important;
361
+ font-weight: 600 !important;
362
+ padding: 16px 40px !important;
363
+ border-radius: 12px !important;
364
+ transition: all 0.3s ease !important;
365
+ }
366
+
367
+ .gr-button-primary:hover {
368
+ transform: translateY(-2px) !important;
369
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4) !important;
370
+ }
371
+
372
+ footer {
373
+ visibility: hidden !important;
374
+ }
375
+ """
376
+
377
+ # Create Gradio Interface
378
+ with gr.Blocks(
379
+ theme=gr.themes.Soft(
380
+ primary_hue="purple",
381
+ secondary_hue="pink",
382
+ font=gr.themes.GoogleFont("Inter")
383
+ ),
384
+ css=custom_css,
385
+ title="🎭 AI Emotion Detector | koyelog",
386
+ analytics_enabled=False
387
+ ) as demo:
388
 
389
+ # Header
390
+ gr.HTML("""
391
+ <div class="main-header">
392
+ <h1 style='font-size: 4em; margin: 0; font-weight: 900; text-shadow: 3px 3px 6px rgba(0,0,0,0.2);'>
393
+ 🎭 AI Emotion Detector
394
+ </h1>
395
+ <p style='font-size: 1.5em; margin: 20px 0 10px; opacity: 0.95; font-weight: 500;'>
396
+ Powered by Vision Transformer | 98.80% Validation Accuracy
397
+ </p>
398
+ <p style='font-size: 1.1em; opacity: 0.85;'>
399
+ Model: <strong>koyelog/face</strong> | 85.8M Parameters | Real-time Detection
400
+ </p>
401
+ <div style='margin-top: 20px; display: flex; gap: 15px; justify-content: center; flex-wrap: wrap;'>
402
+ <span style='background: rgba(255,255,255,0.25); padding: 10px 25px; border-radius: 25px; backdrop-filter: blur(10px);'>
403
+ 😠 Angry
404
+ </span>
405
+ <span style='background: rgba(255,255,255,0.25); padding: 10px 25px; border-radius: 25px; backdrop-filter: blur(10px);'>
406
+ 🀒 Disgust
407
+ </span>
408
+ <span style='background: rgba(255,255,255,0.25); padding: 10px 25px; border-radius: 25px; backdrop-filter: blur(10px);'>
409
+ 😨 Fear
410
+ </span>
411
+ <span style='background: rgba(255,255,255,0.25); padding: 10px 25px; border-radius: 25px; backdrop-filter: blur(10px);'>
412
+ 😊 Happy
413
+ </span>
414
+ <span style='background: rgba(255,255,255,0.25); padding: 10px 25px; border-radius: 25px; backdrop-filter: blur(10px);'>
415
+ 😒 Sad
416
+ </span>
417
+ <span style='background: rgba(255,255,255,0.25); padding: 10px 25px; border-radius: 25px; backdrop-filter: blur(10px);'>
418
+ 😲 Surprise
419
+ </span>
420
+ <span style='background: rgba(255,255,255,0.25); padding: 10px 25px; border-radius: 25px; backdrop-filter: blur(10px);'>
421
+ 😐 Neutral
422
+ </span>
 
 
 
 
423
  </div>
424
+ </div>
425
+ """)
426
+
427
+ with gr.Tabs():
428
 
429
+ # TAB 1: WEBCAM
430
+ with gr.Tab("πŸ“Ή Live Webcam Detection"):
431
+ gr.Markdown("""
432
+ ### πŸŽ₯ Capture Your Emotion in Real-Time
433
+ Click the camera button to capture your face and instantly detect your emotion!
434
+ """)
435
 
436
+ with gr.Row():
437
+ with gr.Column(scale=1):
438
+ webcam_input = gr.Image(
439
+ sources=["webcam"],
440
+ type="pil",
441
+ label="πŸ“Έ Your Face",
442
+ streaming=False,
443
+ mirror_webcam=True
444
+ )
445
+ webcam_button = gr.Button(
446
+ "πŸ” Detect My Emotion",
447
+ variant="primary",
448
+ size="lg",
449
+ scale=1
450
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
+ with gr.Column(scale=1):
453
+ webcam_html = gr.HTML(label="🎯 Emotion Result")
454
+ webcam_label = gr.Label(
455
+ label="πŸ“Š Emotion Probabilities",
456
+ num_top_classes=7
457
+ )
458
 
459
+ webcam_button.click(
460
+ fn=predict_emotion,
461
+ inputs=webcam_input,
462
+ outputs=[webcam_label, webcam_html]
463
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
 
465
+ # TAB 2: UPLOAD
466
+ with gr.Tab("πŸ–ΌοΈ Upload Image"):
467
+ gr.Markdown("""
468
+ ### πŸ“€ Upload or Drag & Drop Face Image
469
+ Supports JPG, PNG, JPEG formats. Best results with front-facing, well-lit photos!
470
+ """)
471
+
472
+ with gr.Row():
473
+ with gr.Column(scale=1):
474
+ image_input = gr.Image(
475
+ type="pil",
476
+ label="πŸ–ΌοΈ Upload Face Image",
477
+ sources=["upload", "clipboard"]
478
+ )
479
+ image_button = gr.Button(
480
+ "πŸ” Detect Emotion",
481
+ variant="primary",
482
+ size="lg"
483
+ )
484
 
485
+ with gr.Column(scale=1):
486
+ image_html = gr.HTML(label="🎯 Emotion Result")
487
+ image_label = gr.Label(
488
+ label="πŸ“Š Emotion Probabilities",
489
+ num_top_classes=7
490
+ )
491
+
492
+ image_button.click(
493
+ fn=predict_emotion,
494
+ inputs=image_input,
495
+ outputs=[image_label, image_html]
496
+ )
497
+
498
+ # Footer
499
+ gr.HTML("""
500
+ <div style='
501
+ text-align: center;
502
+ margin-top: 60px;
503
+ padding: 50px 30px;
504
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
505
+ border-radius: 25px;
506
+ box-shadow: 0 8px 32px rgba(0,0,0,0.08);
507
+ '>
508
+ <h2 style='color: #333; margin-bottom: 30px; font-size: 2em;'>
509
+ πŸ“Š Model Information
510
+ </h2>
511
+
512
+ <div style='
513
+ display: grid;
514
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
515
+ gap: 25px;
516
+ margin: 30px 0;
517
+ '>
518
+ <div style='background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 16px rgba(0,0,0,0.06);'>
519
+ <p style='font-weight: 700; color: #667eea; font-size: 1.1em; margin-bottom: 10px;'>Model ID</p>
520
+ <p style='font-size: 1.2em; color: #333; font-weight: 600;'>koyelog/face</p>
521
  </div>
522
+ <div style='background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 16px rgba(0,0,0,0.06);'>
523
+ <p style='font-weight: 700; color: #667eea; font-size: 1.1em; margin-bottom: 10px;'>Architecture</p>
524
+ <p style='font-size: 1.2em; color: #333; font-weight: 600;'>Vision Transformer (ViT)</p>
525
+ </div>
526
+ <div style='background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 16px rgba(0,0,0,0.06);'>
527
+ <p style='font-weight: 700; color: #667eea; font-size: 1.1em; margin-bottom: 10px;'>Parameters</p>
528
+ <p style='font-size: 1.2em; color: #333; font-weight: 600;'>85.8 Million</p>
529
+ </div>
530
+ <div style='background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 16px rgba(0,0,0,0.06);'>
531
+ <p style='font-weight: 700; color: #667eea; font-size: 1.1em; margin-bottom: 10px;'>Accuracy</p>
532
+ <p style='font-size: 1.2em; color: #333; font-weight: 600;'>Train: 99.29% | Val: 98.80%</p>
533
+ </div>
534
+ </div>
535
+
536
+ <div style='margin: 30px 0; padding: 25px; background: white; border-radius: 15px; box-shadow: 0 4px 16px rgba(0,0,0,0.06);'>
537
+ <p style='font-weight: 700; color: #333; font-size: 1.3em; margin-bottom: 15px;'>
538
+ Training Details
539
  </p>
540
+ <p style='color: #666; font-size: 1.05em; line-height: 1.6;'>
541
+ <strong>Dataset:</strong> 181,230 images across 7 emotion categories<br>
542
+ <strong>Training Epochs:</strong> 20 epochs with dual T4 GPUs<br>
543
+ <strong>Best Epoch:</strong> Epoch 20/20 (Val Acc: 98.80%)<br>
544
+ <strong>License:</strong> MIT License
 
 
 
545
  </p>
546
  </div>
547
+
548
+ <p style='color: #666; font-size: 1.05em; margin-top: 30px; line-height: 1.6;'>
549
+ ⚠️ <strong>Best Results:</strong> Front-facing photos | Good lighting | Single face | Clear expressions
550
+ </p>
551
+
552
+ <p style='color: #999; font-size: 0.95em; margin-top: 30px;'>
553
+ Created by <strong style='color: #667eea;'>Koyeliya Ghosh</strong><br>
554
+ <a href='https://huggingface.co/koyelog/face' target='_blank' style='color: #667eea; font-weight: 600;'>
555
+ View Model on HuggingFace β†’
556
+ </a>
557
+ </p>
558
+ </div>
559
+ """)
560
 
561
  # ===== LAUNCH =====
562
  if __name__ == "__main__":
563
+ print("\n" + "="*70)
564
  print("πŸš€ LAUNCHING EMOTION DETECTION APP")
565
+ print("="*70)
566
+ print("βœ… Model loaded and ready")
567
+ print("βœ… Gradio interface built")
568
+ print("βœ… Starting server...\n")
569
 
 
570
  demo.launch(
571
  server_name="0.0.0.0",
572
  server_port=7860,
573
  share=False,
574
+ show_error=True,
575
+ show_api=True
576
  )