Clocksp commited on
Commit
39890a7
·
verified ·
1 Parent(s): 1cae0ac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -54
app.py CHANGED
@@ -2,61 +2,164 @@ import gradio as gr
2
  import cv2
3
  import numpy as np
4
  import pickle
5
- from util import get_face_landmarks
6
-
7
- # Emotion labels
8
- emotions = ['HAPPY', 'SAD', 'SURPRISED']
9
-
10
- # Load the model once
11
- with open('model.pkl', 'rb') as f:
12
- model = pickle.load(f)
13
-
14
- def predict_emotion(image):
15
- # Convert PIL image to OpenCV format
16
- img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
17
-
18
- # Extract landmarks
19
- face_landmarks = get_face_landmarks(img, draw=False, static_image_mode=True)
20
-
21
- if face_landmarks is None or len(face_landmarks) == 0:
22
- # Draw "No Face Detected" on the image
23
- cv2.putText(img, "No Face Detected", (10, img.shape[0] - 10),
24
- cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
25
- return {"No face detected": 1.0}, cv2.cvtColor(img, cv2.COLOR_BGR2RGB), "⚠ No face detected in the image."
26
-
27
- # Predict emotion and probabilities
28
- output = model.predict([face_landmarks])
29
- predicted_emotion = emotions[int(output[0])]
30
-
31
- if hasattr(model, "predict_proba"):
32
- probs = model.predict_proba([face_landmarks])[0]
33
- confidence_dict = {emotions[i]: float(probs[i]) for i in range(len(emotions))}
34
- else:
35
- confidence_dict = {predicted_emotion: 1.0}
36
-
37
- # Annotate image with predicted emotion
38
- cv2.putText(img, predicted_emotion, (10, img.shape[0] - 10),
39
- cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
40
-
41
- return confidence_dict, cv2.cvtColor(img, cv2.COLOR_BGR2RGB), f" Detected emotion: {predicted_emotion}"
42
-
43
- # Example images
44
- examples = [["examples/happy.png"], ["examples/sad.png"], ["examples/surprised.png"]]
45
-
46
- # Gradio Interface
47
- demo = gr.Interface(
48
- fn=predict_emotion,
49
- inputs=gr.Image(type="pil", label="Upload Image or Use Webcam", sources=["upload", "webcam"]),
50
- outputs=[
51
- gr.Label(num_top_classes=3, label="Predicted Emotion & Confidence"),
52
- gr.Image(type="numpy", label="Annotated Image"),
53
- gr.Textbox(label="Status", interactive=False)
54
- ],
55
- title="Emotion Detector",
56
- description="Upload an image or use webcam to detect emotions (HAPPY, SAD, SURPRISED).",
57
- examples=examples,
58
- theme="default"
59
  )
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  if __name__ == "__main__":
62
  demo.launch()
 
2
  import cv2
3
  import numpy as np
4
  import pickle
5
+ from functools import lru_cache
6
+
7
+ # ---- Import your landmarks util ----
8
+ try:
9
+ from util import get_face_landmarks
10
+ except Exception as e:
11
+ raise ImportError(
12
+ "Could not import 'get_face_landmarks' from util.py. "
13
+ "Make sure util.py exists and defines get_face_landmarks(img, draw: bool, static_image_mode: bool)."
14
+ ) from e
15
+
16
+
17
+ # ---- App Config ----
18
+ EMOTIONS = ["HAPPY", "SAD", "SURPRISED"]
19
+ MODEL_PATH = "model.pkl"
20
+ APP_TITLE = "Emotion Detector"
21
+ APP_DESC = (
22
+ "Upload an image or use your webcam. Toggle 'Draw Landmarks' for visualization. "
23
+ "If no face is detected, the original image is shown and status explains why."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  )
25
 
26
+
27
+ # ---- Model Loader (cached) ----
28
+ @lru_cache(maxsize=1)
29
+ def load_model():
30
+ with open(MODEL_PATH, "rb") as f:
31
+ model = pickle.load(f)
32
+ return model
33
+
34
+
35
+ # ---- Core Inference ----
36
+ def predict_emotion(image, draw_toggle):
37
+ """
38
+ image: PIL.Image (from gr.Image with type='pil')
39
+ draw_toggle: 'OFF' or 'ON'
40
+ """
41
+ # Input validation
42
+ if image is None:
43
+ return {"Status": 1.0}, None, "Please upload an image."
44
+
45
+ draw = (draw_toggle == "ON")
46
+
47
+ # Convert PIL -> OpenCV BGR
48
+ try:
49
+ img_rgb = np.array(image) # PIL -> RGB ndarray
50
+ if img_rgb.ndim == 2: # grayscale to 3-ch
51
+ img_rgb = cv2.cvtColor(img_rgb, cv2.COLOR_GRAY2RGB)
52
+ img_bgr = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
53
+ except Exception:
54
+ return {"Status": 1.0}, None, "⚠ Could not read the image. Please try a different one."
55
+
56
+ # Extract landmarks (your util may also draw on img internally when draw=True)
57
+ try:
58
+ landmarks = get_face_landmarks(img_bgr, draw=draw, static_image_mode=True)
59
+ except Exception as e:
60
+ return {"Status": 1.0}, img_rgb, f"⚠ Landmark extraction failed: {e}"
61
+
62
+ # Handle no-face case (do NOT annotate; return original image)
63
+ if landmarks is None or (hasattr(landmarks, "__len__") and len(landmarks) == 0):
64
+ return {"No face detected": 1.0}, img_rgb, "⚠ No face detected in the image."
65
+
66
+ # Load model
67
+ try:
68
+ model = load_model()
69
+ except FileNotFoundError:
70
+ return {"Status": 1.0}, img_rgb, "⚠ model.pkl not found in repo root."
71
+ except Exception as e:
72
+ return {"Status": 1.0}, img_rgb, f"⚠ Failed to load model: {e}"
73
+
74
+ # Predict
75
+ try:
76
+ output = model.predict([landmarks])
77
+ pred_idx = int(output[0])
78
+ pred_label = EMOTIONS[pred_idx] if 0 <= pred_idx < len(EMOTIONS) else str(pred_idx)
79
+
80
+ # Confidence/probabilities if available
81
+ if hasattr(model, "predict_proba"):
82
+ probs = model.predict_proba([landmarks])[0]
83
+ confidence = {EMOTIONS[i]: float(probs[i]) for i in range(len(EMOTIONS))}
84
+ else:
85
+ confidence = {pred_label: 1.0}
86
+
87
+ # Always draw predicted text on the copy we return (but ONLY if a face exists)
88
+ img_annot = img_bgr.copy()
89
+ try:
90
+ cv2.putText(
91
+ img_annot,
92
+ pred_label,
93
+ (10, img_annot.shape[0] - 10),
94
+ cv2.FONT_HERSHEY_SIMPLEX,
95
+ 1.0,
96
+ (0, 255, 0),
97
+ 2,
98
+ cv2.LINE_AA,
99
+ )
100
+ except Exception:
101
+ # If drawing fails, just return the original image
102
+ img_annot = img_bgr
103
+
104
+ img_out = cv2.cvtColor(img_annot, cv2.COLOR_BGR2RGB)
105
+ status = f"✅ Detected emotion: {pred_label}"
106
+ return confidence, img_out, status
107
+
108
+ except Exception as e:
109
+ return {"Status": 1.0}, img_rgb, f"⚠ Inference failed: {e}"
110
+
111
+
112
+ # ---- Gradio UI ----
113
+ with gr.Blocks(theme="default") as demo:
114
+ gr.Markdown(f"# {APP_TITLE}\n{APP_DESC}")
115
+
116
+ with gr.Row():
117
+ with gr.Column(scale=1):
118
+ image_input = gr.Image(
119
+ type="pil",
120
+ label="Upload Image or Use Webcam",
121
+ sources=["upload", "webcam"],
122
+ interactive=True,
123
+ )
124
+ draw_toggle = gr.Radio(
125
+ choices=["OFF", "ON"],
126
+ value="OFF",
127
+ label="Draw Landmarks",
128
+ interactive=True,
129
+ )
130
+ gr.Markdown(
131
+ "Tip: Switch **Draw Landmarks** ON to visualize key points (if your `util.get_face_landmarks` draws them)."
132
+ )
133
+
134
+ with gr.Column(scale=1):
135
+ label_output = gr.Label(num_top_classes=3, label="Predicted Emotion & Confidence")
136
+ image_output = gr.Image(type="numpy", label="Annotated Image")
137
+ status_output = gr.Textbox(label="Status", interactive=False)
138
+
139
+ # Examples (ensure these files exist in /examples)
140
+ gr.Examples(
141
+ examples=[
142
+ ["examples/happy.png", "OFF"],
143
+ ["examples/sad.png", "OFF"],
144
+ ["examples/surprised.png", "OFF"],
145
+ ],
146
+ inputs=[image_input, draw_toggle],
147
+ label="Try examples",
148
+ )
149
+
150
+ # Real-time: changing either the image or the toggle re-runs inference automatically
151
+ image_input.change(
152
+ fn=predict_emotion,
153
+ inputs=[image_input, draw_toggle],
154
+ outputs=[label_output, image_output, status_output],
155
+ queue=False,
156
+ )
157
+ draw_toggle.change(
158
+ fn=predict_emotion,
159
+ inputs=[image_input, draw_toggle],
160
+ outputs=[label_output, image_output, status_output],
161
+ queue=False,
162
+ )
163
+
164
  if __name__ == "__main__":
165
  demo.launch()