Muscle_Memory / app.py
zforkash's picture
Update 5
8f2f623 verified
raw
history blame
31.3 kB
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.")