ktejeshnaidu commited on
Commit
440c0a1
ยท
verified ยท
1 Parent(s): eda0fea

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +124 -89
app.py CHANGED
@@ -15,118 +15,153 @@ face_cascade = cv2.CascadeClassifier(
15
  if face_cascade.empty():
16
  raise RuntimeError("Failed to load Haar Cascade")
17
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  def predict_emotion(image):
20
  """
21
  Predict emotion from an image.
22
-
23
  Args:
24
- image: PIL Image or numpy array
25
-
26
  Returns:
27
- annotated image and emotion prediction
28
  """
29
  if image is None:
30
- return None, "No image provided"
31
-
32
- # Convert PIL Image to numpy array if needed
33
- if isinstance(image, np.ndarray):
34
- frame = image
35
- else:
36
- frame = np.array(image)
37
-
38
- # Convert RGB to BGR for OpenCV
39
- if len(frame.shape) == 3 and frame.shape[2] == 3:
40
- frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
41
- else:
42
- frame_bgr = frame
43
-
44
- # Convert to grayscale for face detection
45
  gray = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2GRAY)
46
-
47
- # Detect faces
48
  detected = face_cascade.detectMultiScale(
49
  gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)
50
  )
51
-
52
  if len(detected) == 0:
53
- return frame, "No face detected"
54
-
55
- # Get the largest face
56
- faces = [max(detected, key=lambda r: r[2]*r[3])]
57
-
58
- # Process the face
59
- output_frame = frame_bgr.copy()
60
- emotions = []
61
-
62
- for (x, y, w, h) in faces:
63
- # Extract face region
64
- face_rgb = cv2.cvtColor(frame_bgr[y:y+h, x:x+w], cv2.COLOR_BGR2RGB)
65
-
66
- # Predict emotion
67
- emotion = predictor.predict(face_rgb)
68
- emotions.append(emotion)
69
-
70
- # Draw rectangle and label
71
- cv2.rectangle(output_frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
72
- cv2.putText(
73
- output_frame, emotion, (x, y - 10),
74
- cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2
75
- )
76
-
77
- # Convert back to RGB for display
78
- output_frame_rgb = cv2.cvtColor(output_frame, cv2.COLOR_BGR2RGB)
79
-
80
- # Return annotated image and detected emotion
81
- emotion_text = ", ".join(emotions) if emotions else "No emotion detected"
82
-
83
- return output_frame_rgb, f"Detected emotion(s): {emotion_text}"
84
-
85
-
86
- # Create Gradio interface
87
- with gr.Blocks(title="Smilo๐Ÿ˜ƒ - Real-Time Emotion Detection") as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  gr.HTML("""
89
- <div style="background: linear-gradient(90deg, #FF9933 0%, #D0B264 50%, #469F93 100%);
90
- padding: 40px;
91
- border-radius: 12px;
92
- text-align: center;
93
- color: white;
94
- font-family: 'Helvetica Neue', Arial, sans-serif;
95
- margin-bottom: 20px;">
96
- <h1 style="color: white; margin: 0; margin-bottom: 10px; font-weight: 900; font-size: 3.5em; display: flex; align-items: center; justify-content: center; gap: 10px;">
97
- Smilo <span style="font-size: 0.9em;">๐Ÿ˜ƒ</span>
98
- </h1>
99
- <p style="color: #f0f0f0; font-size: 1.2em; margin: 0; font-weight: 400; letter-spacing: 0.5px;">Real-Time Emotion Detection powered by PyTorch</p>
100
  </div>
101
  """)
102
-
103
  with gr.Row():
104
- with gr.Column():
 
105
  image_input = gr.Image(
106
- label="Input Image",
107
  type="pil",
108
- sources=["upload", "webcam"]
 
 
109
  )
110
- submit_btn = gr.Button("Predict Emotion", variant="primary")
111
-
112
- with gr.Column():
113
- image_output = gr.Image(label="Annotated Image")
114
- emotion_output = gr.Textbox(label="Prediction Result", interactive=False)
115
-
116
- # Connect the function to the button
117
- submit_btn.click(
118
- fn=predict_emotion,
119
- inputs=[image_input],
120
- outputs=[image_output, emotion_output]
 
 
 
 
 
 
 
121
  )
122
-
123
- # Also run prediction when image is uploaded
124
- image_input.change(
 
 
 
 
 
125
  fn=predict_emotion,
126
- inputs=[image_input],
127
- outputs=[image_output, emotion_output]
 
 
128
  )
129
 
 
 
 
 
 
130
 
131
  if __name__ == "__main__":
132
- demo.launch(share=True)
 
15
  if face_cascade.empty():
16
  raise RuntimeError("Failed to load Haar Cascade")
17
 
18
+ EMOTION_EMOJI = {
19
+ "angry": "๐Ÿ˜  Angry",
20
+ "disgust": "๐Ÿคข Disgust",
21
+ "fear": "๐Ÿ˜จ Fear",
22
+ "happy": "๐Ÿ˜ƒ Happy",
23
+ "neutral": "๐Ÿ˜ Neutral",
24
+ "sad": "๐Ÿ˜” Sad",
25
+ "surprise": "๐Ÿ˜ฎ Surprise",
26
+ }
27
+
28
 
29
  def predict_emotion(image):
30
  """
31
  Predict emotion from an image.
32
+
33
  Args:
34
+ image: PIL Image or numpy array from Gradio
35
+
36
  Returns:
37
+ annotated_image (np.ndarray), status_text (str), confidence_dict (dict)
38
  """
39
  if image is None:
40
+ return None, "โš ๏ธ No image provided", {}
41
+
42
+ # Ensure numpy array in RGB
43
+ frame = np.array(image) if not isinstance(image, np.ndarray) else image
44
+
45
+ # BGR copy for OpenCV ops
46
+ frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
 
 
 
 
 
 
 
 
47
  gray = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2GRAY)
48
+
 
49
  detected = face_cascade.detectMultiScale(
50
  gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)
51
  )
52
+
53
  if len(detected) == 0:
54
+ return frame, "๐Ÿ” No face detected โ€” try better lighting!", {}
55
+
56
+ # Largest face only
57
+ x, y, w, h = max(detected, key=lambda r: r[2] * r[3])
58
+ y1, y2 = max(0, y), min(frame_bgr.shape[0], y + h)
59
+ x1, x2 = max(0, x), min(frame_bgr.shape[1], x + w)
60
+
61
+ face_rgb = cv2.cvtColor(frame_bgr[y1:y2, x1:x2], cv2.COLOR_BGR2RGB)
62
+
63
+ # Get emotion + confidence scores
64
+ emotion, scores = predictor.predict_with_confidence(face_rgb)
65
+
66
+ # Draw bounding box + label
67
+ output = frame_bgr.copy()
68
+ cv2.rectangle(output, (x1, y1), (x2, y2), (0, 200, 100), 2)
69
+ label = EMOTION_EMOJI.get(emotion.lower(), emotion)
70
+ cv2.putText(
71
+ output, label, (x1, y1 - 12),
72
+ cv2.FONT_HERSHEY_SIMPLEX, 0.85, (0, 200, 100), 2, cv2.LINE_AA
73
+ )
74
+
75
+ # Map scores to emoji labels for gr.Label
76
+ emoji_scores = {
77
+ EMOTION_EMOJI.get(cls.lower(), cls): float(conf)
78
+ for cls, conf in scores.items()
79
+ }
80
+
81
+ return (
82
+ cv2.cvtColor(output, cv2.COLOR_BGR2RGB),
83
+ f"โœ… Detected: **{EMOTION_EMOJI.get(emotion.lower(), emotion)}**",
84
+ emoji_scores,
85
+ )
86
+
87
+
88
+ # ---------- UI ----------
89
+ theme = gr.themes.Soft(
90
+ primary_hue="teal",
91
+ secondary_hue="orange",
92
+ neutral_hue="slate",
93
+ font=[gr.themes.GoogleFont("Inter"), "sans-serif"],
94
+ )
95
+
96
+ with gr.Blocks(
97
+ title="Smilo ๐Ÿ˜ƒ โ€“ Real-Time Emotion Detection",
98
+ theme=theme,
99
+ css="""
100
+ .hero { background: linear-gradient(135deg,#FF9933 0%,#D0B264 50%,#469F93 100%);
101
+ padding:36px; border-radius:14px; text-align:center; color:#fff;
102
+ margin-bottom:18px; }
103
+ .hero h1 { font-size:3em; font-weight:900; margin:0 0 8px; }
104
+ .hero p { font-size:1.1em; opacity:0.9; margin:0; }
105
+ footer { display:none !important; }
106
+ """,
107
+ ) as demo:
108
+
109
  gr.HTML("""
110
+ <div class="hero">
111
+ <h1>Smilo ๐Ÿ˜ƒ</h1>
112
+ <p>Real-Time Facial Emotion Detection &nbsp;ยท&nbsp; PyTorch + OpenCV</p>
 
 
 
 
 
 
 
 
113
  </div>
114
  """)
115
+
116
  with gr.Row():
117
+ # โ”€โ”€ Left column: input โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
118
+ with gr.Column(scale=1):
119
  image_input = gr.Image(
120
+ label="๐Ÿ“ท Input โ€“ upload a photo or use your webcam",
121
  type="pil",
122
+ sources=["upload", "webcam"],
123
+ mirror_webcam=True,
124
+ format="jpeg",
125
  )
126
+ with gr.Row():
127
+ submit_btn = gr.Button("๐Ÿ” Detect Emotion", variant="primary", scale=2)
128
+ clear_btn = gr.Button("๐Ÿ—‘๏ธ Clear", variant="secondary", scale=1)
129
+
130
+ # โ”€โ”€ Right column: output โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
131
+ with gr.Column(scale=1):
132
+ image_output = gr.Image(label="๐Ÿ–ผ๏ธ Annotated Result", interactive=False)
133
+ emotion_output = gr.Markdown(label="Result")
134
+ confidence_out = gr.Label(
135
+ label="๐Ÿ“Š Confidence Scores",
136
+ num_top_classes=7,
137
+ )
138
+
139
+ # โ”€โ”€ Examples โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
140
+ gr.Examples(
141
+ examples=[], # add example image paths here if you have them
142
+ inputs=image_input,
143
+ label="Try an example",
144
  )
145
+
146
+ # โ”€โ”€ Event wiring โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
147
+ outputs = [image_output, emotion_output, confidence_out]
148
+
149
+ submit_btn.click(fn=predict_emotion, inputs=image_input, outputs=outputs)
150
+
151
+ # Real-time streaming when webcam is active (Gradio 6 feature)
152
+ image_input.stream(
153
  fn=predict_emotion,
154
+ inputs=image_input,
155
+ outputs=outputs,
156
+ stream_every=0.1, # run ~10 fps
157
+ time_limit=None,
158
  )
159
 
160
+ # Also fire on static image upload
161
+ image_input.change(fn=predict_emotion, inputs=image_input, outputs=outputs)
162
+
163
+ clear_btn.click(lambda: (None, None, "", {}), outputs=[image_input, *outputs])
164
+
165
 
166
  if __name__ == "__main__":
167
+ demo.launch()