Sohan2004 commited on
Commit
1899f78
Β·
verified Β·
1 Parent(s): 2e0ce7a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +168 -108
app.py CHANGED
@@ -2,59 +2,63 @@ import gradio as gr
2
  import cv2
3
  import mediapipe as mp
4
  import numpy as np
5
- from stress_detection import * # Import your entire code
 
 
6
 
7
- # Initialize detectors (create once, reuse for all images)
8
- au01 = AU01Detector()
9
- au04 = AU04Detector()
10
- au06 = AU06Detector()
11
- au07 = AU07Detector()
12
- au12 = AU12Detector()
13
- au14 = AU14Detector()
14
- au17 = AU17Detector()
15
- au23 = AU23Detector()
16
- au24 = AU24Detector()
17
- au26 = AU26Detector()
 
 
 
 
 
 
 
18
 
19
  detectors = [au01, au04, au06, au07, au12, au14, au17, au23, au24, au26]
20
 
21
  # Initialize MediaPipe
22
- mp_face_mesh = mp.solutions.face_mesh
23
  face_mesh = mp_face_mesh.FaceMesh(
24
  min_detection_confidence=0.5,
25
  min_tracking_confidence=0.5,
26
  refine_landmarks=True
27
  )
28
 
29
- def detect_stress_from_single_image(image):
30
  """
31
- Process a single image and return stress prediction
32
-
33
- Args:
34
- image: numpy array (BGR from Gradio)
35
-
36
- Returns:
37
- dict: Stress classification with probabilities
38
  """
39
- if image is None:
40
- return {"Error": "No image provided"}
41
 
42
  try:
43
- # Get image dimensions
44
- frame_height, frame_width = image.shape[:2]
45
-
46
- # Convert to RGB for MediaPipe
47
- rgb_frame = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
48
 
49
  # Process with MediaPipe
 
50
  results = face_mesh.process(rgb_frame)
51
 
52
  if not results.multi_face_landmarks:
53
- return {"Error": "No face detected in image"}
 
 
54
 
55
  landmarks = results.multi_face_landmarks[0].landmark
56
 
57
- # Detect all 10 AUs
58
  au01_active, au01_intensity = au01.detect(landmarks, frame_width, frame_height)
59
  au04_active, au04_intensity = au04.detect(landmarks, frame_width, frame_height)
60
  au06_active, au06_intensity = au06.detect(landmarks, frame_width, frame_height)
@@ -66,103 +70,159 @@ def detect_stress_from_single_image(image):
66
  au24_active, au24_intensity = au24.detect(landmarks, frame_width, frame_height)
67
  au26_active, au26_intensity = au26.detect(landmarks, frame_width, frame_height)
68
 
69
- # Calculate simple stress score from single frame
70
- stress_aus_intensity = (au01_intensity + au04_intensity + au07_intensity +
71
- au17_intensity + au23_intensity + au24_intensity) / 6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
- positive_aus_intensity = (au06_intensity + au12_intensity + au14_intensity) / 3
 
 
 
 
 
74
 
75
- # Simple scoring for single frame
76
- stress_score = stress_aus_intensity - (positive_aus_intensity * 0.3)
77
- stress_score = min(100, max(0, stress_score))
 
 
 
78
 
79
- # Classification
80
- if stress_score < 30:
81
- return {
82
- "Not Stressed 😊": float(100 - stress_score) / 100,
83
- "Stressed 😰": float(stress_score) / 100
84
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  else:
86
- return {
87
- "Stressed 😰": float(stress_score) / 100,
88
- "Not Stressed 😊": float(100 - stress_score) / 100
89
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
  except Exception as e:
92
- return {"Error": f"Processing failed: {str(e)}"}
93
 
94
 
95
- # Create Gradio interface
96
- with gr.Blocks(title="Stress Detection System", theme=gr.themes.Soft()) as demo:
97
- gr.Markdown("# 🧠 Stress Detection System")
98
- gr.Markdown("### AI-Powered Facial Expression Analysis using 10 Action Units (FACS)")
99
- gr.Markdown("*Based on Facial Action Coding System - Ekman & Friesen*")
100
 
101
  with gr.Row():
102
- with gr.Column():
103
- image_input = gr.Image(
104
  sources=["webcam"],
105
- type="numpy",
106
- label="πŸ“Ή Capture Your Face"
107
  )
108
- analyze_btn = gr.Button("πŸ” Analyze Stress Level", variant="primary", size="lg")
109
 
110
  gr.Markdown("""
111
- ### πŸ“ How to use:
112
- 1. Click the **webcam icon** above
113
- 2. Allow camera access
114
- 3. Position your face clearly in frame
115
- 4. Click **Analyze Stress Level**
116
  """)
117
-
118
- with gr.Column():
119
- output_label = gr.Label(
120
- num_top_classes=2,
121
- label="πŸ“Š Stress Analysis Results"
 
122
  )
123
-
124
- gr.Markdown("""
125
- ### πŸ”¬ Action Units Detected:
126
- **Stress Indicators:**
127
- - AU01: Inner Brow Raise
128
- - AU04: Brow Lowerer
129
- - AU07: Lid Tightener
130
- - AU17: Chin Raiser
131
- - AU23: Lip Tightener
132
- - AU24: Lip Pressor
133
-
134
- **Positive Indicators:**
135
- - AU06: Cheek Raiser
136
- - AU12: Lip Corner Puller
137
- - AU14: Dimpler
138
- - AU26: Jaw Drop
139
- """)
140
-
141
- # Connect button
142
- analyze_btn.click(
143
- fn=detect_stress_from_single_image,
144
- inputs=image_input,
145
- outputs=output_label
146
- )
147
 
148
  gr.Markdown("""
149
  ---
150
- ### ℹ️ About
151
- This system uses **10 Facial Action Units** based on the Facial Action Coding System (FACS)
152
- to detect stress through micro-expressions. Developed under the guidance of **Prof. Anup Nandy**.
 
 
 
 
153
  """)
 
 
 
 
 
 
 
 
154
 
155
- # Launch
156
  if __name__ == "__main__":
157
- demo.launch()
158
- ```
159
-
160
- ---
161
-
162
- ## Final File Structure in Your Space:
163
- ```
164
- your-space/
165
- β”œβ”€β”€ app.py # ← Gradio interface (code above)
166
- β”œβ”€β”€ stress_detection.py # ← Your complete code (paste as-is)
167
- β”œβ”€β”€ requirements.txt # ← 6 packages listed above
168
- └── README.md # ← Update with proper metadata
 
2
  import cv2
3
  import mediapipe as mp
4
  import numpy as np
5
+ import base64
6
+ from io import BytesIO
7
+ from PIL import Image
8
 
9
+ # Import your detector classes
10
+ from stress_detection import (
11
+ AU01Detector, AU04Detector, AU06Detector, AU07Detector,
12
+ AU12Detector, AU14Detector, AU17Detector, AU23Detector,
13
+ AU24Detector, AU26Detector, mp_face_mesh
14
+ )
15
+
16
+ # Initialize all detectors globally
17
+ au01 = AU01Detector(window_size=10)
18
+ au04 = AU04Detector(window_size=10)
19
+ au06 = AU06Detector(window_size=10)
20
+ au07 = AU07Detector(window_size=10)
21
+ au12 = AU12Detector(window_size=10)
22
+ au14 = AU14Detector(window_size=10)
23
+ au17 = AU17Detector(window_size=10)
24
+ au23 = AU23Detector(window_size=10)
25
+ au24 = AU24Detector(window_size=10)
26
+ au26 = AU26Detector(window_size=10)
27
 
28
  detectors = [au01, au04, au06, au07, au12, au14, au17, au23, au24, au26]
29
 
30
  # Initialize MediaPipe
 
31
  face_mesh = mp_face_mesh.FaceMesh(
32
  min_detection_confidence=0.5,
33
  min_tracking_confidence=0.5,
34
  refine_landmarks=True
35
  )
36
 
37
+ def process_frame(frame):
38
  """
39
+ Process a single frame from webcam
40
+ Returns: processed frame with overlays + stress analysis
 
 
 
 
 
41
  """
42
+ if frame is None:
43
+ return None, "No frame received"
44
 
45
  try:
46
+ # Convert from RGB (Gradio) to BGR (OpenCV)
47
+ frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
48
+ frame_height, frame_width = frame_bgr.shape[:2]
 
 
49
 
50
  # Process with MediaPipe
51
+ rgb_frame = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
52
  results = face_mesh.process(rgb_frame)
53
 
54
  if not results.multi_face_landmarks:
55
+ cv2.putText(frame_bgr, "No face detected", (50, 50),
56
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
57
+ return cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB), "⚠️ No face detected"
58
 
59
  landmarks = results.multi_face_landmarks[0].landmark
60
 
61
+ # Detect all AUs
62
  au01_active, au01_intensity = au01.detect(landmarks, frame_width, frame_height)
63
  au04_active, au04_intensity = au04.detect(landmarks, frame_width, frame_height)
64
  au06_active, au06_intensity = au06.detect(landmarks, frame_width, frame_height)
 
70
  au24_active, au24_intensity = au24.detect(landmarks, frame_width, frame_height)
71
  au26_active, au26_intensity = au26.detect(landmarks, frame_width, frame_height)
72
 
73
+ # Draw overlays (like your original code)
74
+ overlay = frame_bgr.copy()
75
+
76
+ # Create semi-transparent overlay panels
77
+ cv2.rectangle(overlay, (5, 5), (300, 200), (50, 50, 50), -1)
78
+ cv2.rectangle(overlay, (frame_width - 305, 5), (frame_width - 5, 150), (50, 50, 50), -1)
79
+ frame_bgr = cv2.addWeighted(overlay, 0.7, frame_bgr, 0.3, 0)
80
+
81
+ # Left panel: Stress Indicators
82
+ y_offset = 25
83
+ cv2.putText(frame_bgr, "STRESS INDICATORS:", (10, y_offset),
84
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
85
+ y_offset += 25
86
+
87
+ stress_aus = [
88
+ (au01_active, au01_intensity, "AU01-BrowRaise"),
89
+ (au04_active, au04_intensity, "AU04-BrowLower"),
90
+ (au07_active, au07_intensity, "AU07-LidTight"),
91
+ (au17_active, au17_intensity, "AU17-ChinRaise"),
92
+ (au23_active, au23_intensity, "AU23-LipTight"),
93
+ (au24_active, au24_intensity, "AU24-LipPress")
94
+ ]
95
+
96
+ stress_count = 0
97
+ for active, intensity, name in stress_aus:
98
+ if active:
99
+ stress_count += 1
100
+ color = (0, 0, 255) if active else (150, 150, 150)
101
+ cv2.putText(frame_bgr, f"{name}: {intensity:.0f}%",
102
+ (15, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.4, color, 1)
103
+ y_offset += 22
104
 
105
+ # Right panel: Positive Indicators
106
+ y_offset = 25
107
+ x_right = frame_width - 300
108
+ cv2.putText(frame_bgr, "POSITIVE:", (x_right, y_offset),
109
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
110
+ y_offset += 25
111
 
112
+ positive_aus = [
113
+ (au06_active, au06_intensity, "AU06-Cheek"),
114
+ (au12_active, au12_intensity, "AU12-Smile"),
115
+ (au14_active, au14_intensity, "AU14-Dimple"),
116
+ (au26_active, au26_intensity, "AU26-Jaw")
117
+ ]
118
 
119
+ positive_count = 0
120
+ for active, intensity, name in positive_aus:
121
+ if active:
122
+ positive_count += 1
123
+ color = (0, 255, 0) if active else (150, 150, 150)
124
+ cv2.putText(frame_bgr, f"{name}: {intensity:.0f}%",
125
+ (x_right, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.4, color, 1)
126
+ y_offset += 22
127
+
128
+ # Bottom status bar
129
+ cv2.rectangle(frame_bgr, (0, frame_height - 40), (frame_width, frame_height), (40, 40, 40), -1)
130
+
131
+ # Simple stress calculation
132
+ stress_score = (au01_intensity + au04_intensity + au07_intensity +
133
+ au17_intensity + au23_intensity + au24_intensity) / 6
134
+
135
+ if stress_score > 50:
136
+ status = "πŸ”΄ STRESSED"
137
+ status_color = (0, 0, 255)
138
+ elif stress_score > 25:
139
+ status = "🟑 POSSIBLY STRESSED"
140
+ status_color = (0, 165, 255)
141
  else:
142
+ status = "🟒 NOT STRESSED"
143
+ status_color = (0, 255, 0)
144
+
145
+ cv2.putText(frame_bgr, f"{status} | Score: {stress_score:.1f}/100 | Stress AUs: {stress_count}/6 | Positive: {positive_count}/4",
146
+ (10, frame_height - 12), cv2.FONT_HERSHEY_SIMPLEX, 0.6, status_color, 2)
147
+
148
+ # Convert back to RGB for Gradio
149
+ output_frame = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
150
+
151
+ # Create detailed text output
152
+ analysis_text = f"""
153
+ 🎯 CURRENT ANALYSIS:
154
+
155
+ {status}
156
+ Stress Score: {stress_score:.1f}/100
157
+
158
+ πŸ“Š STRESS INDICATORS:
159
+ - AU01 (Inner Brow): {au01_intensity:.0f}% {'βœ“ ACTIVE' if au01_active else ''}
160
+ - AU04 (Brow Lower): {au04_intensity:.0f}% {'βœ“ ACTIVE' if au04_active else ''}
161
+ - AU07 (Lid Tighten): {au07_intensity:.0f}% {'βœ“ ACTIVE' if au07_active else ''}
162
+ - AU17 (Chin Raise): {au17_intensity:.0f}% {'βœ“ ACTIVE' if au17_active else ''}
163
+ - AU23 (Lip Tighten): {au23_intensity:.0f}% {'βœ“ ACTIVE' if au23_active else ''}
164
+ - AU24 (Lip Press): {au24_intensity:.0f}% {'βœ“ ACTIVE' if au24_active else ''}
165
+
166
+ 😊 POSITIVE INDICATORS:
167
+ - AU06 (Cheek Raise): {au06_intensity:.0f}% {'βœ“ ACTIVE' if au06_active else ''}
168
+ - AU12 (Smile): {au12_intensity:.0f}% {'βœ“ ACTIVE' if au12_active else ''}
169
+ - AU14 (Dimpler): {au14_intensity:.0f}% {'βœ“ ACTIVE' if au14_active else ''}
170
+ - AU26 (Jaw Drop): {au26_intensity:.0f}% {'βœ“ ACTIVE' if au26_active else ''}
171
+ """
172
+
173
+ return output_frame, analysis_text
174
 
175
  except Exception as e:
176
+ return frame, f"❌ Error: {str(e)}"
177
 
178
 
179
+ # Create Gradio Interface
180
+ with gr.Blocks(title="Real-Time Stress Detection", theme=gr.themes.Soft()) as demo:
181
+ gr.Markdown("# 🧠 Real-Time 10-AU Stress Detection System")
182
+ gr.Markdown("### Live Facial Expression Analysis - Based on FACS")
183
+ gr.Markdown("*Research Guide: Prof. Anup Nandy*")
184
 
185
  with gr.Row():
186
+ with gr.Column(scale=2):
187
+ webcam = gr.Image(
188
  sources=["webcam"],
189
+ streaming=True,
190
+ label="πŸ“Ή Live Webcam Feed"
191
  )
 
192
 
193
  gr.Markdown("""
194
+ ### πŸ“ Instructions:
195
+ 1. **Allow camera access** when prompted
196
+ 2. **Position your face** clearly in the frame
197
+ 3. **Analysis runs automatically** in real-time
198
+ 4. Watch the overlay for instant AU detection
199
  """)
200
+
201
+ with gr.Column(scale=1):
202
+ analysis_output = gr.Textbox(
203
+ label="πŸ“Š Real-Time Analysis",
204
+ lines=20,
205
+ value="Waiting for camera..."
206
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
 
208
  gr.Markdown("""
209
  ---
210
+ ### πŸ”¬ Action Units Being Tracked:
211
+
212
+ **Stress Indicators:** AU01, AU04, AU07, AU17, AU23, AU24
213
+ **Positive Indicators:** AU06, AU12, AU14, AU26
214
+
215
+ The system analyzes your facial expressions in real-time and provides instant feedback
216
+ on stress levels based on the Facial Action Coding System (FACS).
217
  """)
218
+
219
+ # Process streaming webcam
220
+ webcam.stream(
221
+ fn=process_frame,
222
+ inputs=[webcam],
223
+ outputs=[webcam, analysis_output],
224
+ stream_every=0.1 # Process every 100ms (10 FPS)
225
+ )
226
 
 
227
  if __name__ == "__main__":
228
+ demo.launch()