File size: 4,615 Bytes
c6abe34 | 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 | from personal_analysis.drawers.utils import get_center
def valid_point(p):
return (
p is not None and
len(p) == 2 and
p[0] is not None and
p[1] is not None
)
def valid_bbox(b):
"""Return False for None, 0, empty, or invalid bbox."""
if b == 0:
return False
else:
return True
def distance(p1, p2):
if p1 is None or p2 is None:
return None
return ((p1**2 + p2**2)**0.5)
def ball_hand(ball_loco, points, frames):
leave_frames = []
in_hand_prev = False
is_dribble = False
ball_is_head = False
prev_ball_valid = False
dist_thresh=40
for i, frame_id in enumerate(frames):
# ---- Safe Ball Center ----
ball_bbox = ball_loco[i] if i < len(ball_loco) else None
if valid_bbox(ball_bbox):
ball_center = get_center(ball_bbox)
else:
ball_center = None # No ball detected
prev_ball_valid = False
continue
# ---- Right wrist and face parts ----
joints = points[i] if i < len(points) else None
if joints is None:
in_hand = False
prev_ball_valid = True
in_hand_prev = in_hand
continue
right_wrist = joints[10]
right_soulder = joints[6]
nose = joints[0]
l_eye = joints[1]
r_eye = joints[2]
l_ear = joints[3]
r_ear = joints[4]
# ---- Validity check ----
if not (valid_point(ball_center) and valid_point(right_wrist)):
in_hand = False
else:
dx = ball_center[0] - right_wrist[0]
dy = ball_center[1] - right_wrist[1]
dist = distance(dx, dy)
# Increased threshold for high-res videos (1080p/4K)
if (dist < 100):
in_hand = True
else:
in_hand = False
# Only mark as dribble if it's significantly below the shoulder
if (ball_center[1] > (right_soulder[1] + 50)):
is_dribble = True
# Check if the head is wrongly detected as the ball
if not (valid_point(nose) and valid_point(r_eye) and valid_point(l_eye) and
valid_point(r_ear) and valid_point(l_ear)):
ball_is_head = False
else:
# More robust head check
dist_thresh_head = 80
distance_nose = distance(ball_center[0] - nose[0], ball_center[1] - nose[1])
distance_r_eye = distance(ball_center[0] - r_eye[0], ball_center[1] - r_eye[1])
distance_l_eye = distance(ball_center[0] - l_eye[0], ball_center[1] - l_eye[1])
distance_r_ear = distance(ball_center[0] - r_ear[0], ball_center[1] - r_ear[1])
distance_l_ear = distance(ball_center[0] - l_ear[0], ball_center[1] - l_ear[1])
if (distance_nose <= dist_thresh_head or distance_r_eye <= dist_thresh_head or
distance_l_eye <= dist_thresh_head or distance_r_ear <= dist_thresh_head or
distance_l_ear <= dist_thresh_head):
ball_is_head = True
# ---- Detect transition: ball was in hand → now not ----
if prev_ball_valid and in_hand_prev and not in_hand:
leave_frames.append(i)
prev_ball_valid = True
in_hand_prev = in_hand
is_dribble = False
ball_is_head = False
accurate_leave_frames = []
buffer_frames=10
last_kept = -10000000000
for f in leave_frames:
if f - last_kept > buffer_frames:
accurate_leave_frames.append(f)
last_kept = f
return accurate_leave_frames
def shot_started(points, leave_frames):
shot_start_frames = []
for frame_num in leave_frames:
start_found = False
start = max(0, frame_num - 20)
end = frame_num
for i in range(start, end):
if i >= len(points):
continue
joints = points[i]
if joints is None:
continue
right_shoulder = joints[6]
right_elbow = joints[8]
# Heuristic: Shot starts when elbow is near or above shoulder height
if (right_elbow[1] - 30) <= right_shoulder[1]:
shot_start_frames.append(i)
start_found = True
break
if not start_found:
# Fallback: Use the release frame itself minus a small buffer
shot_start_frames.append(max(0, frame_num - 10))
return shot_start_frames
|