chenemii commited on
Commit
f5fd0f0
·
1 Parent(s): 0723bf0

frame analysis

Browse files
Files changed (1) hide show
  1. app/models/swing_analyzer.py +39 -45
app/models/swing_analyzer.py CHANGED
@@ -17,74 +17,68 @@ def segment_swing(pose_data, detections, sample_rate=1):
17
  angles = calculate_joint_angles(keypoints)
18
  angles_by_frame[idx] = angles
19
 
 
 
 
 
 
 
 
 
20
  setup_end = frame_indices[0]
21
  initial_angles = angles_by_frame[frame_indices[0]]
22
  initial_shoulder = initial_angles.get("right_shoulder")
23
  initial_wrist = initial_angles.get("right_elbow")
24
-
25
  for idx in frame_indices[1:]:
26
  angles = angles_by_frame[idx]
27
  shoulder = angles.get("right_shoulder")
28
  wrist = angles.get("right_elbow")
29
- if shoulder and initial_shoulder and abs(shoulder - initial_shoulder) > 10:
30
- setup_end = idx
31
- break
32
- if wrist and initial_wrist and abs(wrist - initial_wrist) > 10:
33
- setup_end = idx
34
  break
35
 
 
36
  max_shoulder_angle = -1
37
- top_backswing_frame = setup_end
38
  for idx in frame_indices:
39
- if idx < setup_end:
40
  continue
41
  shoulder = angles_by_frame[idx].get("right_shoulder")
42
  if shoulder and shoulder > max_shoulder_angle:
43
  max_shoulder_angle = shoulder
44
  top_backswing_frame = idx
45
 
46
- # Find impact frame by looking for the point where the club head is at its lowest point
47
- # during the downswing, before it starts rising in the follow-through
48
  impact_frame = None
49
- min_wrist_y = float('inf')
50
- prev_wrist_y = None
51
- wrist_velocities = []
52
-
53
- # First pass: collect wrist positions and calculate velocities
54
- wrist_positions = []
55
- for idx in frame_indices:
56
- if idx < top_backswing_frame:
57
  continue
58
- keypoints = pose_data[idx]
59
- if len(keypoints) > 16:
60
- wrist_y = keypoints[16][1]
61
- wrist_positions.append((idx, wrist_y))
62
-
63
- # Calculate velocities between consecutive frames
64
- for i in range(1, len(wrist_positions)):
65
- idx, wrist_y = wrist_positions[i]
66
- prev_idx, prev_y = wrist_positions[i-1]
67
- velocity = (wrist_y - prev_y) / (idx - prev_idx)
68
- wrist_velocities.append((idx, velocity))
69
-
70
- # Find impact as the point where velocity changes from negative (downward) to positive (upward)
71
- for i in range(1, len(wrist_velocities)):
72
- idx, velocity = wrist_velocities[i]
73
- prev_idx, prev_velocity = wrist_velocities[i-1]
74
- if prev_velocity < 0 and velocity > 0: # Velocity changes from negative to positive
75
- impact_frame = prev_idx
76
- break
77
-
78
- # If no clear impact point found, use the frame with minimum wrist Y position
79
- if impact_frame is None:
80
- for idx, wrist_y in wrist_positions:
81
- if wrist_y < min_wrist_y:
82
- min_wrist_y = wrist_y
83
- impact_frame = idx
84
-
85
  if impact_frame is None:
86
  impact_frame = frame_indices[-1]
87
 
 
88
  for idx in frame_indices:
89
  if idx <= setup_end:
90
  swing_phases["setup"].append(idx)
 
17
  angles = calculate_joint_angles(keypoints)
18
  angles_by_frame[idx] = angles
19
 
20
+ # --- Dynamic Phase Segmentation ---
21
+ # 1. Setup: before any significant movement
22
+ # 2. Backswing: from end of setup to top of backswing
23
+ # 3. Downswing: from top of backswing to just before impact
24
+ # 4. Impact: frame(s) where ball first moves
25
+ # 5. Follow-through: after impact
26
+
27
+ # --- 1. Find end of setup (first significant movement) ---
28
  setup_end = frame_indices[0]
29
  initial_angles = angles_by_frame[frame_indices[0]]
30
  initial_shoulder = initial_angles.get("right_shoulder")
31
  initial_wrist = initial_angles.get("right_elbow")
32
+ movement_threshold = 8 # degrees, can be tuned
33
  for idx in frame_indices[1:]:
34
  angles = angles_by_frame[idx]
35
  shoulder = angles.get("right_shoulder")
36
  wrist = angles.get("right_elbow")
37
+ if (shoulder and initial_shoulder and abs(shoulder - initial_shoulder) > movement_threshold) or \
38
+ (wrist and initial_wrist and abs(wrist - initial_wrist) > movement_threshold):
39
+ setup_end = idx - 1
 
 
40
  break
41
 
42
+ # --- 2. Top of backswing (max shoulder angle after setup) ---
43
  max_shoulder_angle = -1
44
+ top_backswing_frame = setup_end + 1
45
  for idx in frame_indices:
46
+ if idx <= setup_end:
47
  continue
48
  shoulder = angles_by_frame[idx].get("right_shoulder")
49
  if shoulder and shoulder > max_shoulder_angle:
50
  max_shoulder_angle = shoulder
51
  top_backswing_frame = idx
52
 
53
+ # --- 3. Impact: first significant ball movement ---
 
54
  impact_frame = None
55
+ ball_detections = [d for d in detections if d.class_name == "sports ball"]
56
+ ball_detections.sort(key=lambda x: x.frame_idx)
57
+ movement_threshold_px = 2
58
+ prev_x = prev_y = None
59
+ prev_frame = None
60
+ for detection in ball_detections:
61
+ frame_idx = detection.frame_idx
62
+ if frame_idx < top_backswing_frame:
63
  continue
64
+ x1, y1, x2, y2 = detection.bbox
65
+ ball_x = (x1 + x2) / 2
66
+ ball_y = (y1 + y2) / 2
67
+ if prev_x is not None and prev_y is not None:
68
+ dx = abs(ball_x - prev_x)
69
+ dy = abs(ball_y - prev_y)
70
+ if dx > movement_threshold_px or dy > movement_threshold_px:
71
+ impact_frame = frame_idx
72
+ break
73
+ prev_x = ball_x
74
+ prev_y = ball_y
75
+ prev_frame = frame_idx
76
+ if impact_frame is None and prev_frame is not None:
77
+ impact_frame = prev_frame
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  if impact_frame is None:
79
  impact_frame = frame_indices[-1]
80
 
81
+ # --- 4. Assign phases dynamically ---
82
  for idx in frame_indices:
83
  if idx <= setup_end:
84
  swing_phases["setup"].append(idx)