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