File size: 8,959 Bytes
0a79be4
2e0ce7a
 
 
1899f78
 
 
2e0ce7a
1899f78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e0ce7a
 
 
 
 
 
 
 
 
 
1899f78
2e0ce7a
1899f78
 
2e0ce7a
1899f78
 
2e0ce7a
 
1899f78
 
 
2e0ce7a
 
1899f78
2e0ce7a
 
 
1899f78
 
 
2e0ce7a
 
 
1899f78
2e0ce7a
 
 
 
 
 
 
 
 
 
 
1899f78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e0ce7a
1899f78
 
 
 
 
 
2e0ce7a
1899f78
 
 
 
 
 
2e0ce7a
1899f78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e0ce7a
1899f78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e0ce7a
 
1899f78
2e0ce7a
b8e7e64
1899f78
 
 
 
 
b8e7e64
 
1899f78
 
b8e7e64
1899f78
 
b8e7e64
2e0ce7a
 
1899f78
 
 
 
 
2e0ce7a
1899f78
 
 
 
 
 
b8e7e64
2e0ce7a
 
 
1899f78
 
 
 
 
 
 
2e0ce7a
1899f78
 
 
 
 
 
 
 
b8e7e64
0a79be4
1899f78
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
import gradio as gr
import cv2
import mediapipe as mp
import numpy as np
import base64
from io import BytesIO
from PIL import Image

# Import your detector classes
from stress_detection import (
    AU01Detector, AU04Detector, AU06Detector, AU07Detector,
    AU12Detector, AU14Detector, AU17Detector, AU23Detector,
    AU24Detector, AU26Detector, mp_face_mesh
)

# Initialize all detectors globally
au01 = AU01Detector(window_size=10)
au04 = AU04Detector(window_size=10)
au06 = AU06Detector(window_size=10)
au07 = AU07Detector(window_size=10)
au12 = AU12Detector(window_size=10)
au14 = AU14Detector(window_size=10)
au17 = AU17Detector(window_size=10)
au23 = AU23Detector(window_size=10)
au24 = AU24Detector(window_size=10)
au26 = AU26Detector(window_size=10)

detectors = [au01, au04, au06, au07, au12, au14, au17, au23, au24, au26]

# Initialize MediaPipe
face_mesh = mp_face_mesh.FaceMesh(
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5,
    refine_landmarks=True
)

def process_frame(frame):
    """
    Process a single frame from webcam
    Returns: processed frame with overlays + stress analysis
    """
    if frame is None:
        return None, "No frame received"
    
    try:
        # Convert from RGB (Gradio) to BGR (OpenCV)
        frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
        frame_height, frame_width = frame_bgr.shape[:2]
        
        # Process with MediaPipe
        rgb_frame = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
        results = face_mesh.process(rgb_frame)
        
        if not results.multi_face_landmarks:
            cv2.putText(frame_bgr, "No face detected", (50, 50),
                       cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
            return cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB), "⚠️ No face detected"
        
        landmarks = results.multi_face_landmarks[0].landmark
        
        # Detect all AUs
        au01_active, au01_intensity = au01.detect(landmarks, frame_width, frame_height)
        au04_active, au04_intensity = au04.detect(landmarks, frame_width, frame_height)
        au06_active, au06_intensity = au06.detect(landmarks, frame_width, frame_height)
        au07_active, au07_intensity = au07.detect(landmarks, frame_width, frame_height)
        au12_active, au12_intensity = au12.detect(landmarks, frame_width, frame_height)
        au14_active, au14_intensity = au14.detect(landmarks, frame_width, frame_height)
        au17_active, au17_intensity = au17.detect(landmarks, frame_width, frame_height)
        au23_active, au23_intensity = au23.detect(landmarks, frame_width, frame_height)
        au24_active, au24_intensity = au24.detect(landmarks, frame_width, frame_height)
        au26_active, au26_intensity = au26.detect(landmarks, frame_width, frame_height)
        
        # Draw overlays (like your original code)
        overlay = frame_bgr.copy()
        
        # Create semi-transparent overlay panels
        cv2.rectangle(overlay, (5, 5), (300, 200), (50, 50, 50), -1)
        cv2.rectangle(overlay, (frame_width - 305, 5), (frame_width - 5, 150), (50, 50, 50), -1)
        frame_bgr = cv2.addWeighted(overlay, 0.7, frame_bgr, 0.3, 0)
        
        # Left panel: Stress Indicators
        y_offset = 25
        cv2.putText(frame_bgr, "STRESS INDICATORS:", (10, y_offset),
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
        y_offset += 25
        
        stress_aus = [
            (au01_active, au01_intensity, "AU01-BrowRaise"),
            (au04_active, au04_intensity, "AU04-BrowLower"),
            (au07_active, au07_intensity, "AU07-LidTight"),
            (au17_active, au17_intensity, "AU17-ChinRaise"),
            (au23_active, au23_intensity, "AU23-LipTight"),
            (au24_active, au24_intensity, "AU24-LipPress")
        ]
        
        stress_count = 0
        for active, intensity, name in stress_aus:
            if active:
                stress_count += 1
            color = (0, 0, 255) if active else (150, 150, 150)
            cv2.putText(frame_bgr, f"{name}: {intensity:.0f}%",
                       (15, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.4, color, 1)
            y_offset += 22
        
        # Right panel: Positive Indicators
        y_offset = 25
        x_right = frame_width - 300
        cv2.putText(frame_bgr, "POSITIVE:", (x_right, y_offset),
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
        y_offset += 25
        
        positive_aus = [
            (au06_active, au06_intensity, "AU06-Cheek"),
            (au12_active, au12_intensity, "AU12-Smile"),
            (au14_active, au14_intensity, "AU14-Dimple"),
            (au26_active, au26_intensity, "AU26-Jaw")
        ]
        
        positive_count = 0
        for active, intensity, name in positive_aus:
            if active:
                positive_count += 1
            color = (0, 255, 0) if active else (150, 150, 150)
            cv2.putText(frame_bgr, f"{name}: {intensity:.0f}%",
                       (x_right, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.4, color, 1)
            y_offset += 22
        
        # Bottom status bar
        cv2.rectangle(frame_bgr, (0, frame_height - 40), (frame_width, frame_height), (40, 40, 40), -1)
        
        # Simple stress calculation
        stress_score = (au01_intensity + au04_intensity + au07_intensity + 
                       au17_intensity + au23_intensity + au24_intensity) / 6
        
        if stress_score > 50:
            status = "πŸ”΄ STRESSED"
            status_color = (0, 0, 255)
        elif stress_score > 25:
            status = "🟑 POSSIBLY STRESSED"
            status_color = (0, 165, 255)
        else:
            status = "🟒 NOT STRESSED"
            status_color = (0, 255, 0)
        
        cv2.putText(frame_bgr, f"{status} | Score: {stress_score:.1f}/100 | Stress AUs: {stress_count}/6 | Positive: {positive_count}/4",
                   (10, frame_height - 12), cv2.FONT_HERSHEY_SIMPLEX, 0.6, status_color, 2)
        
        # Convert back to RGB for Gradio
        output_frame = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
        
        # Create detailed text output
        analysis_text = f"""
🎯 CURRENT ANALYSIS:

{status}
Stress Score: {stress_score:.1f}/100

πŸ“Š STRESS INDICATORS:
- AU01 (Inner Brow): {au01_intensity:.0f}% {'βœ“ ACTIVE' if au01_active else ''}
- AU04 (Brow Lower): {au04_intensity:.0f}% {'βœ“ ACTIVE' if au04_active else ''}
- AU07 (Lid Tighten): {au07_intensity:.0f}% {'βœ“ ACTIVE' if au07_active else ''}
- AU17 (Chin Raise): {au17_intensity:.0f}% {'βœ“ ACTIVE' if au17_active else ''}
- AU23 (Lip Tighten): {au23_intensity:.0f}% {'βœ“ ACTIVE' if au23_active else ''}
- AU24 (Lip Press): {au24_intensity:.0f}% {'βœ“ ACTIVE' if au24_active else ''}

😊 POSITIVE INDICATORS:
- AU06 (Cheek Raise): {au06_intensity:.0f}% {'βœ“ ACTIVE' if au06_active else ''}
- AU12 (Smile): {au12_intensity:.0f}% {'βœ“ ACTIVE' if au12_active else ''}
- AU14 (Dimpler): {au14_intensity:.0f}% {'βœ“ ACTIVE' if au14_active else ''}
- AU26 (Jaw Drop): {au26_intensity:.0f}% {'βœ“ ACTIVE' if au26_active else ''}
        """
        
        return output_frame, analysis_text
        
    except Exception as e:
        return frame, f"❌ Error: {str(e)}"


# Create Gradio Interface
with gr.Blocks(title="Real-Time Stress Detection", theme=gr.themes.Soft()) as demo:
    gr.Markdown("# 🧠 Real-Time 10-AU Stress Detection System")
    gr.Markdown("### Live Facial Expression Analysis - Based on FACS")
    gr.Markdown("*Research Guide: Prof. Anup Nandy*")
    
    with gr.Row():
        with gr.Column(scale=2):
            webcam = gr.Image(
                sources=["webcam"],
                streaming=True,
                label="πŸ“Ή Live Webcam Feed"
            )
            
            gr.Markdown("""
            ### πŸ“ Instructions:
            1. **Allow camera access** when prompted
            2. **Position your face** clearly in the frame
            3. **Analysis runs automatically** in real-time
            4. Watch the overlay for instant AU detection
            """)
        
        with gr.Column(scale=1):
            analysis_output = gr.Textbox(
                label="πŸ“Š Real-Time Analysis",
                lines=20,
                value="Waiting for camera..."
            )
    
    gr.Markdown("""
    ---
    ### πŸ”¬ Action Units Being Tracked:
    
    **Stress Indicators:** AU01, AU04, AU07, AU17, AU23, AU24  
    **Positive Indicators:** AU06, AU12, AU14, AU26
    
    The system analyzes your facial expressions in real-time and provides instant feedback 
    on stress levels based on the Facial Action Coding System (FACS).
    """)
    
    # Process streaming webcam
    webcam.stream(
        fn=process_frame,
        inputs=[webcam],
        outputs=[webcam, analysis_output],
        stream_every=0.1  # Process every 100ms (10 FPS)
    )

if __name__ == "__main__":
    demo.launch()