File size: 4,839 Bytes
568b799
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import cv2
import mediapipe as mp
import numpy as np
import gradio as gr

# Initialize MediaPipe Pose.
mp_pose = mp.solutions.pose
pose = mp_pose.Pose(
    static_image_mode=False,
    model_complexity=1,
    enable_segmentation=False,
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5
)

# Initialize MediaPipe Face Mesh.
mp_face_mesh = mp.solutions.face_mesh
face_mesh = mp_face_mesh.FaceMesh(
    static_image_mode=False,
    max_num_faces=1,
    refine_landmarks=True,
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5
)

def process_frame(image):
    """
    Processes an input image (from the webcam) by:
      1. Converting from RGB (Gradio default) to BGR for OpenCV.
      2. Flipping the frame for a mirror view.
      3. Creating a black background.
      4. Using MediaPipe Pose to draw body landmarks (excluding facial parts) and compute the shoulder center.
      5. Using MediaPipe Face Mesh to draw the full facial mesh and extract the chin point.
      6. Drawing a neck line from the shoulder center to the chin.
      7. Converting the result back to RGB for display.
    """
    # Convert the input image from RGB (Gradio default) to BGR.
    frame = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    
    # Flip the frame horizontally for a mirror view.
    frame = cv2.flip(frame, 1)
    
    # Create a black background image of the same size.
    output = np.zeros_like(frame)
    
    # Convert frame to RGB for MediaPipe processing.
    rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    
    # --- Body Posture Analysis using MediaPipe Pose ---
    pose_results = pose.process(rgb_frame)
    shoulder_center = None
    if pose_results.pose_landmarks:
        h, w, _ = frame.shape
        landmarks = [(int(lm.x * w), int(lm.y * h)) for lm in pose_results.pose_landmarks.landmark]
        
        # Draw body skeleton (only drawing non-facial landmarks, i.e. indices 11 and above).
        for connection in mp_pose.POSE_CONNECTIONS:
            start_idx, end_idx = connection
            if start_idx >= 11 and end_idx >= 11:
                if start_idx < len(landmarks) and end_idx < len(landmarks):
                    cv2.line(output, landmarks[start_idx], landmarks[end_idx], (255, 255, 0), 2)
        
        # Draw landmarks as yellow circles.
        for i, pt in enumerate(landmarks):
            if i >= 11:
                cv2.circle(output, pt, 3, (255, 255, 0), -1)
        
        # Calculate shoulder center using landmarks 11 (left shoulder) and 12 (right shoulder).
        if len(landmarks) > 12:
            left_shoulder = landmarks[11]
            right_shoulder = landmarks[12]
            shoulder_center = ((left_shoulder[0] + right_shoulder[0]) // 2,
                               (left_shoulder[1] + right_shoulder[1]) // 2)
            cv2.circle(output, shoulder_center, 4, (0, 255, 255), -1)
    
    # --- Facemesh Analysis using MediaPipe Face Mesh ---
    chin_point = None
    fm_results = face_mesh.process(rgb_frame)
    if fm_results.multi_face_landmarks:
        for face_landmarks in fm_results.multi_face_landmarks:
            h, w, _ = frame.shape
            fm_points = [(int(lm.x * w), int(lm.y * h)) for lm in face_landmarks.landmark]
            
            # Draw red lines connecting facial landmarks.
            for connection in mp_face_mesh.FACEMESH_TESSELATION:
                start_idx, end_idx = connection
                if start_idx < len(fm_points) and end_idx < len(fm_points):
                    cv2.line(output, fm_points[start_idx], fm_points[end_idx], (0, 0, 255), 1)
            
            # Draw green dots for each facial landmark.
            for pt in fm_points:
                cv2.circle(output, pt, 2, (0, 255, 0), -1)
            
            # Extract the chin point (landmark 152 is generally at the bottom of the chin).
            if len(face_landmarks.landmark) > 152:
                lm = face_landmarks.landmark[152]
                chin_point = (int(lm.x * w), int(lm.y * h))
                cv2.circle(output, chin_point, 4, (0, 0, 255), -1)
            break  # Process only the first detected face.
    
    # --- Draw the Neck Line ---
    if shoulder_center and chin_point:
        cv2.line(output, shoulder_center, chin_point, (0, 255, 255), 2)
    
    # Convert the processed image back to RGB for display.
    output_rgb = cv2.cvtColor(output, cv2.COLOR_BGR2RGB)
    return output_rgb

# Create the Gradio interface.
iface = gr.Interface(
    fn=process_frame,
    inputs=gr.Image(source="webcam", type="numpy", label="Webcam"),
    outputs=gr.Image(type="numpy", label="Processed Output"),
    live=True,
    title="Body Posture & Neck Analysis (No Face Pose)",
    description="Webcam-based analysis using MediaPipe Pose and Face Mesh."
)

# Launch the Gradio app.
iface.launch()