File size: 6,505 Bytes
d871c13 |
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 |
import streamlit as st
import cv2
import mediapipe as mp
import matplotlib.pyplot as plt
import numpy as np
# Define the function to calculate angle between the legs
def angle_between_the_legs(image):
"""
Calculate the angle between the legs using MediaPipe pose estimation.
Args:
image: Input image in BGR format.
Returns:
A tuple containing:
- The annotated image with visualization
- Left leg angle (degrees)
- Right leg angle (degrees)
- Angle between legs (degrees)
"""
# Initialize MediaPipe Pose
mp_pose = mp.solutions.pose
mp_drawing = mp.solutions.drawing_utils
# Convert the image to RGB (MediaPipe requires RGB images)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Initialize the Pose model
with mp_pose.Pose(static_image_mode=True, model_complexity=2, enable_segmentation=False) as pose:
# Process the image
results = pose.process(image_rgb)
# Check if pose landmarks were detected
if not results.pose_landmarks:
print("No pose landmarks detected.")
return image, None, None, None
# Create a copy of the image for annotation
annotated_image = image.copy()
# Get landmark coordinates
landmarks = results.pose_landmarks.landmark
# Get relevant landmarks for angle calculation
# Center hip point (mid-point between left and right hip)
mid_hip_x = (landmarks[mp_pose.PoseLandmark.LEFT_HIP.value].x +
landmarks[mp_pose.PoseLandmark.RIGHT_HIP.value].x) / 2
mid_hip_y = (landmarks[mp_pose.PoseLandmark.LEFT_HIP.value].y +
landmarks[mp_pose.PoseLandmark.RIGHT_HIP.value].y) / 2
# Get image dimensions for converting normalized coordinates
h, w, _ = annotated_image.shape
mid_hip = (int(mid_hip_x * w), int(mid_hip_y * h))
# Get coordinates for left and right ankles
left_ankle = (int(landmarks[mp_pose.PoseLandmark.LEFT_ANKLE.value].x * w),
int(landmarks[mp_pose.PoseLandmark.LEFT_ANKLE.value].y * h))
right_ankle = (int(landmarks[mp_pose.PoseLandmark.RIGHT_ANKLE.value].x * w),
int(landmarks[mp_pose.PoseLandmark.RIGHT_ANKLE.value].y * h))
# Draw the center vertical line from mid-hip down
center_bottom = (mid_hip[0], h)
cv2.line(annotated_image, mid_hip, center_bottom, (0, 255, 255), 2)
# Draw lines from mid-hip to each ankle
cv2.line(annotated_image, mid_hip, left_ankle, (255, 0, 0), 2) # Blue line to left ankle
cv2.line(annotated_image, mid_hip, right_ankle, (0, 0, 255), 2) # Red line to right ankle
# Calculate vectors
# Vector for center line (pointing down)
center_vector = np.array([0, 1]) # Vertical down
# Vector for left leg
left_leg_vector = np.array([left_ankle[0] - mid_hip[0], left_ankle[1] - mid_hip[1]])
if np.linalg.norm(left_leg_vector) > 0:
left_leg_vector = left_leg_vector / np.linalg.norm(left_leg_vector)
# Vector for right leg
right_leg_vector = np.array([right_ankle[0] - mid_hip[0], right_ankle[1] - mid_hip[1]])
if np.linalg.norm(right_leg_vector) > 0:
right_leg_vector = right_leg_vector / np.linalg.norm(right_leg_vector)
# Calculate angles using dot product: angle = arccos(dot(v1, v2))
# Note: We're using the normalized vectors for angle calculation
left_angle_rad = np.arccos(np.clip(np.dot(center_vector, left_leg_vector), -1.0, 1.0))
right_angle_rad = np.arccos(np.clip(np.dot(center_vector, right_leg_vector), -1.0, 1.0))
# Convert to degrees
left_angle_deg = np.degrees(left_angle_rad)
right_angle_deg = np.degrees(right_angle_rad)
# If left ankle is to the left of center, the angle is negative
if left_ankle[0] < mid_hip[0]:
left_angle_deg = -left_angle_deg
# If right ankle is to the right of center, the angle is positive
if right_ankle[0] > mid_hip[0]:
right_angle_deg = right_angle_deg
else:
right_angle_deg = -right_angle_deg
# Calculate the total angle between legs
angle_between_legs = 2 * (abs(left_angle_deg) + abs(right_angle_deg))
# Add text annotations with angle values
cv2.putText(annotated_image, f"Left angle: {left_angle_deg:.1f}°",
(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
cv2.putText(annotated_image, f"Right angle: {right_angle_deg:.1f}°",
(10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(annotated_image, f"Total angle: {angle_between_legs:.1f}°",
(10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
# Add marker for center hip point
cv2.circle(annotated_image, mid_hip, 5, (255, 255, 0), -1)
# Draw pose landmarks on the image
mp_drawing.draw_landmarks(
annotated_image,
results.pose_landmarks,
mp_pose.POSE_CONNECTIONS,
landmark_drawing_spec=mp_drawing.DrawingSpec(color=(0, 255, 0), thickness=2, circle_radius=2),
connection_drawing_spec=mp_drawing.DrawingSpec(color=(255, 0, 0), thickness=2))
# Convert the annotated image back to RGB for display
annotated_image_rgb = cv2.cvtColor(annotated_image, cv2.COLOR_BGR2RGB)
return annotated_image_rgb, left_angle_deg, right_angle_deg, angle_between_legs
# Streamlit app
st.title("Leg Angle Calculation using MediaPipe")
# Image uploader
uploaded_file = st.file_uploader("Upload an Image", type=["png", "jpg", "jpeg"])
if uploaded_file is not None:
# Read the image from the uploaded file
image = np.array(bytearray(uploaded_file.read()), dtype=np.uint8)
image = cv2.imdecode(image, cv2.IMREAD_COLOR)
# Get the leg angle analysis
annotated_image_rgb, left_angle, right_angle, total_angle = angle_between_the_legs(image)
if annotated_image_rgb is not None:
# Display the annotated image
st.image(annotated_image_rgb, caption=f"Leg Angle Analysis: Total = {total_angle:.1f}°", use_column_width=True)
# Print the results
st.write(f"Left leg angle: {left_angle:.1f}°")
st.write(f"Right leg angle: {right_angle:.1f}°")
st.write(f"Total angle between legs: {total_angle:.1f}°")
|