Spaces:
Sleeping
Sleeping
Upload utils.py
Browse files
utils.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import mediapipe as mp
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
correct = cv2.imread('right.png')
|
| 6 |
+
correct = cv2.cvtColor(correct, cv2.COLOR_BGR2RGB)
|
| 7 |
+
incorrect = cv2.imread('wrong.png')
|
| 8 |
+
incorrect = cv2.cvtColor(incorrect, cv2.COLOR_BGR2RGB)
|
| 9 |
+
|
| 10 |
+
def draw_rounded_rect(img, rect_start, rect_end, corner_width, box_color):
|
| 11 |
+
|
| 12 |
+
x1, y1 = rect_start
|
| 13 |
+
x2, y2 = rect_end
|
| 14 |
+
w = corner_width
|
| 15 |
+
|
| 16 |
+
# draw filled rectangles
|
| 17 |
+
cv2.rectangle(img, (x1 + w, y1), (x2 - w, y1 + w), box_color, -1)
|
| 18 |
+
cv2.rectangle(img, (x1 + w, y2 - w), (x2 - w, y2), box_color, -1)
|
| 19 |
+
cv2.rectangle(img, (x1, y1 + w), (x1 + w, y2 - w), box_color, -1)
|
| 20 |
+
cv2.rectangle(img, (x2 - w, y1 + w), (x2, y2 - w), box_color, -1)
|
| 21 |
+
cv2.rectangle(img, (x1 + w, y1 + w), (x2 - w, y2 - w), box_color, -1)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# draw filled ellipses
|
| 25 |
+
cv2.ellipse(img, (x1 + w, y1 + w), (w, w),
|
| 26 |
+
angle = 0, startAngle = -90, endAngle = -180, color = box_color, thickness = -1)
|
| 27 |
+
|
| 28 |
+
cv2.ellipse(img, (x2 - w, y1 + w), (w, w),
|
| 29 |
+
angle = 0, startAngle = 0, endAngle = -90, color = box_color, thickness = -1)
|
| 30 |
+
|
| 31 |
+
cv2.ellipse(img, (x1 + w, y2 - w), (w, w),
|
| 32 |
+
angle = 0, startAngle = 90, endAngle = 180, color = box_color, thickness = -1)
|
| 33 |
+
|
| 34 |
+
cv2.ellipse(img, (x2 - w, y2 - w), (w, w),
|
| 35 |
+
angle = 0, startAngle = 0, endAngle = 90, color = box_color, thickness = -1)
|
| 36 |
+
|
| 37 |
+
return img
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def draw_dotted_line(frame, lm_coord, start, end, line_color):
|
| 43 |
+
pix_step = 0
|
| 44 |
+
|
| 45 |
+
for i in range(start, end+1, 8):
|
| 46 |
+
cv2.circle(frame, (lm_coord[0], i+pix_step), 2, line_color, -1, lineType=cv2.LINE_AA)
|
| 47 |
+
|
| 48 |
+
return frame
|
| 49 |
+
|
| 50 |
+
def draw_text(
|
| 51 |
+
img,
|
| 52 |
+
msg,
|
| 53 |
+
width = 7,
|
| 54 |
+
font=cv2.FONT_HERSHEY_SIMPLEX,
|
| 55 |
+
pos=(0, 0),
|
| 56 |
+
font_scale=1,
|
| 57 |
+
font_thickness=2,
|
| 58 |
+
text_color=(0, 255, 0),
|
| 59 |
+
text_color_bg=(0, 0, 0),
|
| 60 |
+
box_offset=(20, 10),
|
| 61 |
+
overlay_image = False,
|
| 62 |
+
overlay_type = None
|
| 63 |
+
):
|
| 64 |
+
|
| 65 |
+
offset = box_offset
|
| 66 |
+
x, y = pos
|
| 67 |
+
text_size, _ = cv2.getTextSize(msg, font, font_scale, font_thickness)
|
| 68 |
+
text_w, text_h = text_size
|
| 69 |
+
|
| 70 |
+
rec_start = tuple(p - o for p, o in zip(pos, offset))
|
| 71 |
+
rec_end = tuple(m + n - o for m, n, o in zip((x + text_w, y + text_h), offset, (25, 0)))
|
| 72 |
+
|
| 73 |
+
resize_height = 0
|
| 74 |
+
|
| 75 |
+
if overlay_image:
|
| 76 |
+
resize_height = rec_end[1] - rec_start[1]
|
| 77 |
+
# print("Height: ", resize_height)
|
| 78 |
+
# print("Width: ", rec_end[0] - rec_start[0])
|
| 79 |
+
img = draw_rounded_rect(img, rec_start, (rec_end[0]+resize_height, rec_end[1]), width, text_color_bg)
|
| 80 |
+
if overlay_type == "correct":
|
| 81 |
+
overlay_res = cv2.resize(correct, (resize_height, resize_height), interpolation = cv2.INTER_AREA)
|
| 82 |
+
elif overlay_type == "incorrect":
|
| 83 |
+
overlay_res = cv2.resize(incorrect, (resize_height, resize_height), interpolation = cv2.INTER_AREA)
|
| 84 |
+
|
| 85 |
+
img[rec_start[1]:rec_start[1]+resize_height, rec_start[0]+width:rec_start[0]+width+resize_height] = overlay_res
|
| 86 |
+
|
| 87 |
+
else:
|
| 88 |
+
img = draw_rounded_rect(img, rec_start, rec_end, width, text_color_bg)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
cv2.putText(
|
| 92 |
+
img,
|
| 93 |
+
msg,
|
| 94 |
+
(int(rec_start[0]+resize_height + 8), int(y + text_h + font_scale - 1)),
|
| 95 |
+
font,
|
| 96 |
+
font_scale,
|
| 97 |
+
text_color,
|
| 98 |
+
font_thickness,
|
| 99 |
+
cv2.LINE_AA,
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
return text_size
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def find_angle(p1, p2, ref_pt = np.array([0,0])):
|
| 109 |
+
p1_ref = p1 - ref_pt
|
| 110 |
+
p2_ref = p2 - ref_pt
|
| 111 |
+
|
| 112 |
+
cos_theta = (np.dot(p1_ref,p2_ref)) / (1.0 * np.linalg.norm(p1_ref) * np.linalg.norm(p2_ref))
|
| 113 |
+
theta = np.arccos(np.clip(cos_theta, -1.0, 1.0))
|
| 114 |
+
|
| 115 |
+
degree = int(180 / np.pi) * theta
|
| 116 |
+
|
| 117 |
+
return int(degree)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def get_landmark_array(pose_landmark, key, frame_width, frame_height):
|
| 124 |
+
|
| 125 |
+
denorm_x = int(pose_landmark[key].x * frame_width)
|
| 126 |
+
denorm_y = int(pose_landmark[key].y * frame_height)
|
| 127 |
+
|
| 128 |
+
return np.array([denorm_x, denorm_y])
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def get_landmark_features(kp_results, dict_features, feature, frame_width, frame_height):
|
| 134 |
+
|
| 135 |
+
if feature == 'nose':
|
| 136 |
+
return get_landmark_array(kp_results, dict_features[feature], frame_width, frame_height)
|
| 137 |
+
|
| 138 |
+
elif feature == 'left' or 'right':
|
| 139 |
+
shldr_coord = get_landmark_array(kp_results, dict_features[feature]['shoulder'], frame_width, frame_height)
|
| 140 |
+
elbow_coord = get_landmark_array(kp_results, dict_features[feature]['elbow'], frame_width, frame_height)
|
| 141 |
+
wrist_coord = get_landmark_array(kp_results, dict_features[feature]['wrist'], frame_width, frame_height)
|
| 142 |
+
hip_coord = get_landmark_array(kp_results, dict_features[feature]['hip'], frame_width, frame_height)
|
| 143 |
+
knee_coord = get_landmark_array(kp_results, dict_features[feature]['knee'], frame_width, frame_height)
|
| 144 |
+
ankle_coord = get_landmark_array(kp_results, dict_features[feature]['ankle'], frame_width, frame_height)
|
| 145 |
+
foot_coord = get_landmark_array(kp_results, dict_features[feature]['foot'], frame_width, frame_height)
|
| 146 |
+
|
| 147 |
+
return shldr_coord, elbow_coord, wrist_coord, hip_coord, knee_coord, ankle_coord, foot_coord
|
| 148 |
+
|
| 149 |
+
else:
|
| 150 |
+
raise ValueError("feature needs to be either 'nose', 'left' or 'right")
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def get_mediapipe_pose(
|
| 154 |
+
static_image_mode = False,
|
| 155 |
+
model_complexity = 1,
|
| 156 |
+
smooth_landmarks = True,
|
| 157 |
+
min_detection_confidence = 0.5,
|
| 158 |
+
min_tracking_confidence = 0.5
|
| 159 |
+
|
| 160 |
+
):
|
| 161 |
+
pose = mp.solutions.pose.Pose(
|
| 162 |
+
static_image_mode = static_image_mode,
|
| 163 |
+
model_complexity = model_complexity,
|
| 164 |
+
smooth_landmarks = smooth_landmarks,
|
| 165 |
+
min_detection_confidence = min_detection_confidence,
|
| 166 |
+
min_tracking_confidence = min_tracking_confidence
|
| 167 |
+
)
|
| 168 |
+
return pose
|