Update exercises/hammer_curl.py
Browse files- exercises/hammer_curl.py +155 -100
exercises/hammer_curl.py
CHANGED
|
@@ -1,114 +1,169 @@
|
|
| 1 |
-
import
|
| 2 |
-
import numpy as np
|
| 3 |
from pose_estimation.angle_calculation import calculate_angle
|
| 4 |
-
from voice_feedback.feedback import provide_hammer_curl_feedback , speak
|
| 5 |
-
|
| 6 |
|
| 7 |
class HammerCurl:
|
| 8 |
def __init__(self):
|
| 9 |
self.counter_right = 0
|
| 10 |
self.counter_left = 0
|
| 11 |
-
self.stage_right =
|
| 12 |
-
self.stage_left =
|
| 13 |
-
|
| 14 |
-
self.angle_threshold = 40 # Angle threshold for misalignment
|
| 15 |
-
self.flexion_angle_up = 155 # Flexion angle for 'up' stage
|
| 16 |
-
self.flexion_angle_down = 35 # Flexion angle for 'down' stage
|
| 17 |
|
| 18 |
-
self.
|
| 19 |
-
self.
|
|
|
|
| 20 |
|
| 21 |
def calculate_shoulder_elbow_hip_angle(self, shoulder, elbow, hip):
|
| 22 |
-
|
|
|
|
| 23 |
return calculate_angle(elbow, shoulder, hip)
|
| 24 |
|
| 25 |
def calculate_shoulder_elbow_wrist(self, shoulder, elbow, wrist):
|
| 26 |
-
|
|
|
|
| 27 |
return calculate_angle(shoulder, elbow, wrist)
|
| 28 |
|
| 29 |
-
def track_hammer_curl(self,
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
#
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
#
|
| 53 |
-
self.
|
| 54 |
-
self.
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
self.
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import mediapipe as mp
|
|
|
|
| 2 |
from pose_estimation.angle_calculation import calculate_angle
|
|
|
|
|
|
|
| 3 |
|
| 4 |
class HammerCurl:
|
| 5 |
def __init__(self):
|
| 6 |
self.counter_right = 0
|
| 7 |
self.counter_left = 0
|
| 8 |
+
self.stage_right = "down"
|
| 9 |
+
self.stage_left = "down"
|
| 10 |
+
self.mp_pose = mp.solutions.pose
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
+
self.angle_alignment_threshold = 40 # Threshold for shoulder-elbow-hip alignment
|
| 13 |
+
self.angle_curl_extended = 155 # Angle when arm is considered extended
|
| 14 |
+
self.angle_curl_flexed = 47 # Angle when arm is considered flexed
|
| 15 |
|
| 16 |
def calculate_shoulder_elbow_hip_angle(self, shoulder, elbow, hip):
|
| 17 |
+
# Calculate the angle between elbow, shoulder, and hip (for alignment).
|
| 18 |
+
# elbow is the vertex.
|
| 19 |
return calculate_angle(elbow, shoulder, hip)
|
| 20 |
|
| 21 |
def calculate_shoulder_elbow_wrist(self, shoulder, elbow, wrist):
|
| 22 |
+
# Calculate the angle between shoulder, elbow, and wrist (for curl).
|
| 23 |
+
# elbow is the vertex.
|
| 24 |
return calculate_angle(shoulder, elbow, wrist)
|
| 25 |
|
| 26 |
+
def track_hammer_curl(self, landmarks_mp, frame_width, frame_height):
|
| 27 |
+
lm = landmarks_mp # shortcut
|
| 28 |
+
|
| 29 |
+
# Right arm landmarks
|
| 30 |
+
shoulder_right = [int(lm[self.mp_pose.PoseLandmark.RIGHT_SHOULDER.value].x * frame_width),
|
| 31 |
+
int(lm[self.mp_pose.PoseLandmark.RIGHT_SHOULDER.value].y * frame_height)]
|
| 32 |
+
elbow_right = [int(lm[self.mp_pose.PoseLandmark.RIGHT_ELBOW.value].x * frame_width),
|
| 33 |
+
int(lm[self.mp_pose.PoseLandmark.RIGHT_ELBOW.value].y * frame_height)]
|
| 34 |
+
hip_right = [int(lm[self.mp_pose.PoseLandmark.RIGHT_HIP.value].x * frame_width),
|
| 35 |
+
int(lm[self.mp_pose.PoseLandmark.RIGHT_HIP.value].y * frame_height)]
|
| 36 |
+
wrist_right = [int(lm[self.mp_pose.PoseLandmark.RIGHT_WRIST.value].x * frame_width),
|
| 37 |
+
int(lm[self.mp_pose.PoseLandmark.RIGHT_WRIST.value].y * frame_height)]
|
| 38 |
+
|
| 39 |
+
# Left arm landmarks
|
| 40 |
+
shoulder_left = [int(lm[self.mp_pose.PoseLandmark.LEFT_SHOULDER.value].x * frame_width),
|
| 41 |
+
int(lm[self.mp_pose.PoseLandmark.LEFT_SHOULDER.value].y * frame_height)]
|
| 42 |
+
elbow_left = [int(lm[self.mp_pose.PoseLandmark.LEFT_ELBOW.value].x * frame_width),
|
| 43 |
+
int(lm[self.mp_pose.PoseLandmark.LEFT_ELBOW.value].y * frame_height)]
|
| 44 |
+
hip_left = [int(lm[self.mp_pose.PoseLandmark.LEFT_HIP.value].x * frame_width),
|
| 45 |
+
int(lm[self.mp_pose.PoseLandmark.LEFT_HIP.value].y * frame_height)]
|
| 46 |
+
wrist_left = [int(lm[self.mp_pose.PoseLandmark.LEFT_WRIST.value].x * frame_width),
|
| 47 |
+
int(lm[self.mp_pose.PoseLandmark.LEFT_WRIST.value].y * frame_height)]
|
| 48 |
+
|
| 49 |
+
# Calculate curl angles
|
| 50 |
+
angle_right_curl = self.calculate_shoulder_elbow_wrist(shoulder_right, elbow_right, wrist_right)
|
| 51 |
+
angle_left_curl = self.calculate_shoulder_elbow_wrist(shoulder_left, elbow_left, wrist_left)
|
| 52 |
+
|
| 53 |
+
# Calculate alignment angles
|
| 54 |
+
angle_right_alignment = self.calculate_shoulder_elbow_hip_angle(shoulder_right, elbow_right, hip_right)
|
| 55 |
+
angle_left_alignment = self.calculate_shoulder_elbow_hip_angle(shoulder_left, elbow_left, hip_left)
|
| 56 |
+
|
| 57 |
+
# Stage and Counter Logic - Right Arm
|
| 58 |
+
if angle_right_curl > self.angle_curl_extended:
|
| 59 |
+
self.stage_right = "down"
|
| 60 |
+
elif angle_right_curl < self.angle_curl_flexed and self.stage_right == "down":
|
| 61 |
+
self.stage_right = "up"
|
| 62 |
+
self.counter_right += 1
|
| 63 |
+
|
| 64 |
+
# Stage and Counter Logic - Left Arm
|
| 65 |
+
if angle_left_curl > self.angle_curl_extended:
|
| 66 |
+
self.stage_left = "down"
|
| 67 |
+
elif angle_left_curl < self.angle_curl_flexed and self.stage_left == "down":
|
| 68 |
+
self.stage_left = "up"
|
| 69 |
+
self.counter_left += 1
|
| 70 |
+
|
| 71 |
+
feedback_data = self._get_hammer_curl_feedback(angle_left_curl, angle_right_curl,
|
| 72 |
+
angle_left_alignment, angle_right_alignment,
|
| 73 |
+
self.stage_left, self.stage_right)
|
| 74 |
+
|
| 75 |
+
return {
|
| 76 |
+
"counter_left": self.counter_left,
|
| 77 |
+
"stage_left": self.stage_left,
|
| 78 |
+
"angle_left_curl": angle_left_curl,
|
| 79 |
+
"angle_left_alignment": angle_left_alignment,
|
| 80 |
+
"counter_right": self.counter_right,
|
| 81 |
+
"stage_right": self.stage_right,
|
| 82 |
+
"angle_right_curl": angle_right_curl,
|
| 83 |
+
"angle_right_alignment": angle_right_alignment,
|
| 84 |
+
"feedback_left": feedback_data["left_arm"],
|
| 85 |
+
"feedback_right": feedback_data["right_arm"]
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
def _get_hammer_curl_feedback(self, angle_left_curl, angle_right_curl,
|
| 89 |
+
angle_left_alignment, angle_right_alignment,
|
| 90 |
+
stage_left, stage_right):
|
| 91 |
+
msg_l = ""
|
| 92 |
+
msg_r = ""
|
| 93 |
+
|
| 94 |
+
if abs(angle_left_alignment) > self.angle_alignment_threshold:
|
| 95 |
+
msg_l += "Keep left elbow closer to body. "
|
| 96 |
+
if abs(angle_right_alignment) > self.angle_alignment_threshold:
|
| 97 |
+
msg_r += "Keep right elbow closer to body. "
|
| 98 |
+
|
| 99 |
+
if stage_left == "up":
|
| 100 |
+
if not msg_l: msg_l += "Left arm flexed. "
|
| 101 |
+
elif stage_left == "down":
|
| 102 |
+
if not msg_l: msg_l += "Left arm extended. "
|
| 103 |
+
|
| 104 |
+
if stage_right == "up":
|
| 105 |
+
if not msg_r: msg_r += "Right arm flexed. "
|
| 106 |
+
elif stage_right == "down":
|
| 107 |
+
if not msg_r: msg_r += "Right arm extended. "
|
| 108 |
+
|
| 109 |
+
if not msg_l.strip(): # Use strip() to check if it's empty after potential spaces
|
| 110 |
+
msg_l = "Left: OK"
|
| 111 |
+
if not msg_r.strip():
|
| 112 |
+
msg_r = "Right: OK"
|
| 113 |
+
|
| 114 |
+
return {"left_arm": msg_l.strip(), "right_arm": msg_r.strip()}
|
| 115 |
+
|
| 116 |
+
def get_drawing_annotations(self, landmarks_mp, frame_width, frame_height, exercise_data_dict):
|
| 117 |
+
annotations = []
|
| 118 |
+
lm = landmarks_mp
|
| 119 |
+
|
| 120 |
+
shoulder_right = [int(lm[self.mp_pose.PoseLandmark.RIGHT_SHOULDER.value].x * frame_width),
|
| 121 |
+
int(lm[self.mp_pose.PoseLandmark.RIGHT_SHOULDER.value].y * frame_height)]
|
| 122 |
+
elbow_right = [int(lm[self.mp_pose.PoseLandmark.RIGHT_ELBOW.value].x * frame_width),
|
| 123 |
+
int(lm[self.mp_pose.PoseLandmark.RIGHT_ELBOW.value].y * frame_height)]
|
| 124 |
+
wrist_right = [int(lm[self.mp_pose.PoseLandmark.RIGHT_WRIST.value].x * frame_width),
|
| 125 |
+
int(lm[self.mp_pose.PoseLandmark.RIGHT_WRIST.value].y * frame_height)]
|
| 126 |
+
shoulder_left = [int(lm[self.mp_pose.PoseLandmark.LEFT_SHOULDER.value].x * frame_width),
|
| 127 |
+
int(lm[self.mp_pose.PoseLandmark.LEFT_SHOULDER.value].y * frame_height)]
|
| 128 |
+
elbow_left = [int(lm[self.mp_pose.PoseLandmark.LEFT_ELBOW.value].x * frame_width),
|
| 129 |
+
int(lm[self.mp_pose.PoseLandmark.LEFT_ELBOW.value].y * frame_height)]
|
| 130 |
+
wrist_left = [int(lm[self.mp_pose.PoseLandmark.LEFT_WRIST.value].x * frame_width),
|
| 131 |
+
int(lm[self.mp_pose.PoseLandmark.LEFT_WRIST.value].y * frame_height)]
|
| 132 |
+
|
| 133 |
+
color_bgr = [255, 0, 0]
|
| 134 |
+
thickness = 4
|
| 135 |
+
annotations.append({"type": "line", "start_point": shoulder_left, "end_point": elbow_left, "color_bgr": color_bgr, "thickness": thickness})
|
| 136 |
+
annotations.append({"type": "line", "start_point": elbow_left, "end_point": wrist_left, "color_bgr": color_bgr, "thickness": thickness})
|
| 137 |
+
annotations.append({"type": "line", "start_point": shoulder_right, "end_point": elbow_right, "color_bgr": color_bgr, "thickness": thickness})
|
| 138 |
+
annotations.append({"type": "line", "start_point": elbow_right, "end_point": wrist_right, "color_bgr": color_bgr, "thickness": thickness})
|
| 139 |
+
|
| 140 |
+
radius = 8
|
| 141 |
+
annotations.append({"type": "circle", "center_point": shoulder_left, "radius": radius, "color_bgr": color_bgr, "filled": True})
|
| 142 |
+
# ... (add all other circle and text annotations as per the previous full version of get_drawing_annotations) ...
|
| 143 |
+
annotations.append({"type": "circle", "center_point": elbow_left, "radius": radius, "color_bgr": color_bgr, "filled": True})
|
| 144 |
+
annotations.append({"type": "circle", "center_point": wrist_left, "radius": radius, "color_bgr": color_bgr, "filled": True})
|
| 145 |
+
annotations.append({"type": "circle", "center_point": shoulder_right, "radius": radius, "color_bgr": color_bgr, "filled": True})
|
| 146 |
+
annotations.append({"type": "circle", "center_point": elbow_right, "radius": radius, "color_bgr": color_bgr, "filled": True})
|
| 147 |
+
annotations.append({"type": "circle", "center_point": wrist_right, "radius": radius, "color_bgr": color_bgr, "filled": True})
|
| 148 |
+
|
| 149 |
+
text_color_bgr = [255, 255, 255]
|
| 150 |
+
font_scale = 0.5
|
| 151 |
+
text_thickness = 2
|
| 152 |
+
if 'angle_left_curl' in exercise_data_dict:
|
| 153 |
+
annotations.append({"type": "text", "text_content": f"L Curl: {int(exercise_data_dict['angle_left_curl'])}",
|
| 154 |
+
"position": [elbow_left[0] + 10, elbow_left[1] - 20],
|
| 155 |
+
"font_scale": font_scale, "color_bgr": text_color_bgr, "thickness": text_thickness})
|
| 156 |
+
if 'angle_right_curl' in exercise_data_dict:
|
| 157 |
+
annotations.append({"type": "text", "text_content": f"R Curl: {int(exercise_data_dict['angle_right_curl'])}",
|
| 158 |
+
"position": [elbow_right[0] + 10, elbow_right[1] - 20],
|
| 159 |
+
"font_scale": font_scale, "color_bgr": text_color_bgr, "thickness": text_thickness})
|
| 160 |
+
|
| 161 |
+
if 'feedback_left' in exercise_data_dict: # Check if key exists
|
| 162 |
+
annotations.append({"type": "text", "text_content": f"L: {exercise_data_dict['feedback_left']}",
|
| 163 |
+
"position": [10, frame_height - 70 if frame_height > 100 else 30],
|
| 164 |
+
"font_scale": 0.6, "color_bgr": [0, 255, 0], "thickness": 2})
|
| 165 |
+
if 'feedback_right' in exercise_data_dict: # Check if key exists
|
| 166 |
+
annotations.append({"type": "text", "text_content": f"R: {exercise_data_dict['feedback_right']}",
|
| 167 |
+
"position": [10, frame_height - 40 if frame_height > 70 else 60],
|
| 168 |
+
"font_scale": 0.6, "color_bgr": [0, 255, 0], "thickness": 2})
|
| 169 |
+
return annotations
|