kinemotion 0.76.1__py3-none-any.whl → 0.76.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kinemotion might be problematic. Click here for more details.
- kinemotion/countermovement_jump/analysis.py +14 -57
- kinemotion/drop_jump/__init__.py +0 -6
- kinemotion/drop_jump/analysis.py +10 -10
- kinemotion/drop_jump/kinematics.py +6 -6
- kinemotion/squat_jump/analysis.py +8 -8
- {kinemotion-0.76.1.dist-info → kinemotion-0.76.3.dist-info}/METADATA +25 -25
- {kinemotion-0.76.1.dist-info → kinemotion-0.76.3.dist-info}/RECORD +10 -12
- kinemotion/models/rtmpose-s_simcc-body7_pt-body7-halpe26_700e-256x192-7f134165_20230605.onnx +0 -3
- kinemotion/models/yolox_tiny_8xb8-300e_humanart-6f3252f9.onnx +0 -3
- {kinemotion-0.76.1.dist-info → kinemotion-0.76.3.dist-info}/WHEEL +0 -0
- {kinemotion-0.76.1.dist-info → kinemotion-0.76.3.dist-info}/entry_points.txt +0 -0
- {kinemotion-0.76.1.dist-info → kinemotion-0.76.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -55,50 +55,7 @@ class CMJPhase(Enum):
|
|
|
55
55
|
UNKNOWN = "unknown"
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
def
|
|
59
|
-
positions: FloatArray,
|
|
60
|
-
velocities: FloatArray,
|
|
61
|
-
min_search_frame: int = 80,
|
|
62
|
-
) -> int:
|
|
63
|
-
"""
|
|
64
|
-
Find the lowest point of countermovement (transition from eccentric to concentric).
|
|
65
|
-
|
|
66
|
-
The lowest point occurs BEFORE the peak height (the jump apex). It's where
|
|
67
|
-
velocity crosses from positive (downward/squatting) to negative (upward/jumping).
|
|
68
|
-
|
|
69
|
-
Args:
|
|
70
|
-
positions: Array of vertical positions (higher value = lower in video)
|
|
71
|
-
velocities: Array of SIGNED vertical velocities (positive=down, negative=up)
|
|
72
|
-
min_search_frame: Minimum frame to start searching (default: frame 80)
|
|
73
|
-
|
|
74
|
-
Returns:
|
|
75
|
-
Frame index of lowest point.
|
|
76
|
-
"""
|
|
77
|
-
# First, find the peak height (minimum y value = highest jump point)
|
|
78
|
-
peak_height_frame = int(np.argmin(positions))
|
|
79
|
-
|
|
80
|
-
# Lowest point MUST be before peak height
|
|
81
|
-
# Search from min_search_frame to peak_height_frame
|
|
82
|
-
start_frame = min_search_frame
|
|
83
|
-
end_frame = peak_height_frame
|
|
84
|
-
|
|
85
|
-
if end_frame <= start_frame:
|
|
86
|
-
start_frame = int(len(positions) * 0.3)
|
|
87
|
-
end_frame = int(len(positions) * 0.7)
|
|
88
|
-
|
|
89
|
-
search_positions = positions[start_frame:end_frame]
|
|
90
|
-
|
|
91
|
-
if len(search_positions) == 0:
|
|
92
|
-
return start_frame
|
|
93
|
-
|
|
94
|
-
# Find maximum position value in this range (lowest point in video)
|
|
95
|
-
lowest_idx = int(np.argmax(search_positions))
|
|
96
|
-
lowest_frame = start_frame + lowest_idx
|
|
97
|
-
|
|
98
|
-
return lowest_frame
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def find_cmj_takeoff_from_velocity_peak(
|
|
58
|
+
def _find_cmj_takeoff_from_velocity_peak(
|
|
102
59
|
positions: FloatArray,
|
|
103
60
|
velocities: FloatArray,
|
|
104
61
|
lowest_point_frame: int,
|
|
@@ -135,7 +92,7 @@ def find_cmj_takeoff_from_velocity_peak(
|
|
|
135
92
|
return float(takeoff_frame)
|
|
136
93
|
|
|
137
94
|
|
|
138
|
-
def
|
|
95
|
+
def _find_cmj_landing_from_position_peak(
|
|
139
96
|
positions: FloatArray,
|
|
140
97
|
velocities: FloatArray,
|
|
141
98
|
accelerations: FloatArray,
|
|
@@ -193,7 +150,7 @@ def find_cmj_landing_from_position_peak(
|
|
|
193
150
|
reason="Experimental alternative superseded by backward search algorithm",
|
|
194
151
|
since="0.34.0",
|
|
195
152
|
)
|
|
196
|
-
def
|
|
153
|
+
def _find_interpolated_takeoff_landing(
|
|
197
154
|
positions: FloatArray,
|
|
198
155
|
velocities: FloatArray,
|
|
199
156
|
lowest_point_frame: int,
|
|
@@ -226,19 +183,19 @@ def find_interpolated_takeoff_landing(
|
|
|
226
183
|
)
|
|
227
184
|
|
|
228
185
|
# Find takeoff using peak velocity method (CMJ-specific)
|
|
229
|
-
takeoff_frame =
|
|
186
|
+
takeoff_frame = _find_cmj_takeoff_from_velocity_peak(
|
|
230
187
|
positions, velocities, lowest_point_frame, fps
|
|
231
188
|
)
|
|
232
189
|
|
|
233
190
|
# Find landing using position peak and impact detection
|
|
234
|
-
landing_frame =
|
|
191
|
+
landing_frame = _find_cmj_landing_from_position_peak(
|
|
235
192
|
positions, velocities, accelerations, int(takeoff_frame), fps
|
|
236
193
|
)
|
|
237
194
|
|
|
238
195
|
return (takeoff_frame, landing_frame)
|
|
239
196
|
|
|
240
197
|
|
|
241
|
-
def
|
|
198
|
+
def _find_takeoff_frame(
|
|
242
199
|
velocities: FloatArray,
|
|
243
200
|
peak_height_frame: int,
|
|
244
201
|
fps: float,
|
|
@@ -301,7 +258,7 @@ def find_takeoff_frame(
|
|
|
301
258
|
return float(peak_vel_frame)
|
|
302
259
|
|
|
303
260
|
|
|
304
|
-
def
|
|
261
|
+
def _find_lowest_frame(
|
|
305
262
|
velocities: FloatArray, positions: FloatArray, takeoff_frame: float, fps: float
|
|
306
263
|
) -> float:
|
|
307
264
|
"""Find lowest point frame before takeoff."""
|
|
@@ -370,7 +327,7 @@ def _find_landing_impact(
|
|
|
370
327
|
return float(landing_frame)
|
|
371
328
|
|
|
372
329
|
|
|
373
|
-
def
|
|
330
|
+
def _find_landing_frame(
|
|
374
331
|
accelerations: FloatArray,
|
|
375
332
|
velocities: FloatArray,
|
|
376
333
|
peak_height_frame: int,
|
|
@@ -422,7 +379,7 @@ def compute_average_hip_position(
|
|
|
422
379
|
return (float(np.mean(x_positions)), float(np.mean(y_positions)))
|
|
423
380
|
|
|
424
381
|
|
|
425
|
-
def
|
|
382
|
+
def _find_standing_end(
|
|
426
383
|
velocities: FloatArray,
|
|
427
384
|
lowest_point: float,
|
|
428
385
|
_positions: FloatArray | None = None,
|
|
@@ -533,12 +490,12 @@ def detect_cmj_phases(
|
|
|
533
490
|
|
|
534
491
|
# Step 2-4: Find all phases using helper functions
|
|
535
492
|
with timer.measure("cmj_find_takeoff"):
|
|
536
|
-
takeoff_frame =
|
|
493
|
+
takeoff_frame = _find_takeoff_frame(
|
|
537
494
|
velocities, peak_height_frame, fps, accelerations=accelerations
|
|
538
495
|
)
|
|
539
496
|
|
|
540
497
|
with timer.measure("cmj_find_lowest_point"):
|
|
541
|
-
lowest_point =
|
|
498
|
+
lowest_point = _find_lowest_frame(velocities, positions, takeoff_frame, fps)
|
|
542
499
|
|
|
543
500
|
# Determine landing frame
|
|
544
501
|
with timer.measure("cmj_find_landing"):
|
|
@@ -552,7 +509,7 @@ def detect_cmj_phases(
|
|
|
552
509
|
)
|
|
553
510
|
# We still reference peak_height_frame from Hips, as Feet peak
|
|
554
511
|
# might be different/noisy but generally they align in time.
|
|
555
|
-
landing_frame =
|
|
512
|
+
landing_frame = _find_landing_frame(
|
|
556
513
|
landing_accelerations,
|
|
557
514
|
landing_velocities,
|
|
558
515
|
peak_height_frame,
|
|
@@ -560,7 +517,7 @@ def detect_cmj_phases(
|
|
|
560
517
|
)
|
|
561
518
|
else:
|
|
562
519
|
# Use primary signal (Hips)
|
|
563
|
-
landing_frame =
|
|
520
|
+
landing_frame = _find_landing_frame(
|
|
564
521
|
accelerations,
|
|
565
522
|
velocities,
|
|
566
523
|
peak_height_frame,
|
|
@@ -568,6 +525,6 @@ def detect_cmj_phases(
|
|
|
568
525
|
)
|
|
569
526
|
|
|
570
527
|
with timer.measure("cmj_find_standing_end"):
|
|
571
|
-
standing_end =
|
|
528
|
+
standing_end = _find_standing_end(velocities, lowest_point, positions, accelerations)
|
|
572
529
|
|
|
573
530
|
return (standing_end, lowest_point, takeoff_frame, landing_frame)
|
kinemotion/drop_jump/__init__.py
CHANGED
|
@@ -3,11 +3,8 @@
|
|
|
3
3
|
from ..core.smoothing import interpolate_threshold_crossing
|
|
4
4
|
from .analysis import (
|
|
5
5
|
ContactState,
|
|
6
|
-
calculate_adaptive_threshold,
|
|
7
6
|
compute_average_foot_position,
|
|
8
7
|
detect_ground_contact,
|
|
9
|
-
find_interpolated_phase_transitions_with_curvature,
|
|
10
|
-
refine_transition_with_curvature,
|
|
11
8
|
)
|
|
12
9
|
from .debug_overlay import DropJumpDebugOverlayRenderer
|
|
13
10
|
from .kinematics import DropJumpMetrics, calculate_drop_jump_metrics
|
|
@@ -17,10 +14,7 @@ __all__ = [
|
|
|
17
14
|
"ContactState",
|
|
18
15
|
"detect_ground_contact",
|
|
19
16
|
"compute_average_foot_position",
|
|
20
|
-
"calculate_adaptive_threshold",
|
|
21
17
|
"interpolate_threshold_crossing",
|
|
22
|
-
"refine_transition_with_curvature",
|
|
23
|
-
"find_interpolated_phase_transitions_with_curvature",
|
|
24
18
|
# Metrics
|
|
25
19
|
"DropJumpMetrics",
|
|
26
20
|
"calculate_drop_jump_metrics",
|
kinemotion/drop_jump/analysis.py
CHANGED
|
@@ -27,7 +27,7 @@ class ContactState(Enum):
|
|
|
27
27
|
remove_in="1.0.0",
|
|
28
28
|
since="0.34.0",
|
|
29
29
|
)
|
|
30
|
-
def
|
|
30
|
+
def _calculate_adaptive_threshold(
|
|
31
31
|
positions: FloatArray,
|
|
32
32
|
fps: float,
|
|
33
33
|
baseline_duration: float = 3.0,
|
|
@@ -188,7 +188,7 @@ def _find_drop_from_baseline(
|
|
|
188
188
|
return 0
|
|
189
189
|
|
|
190
190
|
|
|
191
|
-
def
|
|
191
|
+
def _detect_drop_start(
|
|
192
192
|
positions: FloatArray,
|
|
193
193
|
fps: float,
|
|
194
194
|
min_stationary_duration: float = 1.0,
|
|
@@ -580,7 +580,7 @@ def _interpolate_phase_end(
|
|
|
580
580
|
)
|
|
581
581
|
|
|
582
582
|
|
|
583
|
-
def
|
|
583
|
+
def _find_interpolated_phase_transitions(
|
|
584
584
|
foot_positions: FloatArray,
|
|
585
585
|
contact_states: list[ContactState],
|
|
586
586
|
velocity_threshold: float,
|
|
@@ -622,7 +622,7 @@ def find_interpolated_phase_transitions(
|
|
|
622
622
|
return interpolated_phases
|
|
623
623
|
|
|
624
624
|
|
|
625
|
-
def
|
|
625
|
+
def _refine_transition_with_curvature(
|
|
626
626
|
foot_positions: FloatArray,
|
|
627
627
|
estimated_frame: float,
|
|
628
628
|
transition_type: str,
|
|
@@ -720,7 +720,7 @@ def _refine_phase_boundaries(
|
|
|
720
720
|
Returns:
|
|
721
721
|
Tuple of (refined_start, refined_end) fractional frame indices
|
|
722
722
|
"""
|
|
723
|
-
refined_start =
|
|
723
|
+
refined_start = _refine_transition_with_curvature(
|
|
724
724
|
foot_positions,
|
|
725
725
|
start_frac,
|
|
726
726
|
start_type,
|
|
@@ -728,7 +728,7 @@ def _refine_phase_boundaries(
|
|
|
728
728
|
smoothing_window=smoothing_window,
|
|
729
729
|
polyorder=polyorder,
|
|
730
730
|
)
|
|
731
|
-
refined_end =
|
|
731
|
+
refined_end = _refine_transition_with_curvature(
|
|
732
732
|
foot_positions,
|
|
733
733
|
end_frac,
|
|
734
734
|
end_type,
|
|
@@ -739,7 +739,7 @@ def _refine_phase_boundaries(
|
|
|
739
739
|
return refined_start, refined_end
|
|
740
740
|
|
|
741
741
|
|
|
742
|
-
def
|
|
742
|
+
def _find_interpolated_phase_transitions_with_curvature(
|
|
743
743
|
foot_positions: FloatArray,
|
|
744
744
|
contact_states: list[ContactState],
|
|
745
745
|
velocity_threshold: float,
|
|
@@ -767,7 +767,7 @@ def find_interpolated_phase_transitions_with_curvature(
|
|
|
767
767
|
List of (start_frame, end_frame, state) tuples with fractional frame indices
|
|
768
768
|
"""
|
|
769
769
|
# Get interpolated phases using velocity
|
|
770
|
-
interpolated_phases =
|
|
770
|
+
interpolated_phases = _find_interpolated_phase_transitions(
|
|
771
771
|
foot_positions, contact_states, velocity_threshold, smoothing_window
|
|
772
772
|
)
|
|
773
773
|
|
|
@@ -808,7 +808,7 @@ def find_interpolated_phase_transitions_with_curvature(
|
|
|
808
808
|
return refined_phases
|
|
809
809
|
|
|
810
810
|
|
|
811
|
-
def
|
|
811
|
+
def _find_landing_from_acceleration(
|
|
812
812
|
positions: FloatArray,
|
|
813
813
|
accelerations: FloatArray,
|
|
814
814
|
takeoff_frame: int,
|
|
@@ -954,7 +954,7 @@ def _calculate_average_visibility(
|
|
|
954
954
|
reason="Alternative implementation not called by pipeline",
|
|
955
955
|
since="0.34.0",
|
|
956
956
|
)
|
|
957
|
-
def
|
|
957
|
+
def _extract_foot_positions_and_visibilities(
|
|
958
958
|
smoothed_landmarks: list[dict[str, tuple[float, float, float]] | None],
|
|
959
959
|
) -> tuple[np.ndarray, np.ndarray]:
|
|
960
960
|
"""
|
|
@@ -10,10 +10,10 @@ from ..core.smoothing import compute_acceleration_from_derivative
|
|
|
10
10
|
from ..core.timing import NULL_TIMER, Timer
|
|
11
11
|
from .analysis import (
|
|
12
12
|
ContactState,
|
|
13
|
-
|
|
13
|
+
_detect_drop_start, # pyright: ignore[reportPrivateUsage]
|
|
14
|
+
_find_interpolated_phase_transitions_with_curvature, # pyright: ignore[reportPrivateUsage]
|
|
15
|
+
_find_landing_from_acceleration, # pyright: ignore[reportPrivateUsage]
|
|
14
16
|
find_contact_phases,
|
|
15
|
-
find_interpolated_phase_transitions_with_curvature,
|
|
16
|
-
find_landing_from_acceleration,
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
@@ -159,7 +159,7 @@ def _determine_drop_start_frame(
|
|
|
159
159
|
"""
|
|
160
160
|
if drop_start_frame is None:
|
|
161
161
|
# Auto-detect where drop jump actually starts (skip initial stationary period)
|
|
162
|
-
return
|
|
162
|
+
return _detect_drop_start(
|
|
163
163
|
foot_y_positions,
|
|
164
164
|
fps,
|
|
165
165
|
min_stationary_duration=0.5,
|
|
@@ -445,7 +445,7 @@ def _analyze_flight_phase(
|
|
|
445
445
|
accelerations = compute_acceleration_from_derivative(
|
|
446
446
|
foot_y_positions, window_length=smoothing_window, polyorder=polyorder
|
|
447
447
|
)
|
|
448
|
-
flight_end =
|
|
448
|
+
flight_end = _find_landing_from_acceleration(
|
|
449
449
|
foot_y_positions, accelerations, flight_start, fps
|
|
450
450
|
)
|
|
451
451
|
|
|
@@ -559,7 +559,7 @@ def calculate_drop_jump_metrics(
|
|
|
559
559
|
# Find contact phases
|
|
560
560
|
with timer.measure("dj_find_phases"):
|
|
561
561
|
phases = find_contact_phases(contact_states)
|
|
562
|
-
interpolated_phases =
|
|
562
|
+
interpolated_phases = _find_interpolated_phase_transitions_with_curvature(
|
|
563
563
|
foot_y_positions,
|
|
564
564
|
contact_states,
|
|
565
565
|
velocity_threshold,
|
|
@@ -53,7 +53,7 @@ def detect_sj_phases(
|
|
|
53
53
|
window_length += 1
|
|
54
54
|
|
|
55
55
|
# Step 1: Detect squat hold start
|
|
56
|
-
squat_hold_start =
|
|
56
|
+
squat_hold_start = _detect_squat_start(
|
|
57
57
|
positions,
|
|
58
58
|
fps,
|
|
59
59
|
velocity_threshold=velocity_threshold,
|
|
@@ -75,7 +75,7 @@ def detect_sj_phases(
|
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
# Step 3: Detect takeoff (this marks the start of concentric phase)
|
|
78
|
-
takeoff_frame =
|
|
78
|
+
takeoff_frame = _detect_takeoff(
|
|
79
79
|
positions, velocities, fps, velocity_threshold=velocity_threshold
|
|
80
80
|
)
|
|
81
81
|
|
|
@@ -92,7 +92,7 @@ def detect_sj_phases(
|
|
|
92
92
|
break
|
|
93
93
|
|
|
94
94
|
# Step 4: Detect landing
|
|
95
|
-
landing_frame =
|
|
95
|
+
landing_frame = _detect_landing(
|
|
96
96
|
positions,
|
|
97
97
|
velocities,
|
|
98
98
|
fps,
|
|
@@ -126,7 +126,7 @@ def detect_sj_phases(
|
|
|
126
126
|
return (squat_hold_start, concentric_start, takeoff_frame, landing_frame)
|
|
127
127
|
|
|
128
128
|
|
|
129
|
-
def
|
|
129
|
+
def _detect_squat_start(
|
|
130
130
|
positions: FloatArray,
|
|
131
131
|
fps: float,
|
|
132
132
|
velocity_threshold: float = 0.1,
|
|
@@ -201,7 +201,7 @@ def _find_takeoff_threshold_crossing(
|
|
|
201
201
|
return None
|
|
202
202
|
|
|
203
203
|
|
|
204
|
-
def
|
|
204
|
+
def _detect_takeoff(
|
|
205
205
|
positions: FloatArray,
|
|
206
206
|
velocities: FloatArray,
|
|
207
207
|
fps: float,
|
|
@@ -230,7 +230,7 @@ def detect_takeoff(
|
|
|
230
230
|
return None
|
|
231
231
|
|
|
232
232
|
# Find squat start to determine where to begin search
|
|
233
|
-
squat_start =
|
|
233
|
+
squat_start = _detect_squat_start(positions, fps)
|
|
234
234
|
if squat_start is None:
|
|
235
235
|
# If no squat start detected, start from reasonable middle point
|
|
236
236
|
squat_start = len(positions) // 3
|
|
@@ -289,7 +289,7 @@ def _refine_landing_by_velocity(
|
|
|
289
289
|
return landing_frame
|
|
290
290
|
|
|
291
291
|
|
|
292
|
-
def
|
|
292
|
+
def _detect_landing(
|
|
293
293
|
positions: FloatArray,
|
|
294
294
|
velocities: FloatArray,
|
|
295
295
|
fps: float,
|
|
@@ -322,7 +322,7 @@ def detect_landing(
|
|
|
322
322
|
return None
|
|
323
323
|
|
|
324
324
|
# Find takeoff first (needed to determine where to start peak search)
|
|
325
|
-
takeoff_frame =
|
|
325
|
+
takeoff_frame = _detect_takeoff(
|
|
326
326
|
positions, velocities, fps, velocity_threshold, 5, landing_search_window_s
|
|
327
327
|
)
|
|
328
328
|
if takeoff_frame is None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kinemotion
|
|
3
|
-
Version: 0.76.
|
|
3
|
+
Version: 0.76.3
|
|
4
4
|
Summary: Video-based kinematic analysis for athletic performance
|
|
5
5
|
Project-URL: Homepage, https://github.com/feniix/kinemotion
|
|
6
6
|
Project-URL: Repository, https://github.com/feniix/kinemotion
|
|
@@ -625,9 +625,9 @@ The debug video includes:
|
|
|
625
625
|
**Solutions**:
|
|
626
626
|
|
|
627
627
|
1. **Check video quality**: Ensure the athlete is clearly visible in profile view
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
628
|
+
2. **Increase smoothing**: Use `--smoothing-window 7` or higher
|
|
629
|
+
3. **Adjust detection confidence**: Try `--detection-confidence 0.6` or `--tracking-confidence 0.6`
|
|
630
|
+
4. **Generate debug video**: Use `--output` to visualize what's being tracked
|
|
631
631
|
|
|
632
632
|
### No Pose Detected
|
|
633
633
|
|
|
@@ -636,9 +636,9 @@ The debug video includes:
|
|
|
636
636
|
**Solutions**:
|
|
637
637
|
|
|
638
638
|
1. **Verify video format**: OpenCV must be able to read the video
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
639
|
+
2. **Check framing**: Ensure full body is visible in side view
|
|
640
|
+
3. **Lower confidence thresholds**: Try `--detection-confidence 0.3 --tracking-confidence 0.3`
|
|
641
|
+
4. **Test video playback**: Verify video opens correctly with standard video players
|
|
642
642
|
|
|
643
643
|
### Incorrect Contact Detection
|
|
644
644
|
|
|
@@ -647,11 +647,11 @@ The debug video includes:
|
|
|
647
647
|
**Solutions**:
|
|
648
648
|
|
|
649
649
|
1. **Generate debug video**: Visualize contact states to diagnose the issue
|
|
650
|
-
|
|
650
|
+
2. **Adjust velocity threshold**:
|
|
651
651
|
- If missing contacts: decrease to `--velocity-threshold 0.01`
|
|
652
652
|
- If false contacts: increase to `--velocity-threshold 0.03`
|
|
653
|
-
|
|
654
|
-
|
|
653
|
+
3. **Adjust minimum frames**: `--min-contact-frames 5` for longer required contact
|
|
654
|
+
4. **Check visibility**: Lower `--visibility-threshold 0.3` if feet are partially obscured
|
|
655
655
|
|
|
656
656
|
### Jump Height Seems Wrong
|
|
657
657
|
|
|
@@ -660,9 +660,9 @@ The debug video includes:
|
|
|
660
660
|
**Solutions**:
|
|
661
661
|
|
|
662
662
|
1. **Check video quality**: Ensure video frame rate is adequate (30fps or higher recommended)
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
663
|
+
2. **Verify flight time detection**: Check `flight_start_frame` and `flight_end_frame` in JSON
|
|
664
|
+
3. **Compare measurements**: JSON output includes both `jump_height_m` (primary) and `jump_height_kinematic_m` (kinematic-only)
|
|
665
|
+
4. **Check for drop jump detection**: If doing a drop jump, ensure first phase is elevated enough (>5% of frame height)
|
|
666
666
|
|
|
667
667
|
### Video Codec Issues
|
|
668
668
|
|
|
@@ -671,30 +671,30 @@ The debug video includes:
|
|
|
671
671
|
**Solutions**:
|
|
672
672
|
|
|
673
673
|
1. **Install additional codecs**: Ensure OpenCV has proper video codec support
|
|
674
|
-
|
|
675
|
-
|
|
674
|
+
2. **Try different output format**: Use `.avi` extension instead of `.mp4`
|
|
675
|
+
3. **Check output path**: Ensure write permissions for output directory
|
|
676
676
|
|
|
677
677
|
## How It Works
|
|
678
678
|
|
|
679
679
|
1. **Pose Tracking**: MediaPipe extracts 2D pose landmarks (foot points: ankles, heels, foot indices) from each frame
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
680
|
+
2. **Position Calculation**: Averages ankle, heel, and foot index positions to determine foot location
|
|
681
|
+
3. **Smoothing**: Savitzky-Golay filter reduces tracking jitter while preserving motion dynamics
|
|
682
|
+
4. **Contact Detection**: Analyzes vertical position velocity to identify ground contact vs. flight phases
|
|
683
|
+
5. **Phase Identification**: Finds continuous ground contact and flight periods
|
|
684
684
|
- Automatically detects drop jumps vs regular jumps
|
|
685
685
|
- For drop jumps: identifies box → drop → ground contact → jump sequence
|
|
686
|
-
|
|
686
|
+
6. **Sub-Frame Interpolation**: Estimates exact transition times between frames
|
|
687
687
|
- Uses Savitzky-Golay derivative for smooth velocity calculation
|
|
688
688
|
- Linear interpolation of velocity to find threshold crossings
|
|
689
689
|
- Achieves sub-millisecond timing precision (at 30fps: ±10ms vs ±33ms)
|
|
690
690
|
- Reduces timing error by 60-70% for contact and flight measurements
|
|
691
691
|
- Smoother velocity curves eliminate false threshold crossings
|
|
692
|
-
|
|
692
|
+
7. **Trajectory Curvature Analysis**: Refines transitions using acceleration patterns
|
|
693
693
|
- Computes second derivative (acceleration) from position trajectory
|
|
694
694
|
- Detects landing impact by acceleration spike
|
|
695
695
|
- Identifies takeoff by acceleration change patterns
|
|
696
696
|
- Provides independent validation and refinement of velocity-based detection
|
|
697
|
-
|
|
697
|
+
8. **Metric Calculation**:
|
|
698
698
|
- Ground contact time = contact phase duration (using fractional frames)
|
|
699
699
|
- Flight time = flight phase duration (using fractional frames)
|
|
700
700
|
- Jump height = kinematic estimate from flight time: (g × t²) / 8
|
|
@@ -744,9 +744,9 @@ uv run ruff check && uv run pyright && uv run pytest
|
|
|
744
744
|
Before committing code, ensure all checks pass:
|
|
745
745
|
|
|
746
746
|
1. Format with Black
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
747
|
+
2. Fix linting issues with ruff
|
|
748
|
+
3. Ensure type safety with pyright
|
|
749
|
+
4. Run all tests with pytest
|
|
750
750
|
|
|
751
751
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines and requirements, or [CLAUDE.md](CLAUDE.md) for detailed development guidelines.
|
|
752
752
|
|
|
@@ -23,7 +23,7 @@ kinemotion/core/validation.py,sha256=-8Wwe56PO37F0OAEMpWr1AB_7QmFtDY5bVmux3oiLYM
|
|
|
23
23
|
kinemotion/core/video_analysis_base.py,sha256=U8j-6-dv6uiGUiIHl53AIVFUiVHotgTmMNvCArSXx0E,4045
|
|
24
24
|
kinemotion/core/video_io.py,sha256=tLAHm63_sap-CXQpLzmgUXpWZ5_TtBI9LHP8Tk2L-z4,9355
|
|
25
25
|
kinemotion/countermovement_jump/__init__.py,sha256=SkAw9ka8Yd1Qfv9hcvk22m3EfucROzYrSNGNF5kDzho,113
|
|
26
|
-
kinemotion/countermovement_jump/analysis.py,sha256=
|
|
26
|
+
kinemotion/countermovement_jump/analysis.py,sha256=XJd7o7p7l0WvjJuIzTBCnIyrhoUj7kryKZB9BltQIZY,19219
|
|
27
27
|
kinemotion/countermovement_jump/api.py,sha256=uNo2JLuFDeBdpi3Y2qf-DyG-1KIRwmSF7HjXMu9Cwj0,19320
|
|
28
28
|
kinemotion/countermovement_jump/cli.py,sha256=m727IOg5BuixgNraCXc2sjW5jGrxrg7RKvFS4qyrBK8,8902
|
|
29
29
|
kinemotion/countermovement_jump/debug_overlay.py,sha256=vF5Apiz8zDRpgrVzf52manLW99m1kHQAPSdUkar5rPs,11474
|
|
@@ -31,20 +31,18 @@ kinemotion/countermovement_jump/joint_angles.py,sha256=by5M4LDtUfd2_Z9DmcgUl0nsv
|
|
|
31
31
|
kinemotion/countermovement_jump/kinematics.py,sha256=KwA8uSj3g1SeNf0NXMSHsp3gIw6Gfa-6QWIwdYdRXYw,13362
|
|
32
32
|
kinemotion/countermovement_jump/metrics_validator.py,sha256=ma1XSLT-RIDrcjYmgfixf244TwbiosRzN7oFr4yWCXg,24609
|
|
33
33
|
kinemotion/countermovement_jump/validation_bounds.py,sha256=-0iXDhH-RntiGZi_Co22V6qtA5D-hLzkrPkVcfoNd2U,11343
|
|
34
|
-
kinemotion/drop_jump/__init__.py,sha256=
|
|
35
|
-
kinemotion/drop_jump/analysis.py,sha256=
|
|
34
|
+
kinemotion/drop_jump/__init__.py,sha256=p1lqit5OQruIcCizCJwG0OuUab6ktK_9HCp9eaHS2Io,616
|
|
35
|
+
kinemotion/drop_jump/analysis.py,sha256=i38uOZEpmH0T-_8-m9Ne4I24dQO2B5tgYVIgwG0kJ4o,34880
|
|
36
36
|
kinemotion/drop_jump/api.py,sha256=xcA7CkBjLQZhIs6UHr2mo5jT1p-D6e3cgRAFLSwZCmE,21927
|
|
37
37
|
kinemotion/drop_jump/cli.py,sha256=WTUJWCjBl9SgR3Z-2cml1EQhVF8HaXIXQ27fS4tnR7U,14693
|
|
38
38
|
kinemotion/drop_jump/debug_overlay.py,sha256=X4mvCi5Qi1gnvSZZAsUs-0ZRUx9mVBbEUznOFO21HO8,8470
|
|
39
|
-
kinemotion/drop_jump/kinematics.py,sha256=
|
|
39
|
+
kinemotion/drop_jump/kinematics.py,sha256=wQSXvchqZgc-KBnLRWJw_EuhXwo764xQltRL38toou4,23780
|
|
40
40
|
kinemotion/drop_jump/metrics_validator.py,sha256=yY0wzFzUUDLn6pZcOnwErMlIt_aTWq-RyAkqIemBG5M,7885
|
|
41
41
|
kinemotion/drop_jump/validation_bounds.py,sha256=k31qy-kCXTiCTx0RPo2t8yZ-faLxqGO-AeF05QfBFb0,5125
|
|
42
42
|
kinemotion/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
43
|
kinemotion/models/pose_landmarker_lite.task,sha256=WZKeHR7pUodzXd2DOxnPSsRtKbx6_du_Z1PEWWkNV0o,5777746
|
|
44
|
-
kinemotion/models/rtmpose-s_simcc-body7_pt-body7-halpe26_700e-256x192-7f134165_20230605.onnx,sha256=dfZTq8kbhv8RxWiXS0HUIJNCUpxYTBN45dFIorPflEs,133
|
|
45
|
-
kinemotion/models/yolox_tiny_8xb8-300e_humanart-6f3252f9.onnx,sha256=UsutHVQ6GP3X5pCcp52EN8q7o2J3d-TnxZqlF48kY6I,133
|
|
46
44
|
kinemotion/squat_jump/__init__.py,sha256=h6ubO3BUANxqjKMdN-KtlN6m77HARAP25PLzHw9k-Lk,99
|
|
47
|
-
kinemotion/squat_jump/analysis.py,sha256=
|
|
45
|
+
kinemotion/squat_jump/analysis.py,sha256=Y6pRtQDaSd2fl_WYwkGKWQ9_uOjATwGkrWRd1TOcOL8,13508
|
|
48
46
|
kinemotion/squat_jump/api.py,sha256=YMbq2BQzB_SnZ_Z-2KnR_OO2xuua-Zg0hP29Ghbk_d4,20111
|
|
49
47
|
kinemotion/squat_jump/cli.py,sha256=09Q9O4_sHxw6QWDyPiynDQZSMixTO32NrJ5PTXTJNIk,9806
|
|
50
48
|
kinemotion/squat_jump/debug_overlay.py,sha256=IZij8XQvWnmxfDhOZZiLIQ-0xuICx6lDYqcdS7TA3Kw,5280
|
|
@@ -52,8 +50,8 @@ kinemotion/squat_jump/kinematics.py,sha256=RU5JobjkSV6Bxs3ope-4fvbeLBITFb_dv9uvH
|
|
|
52
50
|
kinemotion/squat_jump/metrics_validator.py,sha256=euqd3dYrDCqdifVhs0RPc-UUprkyR-PzTg-D_rIfmI4,15914
|
|
53
51
|
kinemotion/squat_jump/validation_bounds.py,sha256=q01eQ8Eg01Y5UV3KlvZS1S9iY628OVPUwLoukHZvQOs,7276
|
|
54
52
|
kinemotion/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
|
-
kinemotion-0.76.
|
|
56
|
-
kinemotion-0.76.
|
|
57
|
-
kinemotion-0.76.
|
|
58
|
-
kinemotion-0.76.
|
|
59
|
-
kinemotion-0.76.
|
|
53
|
+
kinemotion-0.76.3.dist-info/METADATA,sha256=igm-NnnzV6hH1LNLc4pqN9_gLvAYUGBGfH0wT5hF-Hk,27690
|
|
54
|
+
kinemotion-0.76.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
55
|
+
kinemotion-0.76.3.dist-info/entry_points.txt,sha256=zaqnAnjLvcdrk1Qvj5nvXZCZ2gp0prS7it1zTJygcIY,50
|
|
56
|
+
kinemotion-0.76.3.dist-info/licenses/LICENSE,sha256=KZajvqsHw0NoOHOi2q0FZ4NBe9HdV6oey-IPYAtHXfg,1088
|
|
57
|
+
kinemotion-0.76.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|