Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import cv2 | |
| import mediapipe as mp | |
| import numpy as np | |
| import time | |
| from sklearn.ensemble import IsolationForest | |
| # Encapsulated workout functions | |
| def bicep_curl(): | |
| import cv2 | |
| import mediapipe as mp | |
| import numpy as np | |
| import time | |
| from sklearn.ensemble import IsolationForest | |
| # Mediapipe utilities | |
| mp_drawing = mp.solutions.drawing_utils | |
| mp_pose = mp.solutions.pose | |
| # Function to calculate angles between three points | |
| def calculate_angle(a, b, c): | |
| a = np.array(a) | |
| b = np.array(b) | |
| c = np.array(c) | |
| radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0]) | |
| angle = np.abs(np.degrees(radians)) | |
| if angle > 180.0: | |
| angle = 360 - angle | |
| return angle | |
| # Function to draw text with a background | |
| def draw_text_with_background(image, text, position, font, font_scale, color, thickness, bg_color, padding=10): | |
| text_size = cv2.getTextSize(text, font, font_scale, thickness)[0] | |
| text_x, text_y = position | |
| box_coords = ( | |
| (text_x - padding, text_y - padding), | |
| (text_x + text_size[0] + padding, text_y + text_size[1] + padding), | |
| ) | |
| cv2.rectangle(image, box_coords[0], box_coords[1], bg_color, cv2.FILLED) | |
| cv2.putText(image, text, (text_x, text_y + text_size[1]), font, font_scale, color, thickness) | |
| # Real-time feedback for single rep | |
| def analyze_single_rep(rep, rep_data): | |
| """Provide actionable feedback for a single rep.""" | |
| feedback = [] | |
| avg_rom = np.mean([r["ROM"] for r in rep_data]) | |
| avg_tempo = np.mean([r["Tempo"] for r in rep_data]) | |
| avg_smoothness = np.mean([r["Smoothness"] for r in rep_data]) | |
| if rep["ROM"] < avg_rom * 0.8: | |
| feedback.append("Extend arm more") | |
| if rep["Tempo"] < avg_tempo * 0.8: | |
| feedback.append("Slow down") | |
| if rep["Smoothness"] > avg_smoothness * 1.2: | |
| feedback.append("Move smoothly") | |
| return " | ".join(feedback) if feedback else "Good rep!" | |
| # Post-workout feedback function with Isolation Forest | |
| def analyze_workout_with_isolation_forest(rep_data): | |
| if not rep_data: | |
| print("No reps completed.") | |
| return | |
| print("\n--- Post-Workout Summary ---") | |
| # Convert rep_data to a feature matrix | |
| features = np.array([[rep["ROM"], rep["Tempo"], rep["Smoothness"]] for rep in rep_data]) | |
| # Train Isolation Forest | |
| model = IsolationForest(contamination=0.2, random_state=42) | |
| predictions = model.fit_predict(features) | |
| # Analyze reps | |
| for i, (rep, prediction) in enumerate(zip(rep_data, predictions), 1): | |
| status = "Good" if prediction == 1 else "Anomalous" | |
| reason = [] | |
| if prediction == -1: # If anomalous | |
| if rep["ROM"] < np.mean(features[:, 0]) - np.std(features[:, 0]): | |
| reason.append("Low ROM") | |
| if rep["Tempo"] < np.mean(features[:, 1]) - np.std(features[:, 1]): | |
| reason.append("Too Fast") | |
| if rep["Smoothness"] > np.mean(features[:, 2]) + np.std(features[:, 2]): | |
| reason.append("Jerky Movement") | |
| reason_str = ", ".join(reason) if reason else "None" | |
| print(f"Rep {i}: {status} | ROM: {rep['ROM']:.2f}, Tempo: {rep['Tempo']:.2f}s, Smoothness: {rep['Smoothness']:.2f} | Reason: {reason_str}") | |
| # Main workout tracking function | |
| def main(): | |
| cap = cv2.VideoCapture(0) | |
| counter = 0 # Rep counter | |
| stage = None # Movement stage | |
| max_reps = 10 | |
| rep_data = [] # Store metrics for each rep | |
| feedback = "" # Real-time feedback for the video feed | |
| workout_start_time = None # Timer start | |
| with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose: | |
| while cap.isOpened(): | |
| ret, frame = cap.read() | |
| if not ret: | |
| print("Failed to grab frame.") | |
| break | |
| # Initialize workout start time | |
| if workout_start_time is None: | |
| workout_start_time = time.time() | |
| # Timer | |
| elapsed_time = time.time() - workout_start_time | |
| timer_text = f"Timer: {int(elapsed_time)}s" | |
| # Convert frame to RGB | |
| image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
| image.flags.writeable = False | |
| results = pose.process(image) | |
| # Convert back to BGR | |
| image.flags.writeable = True | |
| image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) | |
| # Check if pose landmarks are detected | |
| if results.pose_landmarks: | |
| landmarks = results.pose_landmarks.landmark | |
| # Extract key joints | |
| shoulder = [ | |
| landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].x, | |
| landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].y | |
| ] | |
| elbow = [ | |
| landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].x, | |
| landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].y | |
| ] | |
| wrist = [ | |
| landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].x, | |
| landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].y | |
| ] | |
| # Check visibility of key joints | |
| visibility_threshold = 0.5 | |
| if (landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].visibility < visibility_threshold or | |
| landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].visibility < visibility_threshold or | |
| landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].visibility < visibility_threshold): | |
| draw_text_with_background(image, "Ensure all key joints are visible!", (50, 150), | |
| cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 255, 255), 5, (0, 0, 255)) | |
| cv2.imshow('Workout Feedback', image) | |
| continue # Skip processing if joints are not visible | |
| # Calculate the angle | |
| angle = calculate_angle(shoulder, elbow, wrist) | |
| # Stage logic for counting reps | |
| if angle > 160 and stage != "down": | |
| stage = "down" | |
| start_time = time.time() # Start timing for the rep | |
| start_angle = angle # Record the starting angle | |
| # Stop the program if it's the 10th rep down stage | |
| if counter == max_reps: | |
| print("Workout complete at rep 10 (down stage)!") | |
| break | |
| elif angle < 40 and stage == "down": | |
| stage = "up" | |
| counter += 1 | |
| end_time = time.time() # End timing for the rep | |
| end_angle = angle # Record the ending angle | |
| # Calculate rep metrics | |
| rom = start_angle - end_angle # Range of Motion | |
| tempo = end_time - start_time # Duration of the rep | |
| smoothness = np.std([start_angle, end_angle]) # Dummy smoothness metric | |
| rep_data.append({"ROM": rom, "Tempo": tempo, "Smoothness": smoothness}) | |
| # Analyze the rep using Isolation Forest | |
| feedback = analyze_single_rep(rep_data[-1], rep_data) | |
| # Wireframe color based on form | |
| wireframe_color = (0, 255, 0) if stage == "up" or stage == "down" else (0, 0, 255) | |
| # Draw wireframe | |
| mp_drawing.draw_landmarks( | |
| image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS, | |
| mp_drawing.DrawingSpec(color=wireframe_color, thickness=5, circle_radius=4), | |
| mp_drawing.DrawingSpec(color=wireframe_color, thickness=5, circle_radius=4) | |
| ) | |
| # Display reps, stage, timer, and feedback | |
| draw_text_with_background(image, f"Reps: {counter}", (50, 150), | |
| cv2.FONT_HERSHEY_SIMPLEX, 3, (255, 255, 255), 5, (0, 0, 0)) | |
| draw_text_with_background(image, f"Stage: {stage if stage else 'N/A'}", (50, 300), | |
| cv2.FONT_HERSHEY_SIMPLEX, 3, (255, 255, 255), 5, (0, 0, 0)) | |
| draw_text_with_background(image, timer_text, (1000, 50), # Timer in the top-right corner | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 3, (0, 0, 0)) | |
| draw_text_with_background(image, feedback, (50, 450), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 3, (0, 0, 0)) | |
| # Show video feed | |
| cv2.imshow('Workout Feedback', image) | |
| # Break if 'q' is pressed | |
| if cv2.waitKey(10) & 0xFF == ord('q'): | |
| break | |
| cap.release() | |
| cv2.destroyAllWindows() | |
| # Post-workout analysis | |
| analyze_workout_with_isolation_forest(rep_data) | |
| if __name__ == "__main__": | |
| main() | |
| def lateral_raise(): | |
| import cv2 | |
| import mediapipe as mp | |
| import numpy as np | |
| import time | |
| from sklearn.ensemble import IsolationForest | |
| # Mediapipe utilities | |
| mp_drawing = mp.solutions.drawing_utils | |
| mp_pose = mp.solutions.pose | |
| # Function to calculate lateral raise angle | |
| def calculate_angle_for_lateral_raise(shoulder, wrist): | |
| """ | |
| Calculate the angle of the arm relative to the horizontal plane | |
| passing through the shoulder. | |
| """ | |
| horizontal_reference = np.array([1, 0]) # Horizontal vector | |
| arm_vector = np.array([wrist[0] - shoulder[0], wrist[1] - shoulder[1]]) | |
| dot_product = np.dot(horizontal_reference, arm_vector) | |
| magnitude_reference = np.linalg.norm(horizontal_reference) | |
| magnitude_arm = np.linalg.norm(arm_vector) | |
| if magnitude_arm == 0 or magnitude_reference == 0: | |
| return 0 | |
| cos_angle = dot_product / (magnitude_reference * magnitude_arm) | |
| angle = np.arccos(np.clip(cos_angle, -1.0, 1.0)) | |
| return np.degrees(angle) | |
| # Function to draw text with a background | |
| def draw_text_with_background(image, text, position, font, font_scale, color, thickness, bg_color, padding=10): | |
| text_size = cv2.getTextSize(text, font, font_scale, thickness)[0] | |
| text_x, text_y = position | |
| box_coords = ( | |
| (text_x - padding, text_y - padding), | |
| (text_x + text_size[0] + padding, text_y + text_size[1] + padding), | |
| ) | |
| cv2.rectangle(image, box_coords[0], box_coords[1], bg_color, cv2.FILLED) | |
| cv2.putText(image, text, (text_x, text_y + text_size[1]), font, font_scale, color, thickness) | |
| # Function to check if all required joints are visible | |
| def are_key_joints_visible(landmarks, visibility_threshold=0.5): | |
| """ | |
| Ensure that all required joints are visible based on their visibility scores. | |
| """ | |
| required_joints = [ | |
| mp_pose.PoseLandmark.LEFT_SHOULDER.value, | |
| mp_pose.PoseLandmark.RIGHT_SHOULDER.value, | |
| mp_pose.PoseLandmark.LEFT_WRIST.value, | |
| mp_pose.PoseLandmark.RIGHT_WRIST.value, | |
| ] | |
| for joint in required_joints: | |
| if landmarks[joint].visibility < visibility_threshold: | |
| return False | |
| return True | |
| # Real-time feedback for single rep | |
| def analyze_single_rep(rep, rep_data): | |
| """Provide actionable feedback for a single rep.""" | |
| feedback = [] | |
| # Calculate averages from previous reps | |
| avg_rom = np.mean([r["ROM"] for r in rep_data]) if rep_data else 0 | |
| avg_tempo = np.mean([r["Tempo"] for r in rep_data]) if rep_data else 0 | |
| # Dynamic tempo thresholds | |
| lower_tempo_threshold = 2.0 # Minimum grace threshold for faster tempo | |
| upper_tempo_threshold = 9.0 # Maximum grace threshold for slower tempo | |
| # Adjust thresholds after a few reps | |
| if len(rep_data) > 3: | |
| lower_tempo_threshold = max(2.0, avg_tempo * 0.7) | |
| upper_tempo_threshold = min(9.0, avg_tempo * 1.3) | |
| # Feedback for ROM | |
| if rep["ROM"] < 30: # Minimum ROM threshold | |
| feedback.append("Lift arm higher") | |
| elif rep_data and rep["ROM"] < avg_rom * 0.8: | |
| feedback.append("Increase ROM") | |
| # Feedback for Tempo | |
| if rep["Tempo"] < lower_tempo_threshold: # Tempo too fast | |
| feedback.append("Slow down") | |
| elif rep["Tempo"] > upper_tempo_threshold: # Tempo too slow | |
| feedback.append("Speed up") | |
| return feedback | |
| # Post-workout feedback function | |
| def analyze_workout_with_isolation_forest(rep_data): | |
| if not rep_data: | |
| print("No reps completed.") | |
| return | |
| print("\n--- Post-Workout Summary ---") | |
| # Filter valid reps for recalculating thresholds | |
| valid_reps = [rep for rep in rep_data if rep["ROM"] > 20] # Ignore very low ROM reps | |
| if not valid_reps: | |
| print("No valid reps to analyze.") | |
| return | |
| features = np.array([[rep["ROM"], rep["Tempo"]] for rep in valid_reps]) | |
| avg_rom = np.mean(features[:, 0]) | |
| avg_tempo = np.mean(features[:, 1]) | |
| std_rom = np.std(features[:, 0]) | |
| std_tempo = np.std(features[:, 1]) | |
| # Adjusted bounds for anomalies | |
| rom_lower_bound = max(20, avg_rom - std_rom * 2) | |
| tempo_lower_bound = max(1.0, avg_tempo - std_tempo * 2) | |
| tempo_upper_bound = min(10.0, avg_tempo + std_tempo * 2) | |
| print(f"ROM Lower Bound: {rom_lower_bound}") | |
| print(f"Tempo Bounds: {tempo_lower_bound}-{tempo_upper_bound}") | |
| # Anomaly detection | |
| for i, rep in enumerate(valid_reps, 1): | |
| feedback = [] | |
| if rep["ROM"] < rom_lower_bound: | |
| feedback.append("Low ROM") | |
| if rep["Tempo"] < tempo_lower_bound: | |
| feedback.append("Too Fast") | |
| elif rep["Tempo"] > tempo_upper_bound: | |
| feedback.append("Too Slow") | |
| if feedback: | |
| print(f"Rep {i}: Anomalous | Feedback: {', '.join(feedback[:1])}") | |
| # Use Isolation Forest for secondary anomaly detection | |
| model = IsolationForest(contamination=0.1, random_state=42) # Reduced contamination | |
| predictions = model.fit_predict(features) | |
| for i, prediction in enumerate(predictions, 1): | |
| if prediction == -1: # Outlier | |
| print(f"Rep {i}: Isolation Forest flagged this rep as anomalous.") | |
| # Main workout tracking function | |
| def main(): | |
| cap = cv2.VideoCapture(0) | |
| counter = 0 # Rep counter | |
| stage = None # Movement stage | |
| feedback = [] # Real-time feedback for the video feed | |
| rep_data = [] # Store metrics for each rep | |
| angles_during_rep = [] # Track angles during a single rep | |
| workout_start_time = None # Timer start | |
| with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose: | |
| while cap.isOpened(): | |
| ret, frame = cap.read() | |
| if not ret: | |
| print("Failed to grab frame.") | |
| break | |
| # Initialize workout start time | |
| if workout_start_time is None: | |
| workout_start_time = time.time() | |
| # Timer | |
| elapsed_time = time.time() - workout_start_time | |
| timer_text = f"Timer: {int(elapsed_time)}s" | |
| # Convert the image to RGB | |
| image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
| image.flags.writeable = False | |
| results = pose.process(image) | |
| # Convert back to BGR | |
| image.flags.writeable = True | |
| image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) | |
| # Check if pose landmarks are detected | |
| if results.pose_landmarks: | |
| landmarks = results.pose_landmarks.landmark | |
| # Check if key joints are visible | |
| if not are_key_joints_visible(landmarks): | |
| draw_text_with_background( | |
| image, "Ensure all joints are visible", (50, 50), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 2, (0, 0, 255) | |
| ) | |
| cv2.imshow("Lateral Raise Tracker", image) | |
| continue | |
| # Extract key joints | |
| left_shoulder = [ | |
| landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].x, | |
| landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].y, | |
| ] | |
| left_wrist = [ | |
| landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].x, | |
| landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].y, | |
| ] | |
| # Calculate angle for lateral raise | |
| angle = calculate_angle_for_lateral_raise(left_shoulder, left_wrist) | |
| # Track angles during a rep | |
| if stage == "up" or stage == "down": | |
| angles_during_rep.append(angle) | |
| # Stage logic for counting reps | |
| if angle < 20 and stage != "down": | |
| stage = "down" | |
| if counter == 10: # Stop on the down stage of the 10th rep | |
| print("Workout complete! 10 reps reached.") | |
| break | |
| # Calculate ROM for the completed rep | |
| if len(angles_during_rep) > 1: | |
| rom = max(angles_during_rep) - min(angles_during_rep) | |
| else: | |
| rom = 0.0 | |
| tempo = elapsed_time | |
| print(f"Rep {counter + 1}: ROM={rom:.2f}, Tempo={tempo:.2f}s") | |
| # Record metrics for the rep | |
| rep_data.append({ | |
| "ROM": rom, | |
| "Tempo": tempo, | |
| }) | |
| # Reset angles and timer for the next rep | |
| angles_during_rep = [] | |
| workout_start_time = time.time() # Reset timer | |
| if 70 <= angle <= 110 and stage == "down": | |
| stage = "up" | |
| counter += 1 | |
| # Analyze feedback | |
| feedback = analyze_single_rep(rep_data[-1], rep_data) | |
| # Determine wireframe color | |
| wireframe_color = (0, 255, 0) if not feedback else (0, 0, 255) | |
| # Display feedback | |
| draw_text_with_background(image, f"Reps: {counter}", (50, 50), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 2, (0, 0, 0)) | |
| draw_text_with_background(image, " | ".join(feedback), (50, 120), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 2, (0, 0, 0)) | |
| draw_text_with_background(image, timer_text, (50, 190), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 2, (0, 0, 0)) | |
| # Render detections with wireframe color | |
| mp_drawing.draw_landmarks( | |
| image, | |
| results.pose_landmarks, | |
| mp_pose.POSE_CONNECTIONS, | |
| mp_drawing.DrawingSpec(color=wireframe_color, thickness=2, circle_radius=2), | |
| mp_drawing.DrawingSpec(color=wireframe_color, thickness=2, circle_radius=2), | |
| ) | |
| # Display the image | |
| cv2.imshow("Lateral Raise Tracker", image) | |
| if cv2.waitKey(10) & 0xFF == ord("q"): | |
| break | |
| cap.release() | |
| cv2.destroyAllWindows() | |
| # Post-workout analysis | |
| analyze_workout_with_isolation_forest(rep_data) | |
| if __name__ == "__main__": | |
| main() | |
| def shoulder_press(): | |
| import cv2 | |
| import mediapipe as mp | |
| import numpy as np | |
| import time | |
| # Mediapipe utilities | |
| mp_drawing = mp.solutions.drawing_utils | |
| mp_pose = mp.solutions.pose | |
| # Function to calculate angles | |
| def calculate_angle(point_a, point_b, point_c): | |
| vector_ab = np.array([point_a[0] - point_b[0], point_a[1] - point_b[1]]) | |
| vector_cb = np.array([point_c[0] - point_b[0], point_c[1] - point_b[1]]) | |
| dot_product = np.dot(vector_ab, vector_cb) | |
| magnitude_ab = np.linalg.norm(vector_ab) | |
| magnitude_cb = np.linalg.norm(vector_cb) | |
| if magnitude_ab == 0 or magnitude_cb == 0: | |
| return 0 | |
| cos_angle = dot_product / (magnitude_ab * magnitude_cb) | |
| angle = np.arccos(np.clip(cos_angle, -1.0, 1.0)) | |
| return np.degrees(angle) | |
| # Function to check if all required joints are visible | |
| def are_key_joints_visible(landmarks, visibility_threshold=0.5): | |
| required_joints = [ | |
| mp_pose.PoseLandmark.LEFT_SHOULDER.value, | |
| mp_pose.PoseLandmark.RIGHT_SHOULDER.value, | |
| mp_pose.PoseLandmark.LEFT_ELBOW.value, | |
| mp_pose.PoseLandmark.RIGHT_ELBOW.value, | |
| mp_pose.PoseLandmark.LEFT_WRIST.value, | |
| mp_pose.PoseLandmark.RIGHT_WRIST.value, | |
| ] | |
| for joint in required_joints: | |
| if landmarks[joint].visibility < visibility_threshold: | |
| return False | |
| return True | |
| # Function to draw text with a background | |
| def draw_text_with_background(image, text, position, font, font_scale, color, thickness, bg_color, padding=10): | |
| text_size = cv2.getTextSize(text, font, font_scale, thickness)[0] | |
| text_x, text_y = position | |
| box_coords = ( | |
| (text_x - padding, text_y - padding), | |
| (text_x + text_size[0] + padding, text_y + text_size[1] + padding), | |
| ) | |
| cv2.rectangle(image, box_coords[0], box_coords[1], bg_color, cv2.FILLED) | |
| cv2.putText(image, text, (text_x, text_y + text_size[1]), font, font_scale, color, thickness) | |
| # Main workout tracking function | |
| def main(): | |
| cap = cv2.VideoCapture(0) | |
| counter = 0 | |
| stage = None | |
| feedback = "" | |
| workout_start_time = None | |
| rep_start_time = None | |
| with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose: | |
| while cap.isOpened(): | |
| ret, frame = cap.read() | |
| if not ret: | |
| print("Failed to grab frame.") | |
| break | |
| # Initialize workout start time | |
| if workout_start_time is None: | |
| workout_start_time = time.time() | |
| # Timer | |
| elapsed_time = time.time() - workout_start_time | |
| timer_text = f"Timer: {int(elapsed_time)}s" | |
| # Convert the image to RGB | |
| image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
| image.flags.writeable = False | |
| results = pose.process(image) | |
| # Convert back to BGR | |
| image.flags.writeable = True | |
| image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) | |
| # Check if pose landmarks are detected | |
| if results.pose_landmarks: | |
| landmarks = results.pose_landmarks.landmark | |
| # Check if key joints are visible | |
| if not are_key_joints_visible(landmarks): | |
| feedback = "Ensure all joints are visible" | |
| draw_text_with_background( | |
| image, feedback, (50, 50), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 2, (0, 0, 255) | |
| ) | |
| cv2.imshow("Shoulder Press Tracker", image) | |
| continue | |
| # Extract key joints for both arms | |
| left_shoulder = [ | |
| landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].x, | |
| landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].y, | |
| ] | |
| left_elbow = [ | |
| landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].x, | |
| landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].y, | |
| ] | |
| left_wrist = [ | |
| landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].x, | |
| landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].y, | |
| ] | |
| right_shoulder = [ | |
| landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value].x, | |
| landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value].y, | |
| ] | |
| right_elbow = [ | |
| landmarks[mp_pose.PoseLandmark.RIGHT_ELBOW.value].x, | |
| landmarks[mp_pose.PoseLandmark.RIGHT_ELBOW.value].y, | |
| ] | |
| right_wrist = [ | |
| landmarks[mp_pose.PoseLandmark.RIGHT_WRIST.value].x, | |
| landmarks[mp_pose.PoseLandmark.RIGHT_WRIST.value].y, | |
| ] | |
| # Calculate angles | |
| left_elbow_angle = calculate_angle(left_shoulder, left_elbow, left_wrist) | |
| right_elbow_angle = calculate_angle(right_shoulder, right_elbow, right_wrist) | |
| # Check starting and ending positions | |
| if 80 <= left_elbow_angle <= 100 and 80 <= right_elbow_angle <= 100 and stage != "down": | |
| stage = "down" | |
| if counter == 10: | |
| feedback = "Workout complete! 10 reps done." | |
| draw_text_with_background(image, feedback, (50, 120), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 2, (0, 0, 255)) | |
| cv2.imshow("Shoulder Press Tracker", image) | |
| break | |
| if rep_start_time is not None: | |
| tempo = time.time() - rep_start_time | |
| feedback = f"Rep {counter} completed! Tempo: {tempo:.2f}s" | |
| rep_start_time = None | |
| elif left_elbow_angle > 160 and right_elbow_angle > 160 and stage == "down": | |
| stage = "up" | |
| counter += 1 | |
| rep_start_time = time.time() | |
| # Wireframe color | |
| wireframe_color = (0, 255, 0) if "completed" in feedback or "Good" in feedback else (0, 0, 255) | |
| # Display feedback | |
| draw_text_with_background(image, f"Reps: {counter}", (50, 50), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 2, (0, 0, 0)) | |
| draw_text_with_background(image, feedback, (50, 120), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 2, (0, 0, 0)) | |
| draw_text_with_background(image, timer_text, (50, 190), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 2, (0, 0, 0)) | |
| # Render detections with wireframe color | |
| mp_drawing.draw_landmarks( | |
| image, | |
| results.pose_landmarks, | |
| mp_pose.POSE_CONNECTIONS, | |
| mp_drawing.DrawingSpec(color=wireframe_color, thickness=2, circle_radius=2), | |
| mp_drawing.DrawingSpec(color=wireframe_color, thickness=2, circle_radius=2), | |
| ) | |
| # Display the image | |
| cv2.imshow("Shoulder Press Tracker", image) | |
| if cv2.waitKey(10) & 0xFF == ord("q"): | |
| break | |
| cap.release() | |
| cv2.destroyAllWindows() | |
| if __name__ == "__main__": | |
| main() | |
| # Streamlit configuration | |
| st.set_page_config(page_title="Workout Tracker", page_icon="💪", layout="centered") | |
| # Custom CSS for styling | |
| st.markdown( | |
| ''' | |
| <style> | |
| body { | |
| background-color: #001f3f; | |
| color: #7FDBFF; | |
| font-family: Arial, sans-serif; | |
| } | |
| .stButton > button { | |
| background-color: #0074D9; | |
| color: white; | |
| border-radius: 5px; | |
| padding: 10px 20px; | |
| font-size: 18px; | |
| } | |
| .stButton > button:hover { | |
| background-color: #7FDBFF; | |
| color: #001f3f; | |
| } | |
| </style> | |
| ''', | |
| unsafe_allow_html=True | |
| ) | |
| # Title and Introduction | |
| st.title("Workout Tracker") | |
| st.markdown("Welcome to the **Workout Tracker App**! Select your desired workout below and receive real-time feedback as you exercise.") | |
| # Check webcam availability | |
| def check_webcam(): | |
| try: | |
| cap = cv2.VideoCapture(0) | |
| if not cap.isOpened(): | |
| st.error("Webcam not detected! Please ensure a webcam is connected.") | |
| return False | |
| cap.release() | |
| return True | |
| except Exception as e: | |
| st.error(f"Error accessing webcam: {e}") | |
| return False | |
| # Workout Selection | |
| workout_option = st.radio("Select Your Workout:", ["Bicep Curl", "Lateral Raise", "Shoulder Press"]) | |
| # Start Button | |
| if st.button("Start Workout"): | |
| if not check_webcam(): | |
| st.stop() | |
| if workout_option == "Bicep Curl": | |
| st.write("Launching Bicep Curl Tracker...") | |
| bicep_curl() | |
| elif workout_option == "Lateral Raise": | |
| st.write("Launching Lateral Raise Tracker...") | |
| lateral_raise() | |
| elif workout_option == "Shoulder Press": | |
| st.write("Launching Shoulder Press Tracker...") | |
| shoulder_press() | |
| # Footer | |
| st.markdown("---") | |
| st.markdown("**Note**: Close the workout window or press 'q' in the camera feed to stop the workout.") | |