kinemotion 0.20.2__py3-none-any.whl → 0.21.0__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/cmj/kinematics.py +25 -4
- kinemotion/core/smoothing.py +19 -12
- kinemotion/dropjump/kinematics.py +27 -5
- {kinemotion-0.20.2.dist-info → kinemotion-0.21.0.dist-info}/METADATA +1 -1
- {kinemotion-0.20.2.dist-info → kinemotion-0.21.0.dist-info}/RECORD +8 -8
- {kinemotion-0.20.2.dist-info → kinemotion-0.21.0.dist-info}/WHEEL +0 -0
- {kinemotion-0.20.2.dist-info → kinemotion-0.21.0.dist-info}/entry_points.txt +0 -0
- {kinemotion-0.20.2.dist-info → kinemotion-0.21.0.dist-info}/licenses/LICENSE +0 -0
kinemotion/cmj/kinematics.py
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
"""Counter Movement Jump (CMJ) metrics calculation."""
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import TypedDict
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
|
+
from numpy.typing import NDArray
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CMJMetricsDict(TypedDict, total=False):
|
|
11
|
+
"""Type-safe dictionary for CMJ metrics JSON output."""
|
|
12
|
+
|
|
13
|
+
jump_height_m: float
|
|
14
|
+
flight_time_s: float
|
|
15
|
+
countermovement_depth_m: float
|
|
16
|
+
eccentric_duration_s: float
|
|
17
|
+
concentric_duration_s: float
|
|
18
|
+
total_movement_time_s: float
|
|
19
|
+
peak_eccentric_velocity_m_s: float
|
|
20
|
+
peak_concentric_velocity_m_s: float
|
|
21
|
+
transition_time_s: float | None
|
|
22
|
+
standing_start_frame: float | None
|
|
23
|
+
lowest_point_frame: float
|
|
24
|
+
takeoff_frame: float
|
|
25
|
+
landing_frame: float
|
|
26
|
+
video_fps: float
|
|
27
|
+
tracking_method: str
|
|
7
28
|
|
|
8
29
|
|
|
9
30
|
@dataclass
|
|
@@ -44,7 +65,7 @@ class CMJMetrics:
|
|
|
44
65
|
video_fps: float
|
|
45
66
|
tracking_method: str
|
|
46
67
|
|
|
47
|
-
def to_dict(self) ->
|
|
68
|
+
def to_dict(self) -> CMJMetricsDict:
|
|
48
69
|
"""Convert metrics to JSON-serializable dictionary.
|
|
49
70
|
|
|
50
71
|
Returns:
|
|
@@ -78,8 +99,8 @@ class CMJMetrics:
|
|
|
78
99
|
|
|
79
100
|
|
|
80
101
|
def calculate_cmj_metrics(
|
|
81
|
-
positions: np.
|
|
82
|
-
velocities: np.
|
|
102
|
+
positions: NDArray[np.float64],
|
|
103
|
+
velocities: NDArray[np.float64],
|
|
83
104
|
standing_start_frame: float | None,
|
|
84
105
|
lowest_point_frame: float,
|
|
85
106
|
takeoff_frame: float,
|
kinemotion/core/smoothing.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Landmark smoothing utilities to reduce jitter in pose tracking."""
|
|
2
2
|
|
|
3
|
+
from typing import TypeAlias
|
|
4
|
+
|
|
3
5
|
import numpy as np
|
|
4
6
|
from scipy.signal import savgol_filter
|
|
5
7
|
|
|
@@ -8,9 +10,14 @@ from .filtering import (
|
|
|
8
10
|
reject_outliers,
|
|
9
11
|
)
|
|
10
12
|
|
|
13
|
+
# Type aliases for landmark data structures
|
|
14
|
+
LandmarkCoord: TypeAlias = tuple[float, float, float] # (x, y, visibility)
|
|
15
|
+
LandmarkFrame: TypeAlias = dict[str, LandmarkCoord] | None
|
|
16
|
+
LandmarkSequence: TypeAlias = list[LandmarkFrame]
|
|
17
|
+
|
|
11
18
|
|
|
12
19
|
def _extract_landmark_coordinates(
|
|
13
|
-
landmark_sequence:
|
|
20
|
+
landmark_sequence: LandmarkSequence,
|
|
14
21
|
landmark_name: str,
|
|
15
22
|
) -> tuple[list[float], list[float], list[int]]:
|
|
16
23
|
"""
|
|
@@ -38,7 +45,7 @@ def _extract_landmark_coordinates(
|
|
|
38
45
|
|
|
39
46
|
|
|
40
47
|
def _get_landmark_names(
|
|
41
|
-
landmark_sequence:
|
|
48
|
+
landmark_sequence: LandmarkSequence,
|
|
42
49
|
) -> list[str] | None:
|
|
43
50
|
"""
|
|
44
51
|
Extract landmark names from first valid frame.
|
|
@@ -56,8 +63,8 @@ def _get_landmark_names(
|
|
|
56
63
|
|
|
57
64
|
|
|
58
65
|
def _fill_missing_frames(
|
|
59
|
-
smoothed_sequence:
|
|
60
|
-
landmark_sequence:
|
|
66
|
+
smoothed_sequence: LandmarkSequence,
|
|
67
|
+
landmark_sequence: LandmarkSequence,
|
|
61
68
|
) -> None:
|
|
62
69
|
"""
|
|
63
70
|
Fill in any missing frames in smoothed sequence with original data.
|
|
@@ -75,8 +82,8 @@ def _fill_missing_frames(
|
|
|
75
82
|
|
|
76
83
|
|
|
77
84
|
def _store_smoothed_landmarks(
|
|
78
|
-
smoothed_sequence:
|
|
79
|
-
landmark_sequence:
|
|
85
|
+
smoothed_sequence: LandmarkSequence,
|
|
86
|
+
landmark_sequence: LandmarkSequence,
|
|
80
87
|
landmark_name: str,
|
|
81
88
|
x_smooth: np.ndarray,
|
|
82
89
|
y_smooth: np.ndarray,
|
|
@@ -118,11 +125,11 @@ def _store_smoothed_landmarks(
|
|
|
118
125
|
|
|
119
126
|
|
|
120
127
|
def _smooth_landmarks_core( # NOSONAR(S1172) - polyorder used via closure capture in smoother_fn
|
|
121
|
-
landmark_sequence:
|
|
128
|
+
landmark_sequence: LandmarkSequence,
|
|
122
129
|
window_length: int,
|
|
123
130
|
polyorder: int,
|
|
124
131
|
smoother_fn, # type: ignore[no-untyped-def]
|
|
125
|
-
) ->
|
|
132
|
+
) -> LandmarkSequence:
|
|
126
133
|
"""
|
|
127
134
|
Core smoothing logic shared by both standard and advanced smoothing.
|
|
128
135
|
|
|
@@ -170,10 +177,10 @@ def _smooth_landmarks_core( # NOSONAR(S1172) - polyorder used via closure captu
|
|
|
170
177
|
|
|
171
178
|
|
|
172
179
|
def smooth_landmarks(
|
|
173
|
-
landmark_sequence:
|
|
180
|
+
landmark_sequence: LandmarkSequence,
|
|
174
181
|
window_length: int = 5,
|
|
175
182
|
polyorder: int = 2,
|
|
176
|
-
) ->
|
|
183
|
+
) -> LandmarkSequence:
|
|
177
184
|
"""
|
|
178
185
|
Smooth landmark trajectories using Savitzky-Golay filter.
|
|
179
186
|
|
|
@@ -330,7 +337,7 @@ def compute_acceleration_from_derivative(
|
|
|
330
337
|
|
|
331
338
|
|
|
332
339
|
def smooth_landmarks_advanced(
|
|
333
|
-
landmark_sequence:
|
|
340
|
+
landmark_sequence: LandmarkSequence,
|
|
334
341
|
window_length: int = 5,
|
|
335
342
|
polyorder: int = 2,
|
|
336
343
|
use_outlier_rejection: bool = True,
|
|
@@ -338,7 +345,7 @@ def smooth_landmarks_advanced(
|
|
|
338
345
|
ransac_threshold: float = 0.02,
|
|
339
346
|
bilateral_sigma_spatial: float = 3.0,
|
|
340
347
|
bilateral_sigma_intensity: float = 0.02,
|
|
341
|
-
) ->
|
|
348
|
+
) -> LandmarkSequence:
|
|
342
349
|
"""
|
|
343
350
|
Advanced landmark smoothing with outlier rejection and bilateral filtering.
|
|
344
351
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"""Kinematic calculations for drop-jump metrics."""
|
|
2
2
|
|
|
3
|
+
from typing import TypedDict
|
|
4
|
+
|
|
3
5
|
import numpy as np
|
|
6
|
+
from numpy.typing import NDArray
|
|
4
7
|
|
|
5
8
|
from ..core.smoothing import compute_acceleration_from_derivative
|
|
6
9
|
from .analysis import (
|
|
@@ -12,6 +15,25 @@ from .analysis import (
|
|
|
12
15
|
)
|
|
13
16
|
|
|
14
17
|
|
|
18
|
+
class DropJumpMetricsDict(TypedDict, total=False):
|
|
19
|
+
"""Type-safe dictionary for drop jump metrics JSON output."""
|
|
20
|
+
|
|
21
|
+
ground_contact_time_ms: float | None
|
|
22
|
+
flight_time_ms: float | None
|
|
23
|
+
jump_height_m: float | None
|
|
24
|
+
jump_height_kinematic_m: float | None
|
|
25
|
+
jump_height_trajectory_normalized: float | None
|
|
26
|
+
contact_start_frame: int | None
|
|
27
|
+
contact_end_frame: int | None
|
|
28
|
+
flight_start_frame: int | None
|
|
29
|
+
flight_end_frame: int | None
|
|
30
|
+
peak_height_frame: int | None
|
|
31
|
+
contact_start_frame_precise: float | None
|
|
32
|
+
contact_end_frame_precise: float | None
|
|
33
|
+
flight_start_frame_precise: float | None
|
|
34
|
+
flight_end_frame_precise: float | None
|
|
35
|
+
|
|
36
|
+
|
|
15
37
|
class DropJumpMetrics:
|
|
16
38
|
"""Container for drop-jump analysis metrics."""
|
|
17
39
|
|
|
@@ -32,7 +54,7 @@ class DropJumpMetrics:
|
|
|
32
54
|
self.flight_start_frame_precise: float | None = None
|
|
33
55
|
self.flight_end_frame_precise: float | None = None
|
|
34
56
|
|
|
35
|
-
def to_dict(self) ->
|
|
57
|
+
def to_dict(self) -> DropJumpMetricsDict:
|
|
36
58
|
"""Convert metrics to dictionary for JSON output."""
|
|
37
59
|
return {
|
|
38
60
|
"ground_contact_time_ms": (
|
|
@@ -108,7 +130,7 @@ class DropJumpMetrics:
|
|
|
108
130
|
|
|
109
131
|
def _determine_drop_start_frame(
|
|
110
132
|
drop_start_frame: int | None,
|
|
111
|
-
foot_y_positions: np.
|
|
133
|
+
foot_y_positions: NDArray[np.float64],
|
|
112
134
|
fps: float,
|
|
113
135
|
smoothing_window: int,
|
|
114
136
|
) -> int:
|
|
@@ -170,7 +192,7 @@ def _identify_main_contact_phase(
|
|
|
170
192
|
phases: list[tuple[int, int, ContactState]],
|
|
171
193
|
ground_phases: list[tuple[int, int, int]],
|
|
172
194
|
air_phases_indexed: list[tuple[int, int, int]],
|
|
173
|
-
foot_y_positions: np.
|
|
195
|
+
foot_y_positions: NDArray[np.float64],
|
|
174
196
|
) -> tuple[int, int, bool]:
|
|
175
197
|
"""Identify the main contact phase and determine if it's a drop jump.
|
|
176
198
|
|
|
@@ -260,7 +282,7 @@ def _analyze_flight_phase(
|
|
|
260
282
|
phases: list[tuple[int, int, ContactState]],
|
|
261
283
|
interpolated_phases: list[tuple[float, float, ContactState]],
|
|
262
284
|
contact_end: int,
|
|
263
|
-
foot_y_positions: np.
|
|
285
|
+
foot_y_positions: NDArray[np.float64],
|
|
264
286
|
fps: float,
|
|
265
287
|
smoothing_window: int,
|
|
266
288
|
polyorder: int,
|
|
@@ -341,7 +363,7 @@ def _analyze_flight_phase(
|
|
|
341
363
|
|
|
342
364
|
def calculate_drop_jump_metrics(
|
|
343
365
|
contact_states: list[ContactState],
|
|
344
|
-
foot_y_positions: np.
|
|
366
|
+
foot_y_positions: NDArray[np.float64],
|
|
345
367
|
fps: float,
|
|
346
368
|
drop_start_frame: int | None = None,
|
|
347
369
|
velocity_threshold: float = 0.02,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kinemotion
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.0
|
|
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
|
|
@@ -6,23 +6,23 @@ kinemotion/cmj/analysis.py,sha256=4HYGn4VDIB6oExAees-VcPfpNgWOltpgwjyNTU7YAb4,18
|
|
|
6
6
|
kinemotion/cmj/cli.py,sha256=bmDvNvL7cu65-R8YkRIZYKD0nuTA0IJnWLcLlH_kFm0,16843
|
|
7
7
|
kinemotion/cmj/debug_overlay.py,sha256=D-y2FQKI01KY0WXFKTKg6p9Qj3AkXCE7xjau3Ais080,15886
|
|
8
8
|
kinemotion/cmj/joint_angles.py,sha256=8ucpDGPvbt4iX3tx9eVxJEUv0laTm2Y58_--VzJCogE,9113
|
|
9
|
-
kinemotion/cmj/kinematics.py,sha256=
|
|
9
|
+
kinemotion/cmj/kinematics.py,sha256=bCtAQY2DIX2JMMou1Z8_Wil3a0sJhpw19pl1CsPKnBg,8202
|
|
10
10
|
kinemotion/core/__init__.py,sha256=3yzDhb5PekDNjydqrs8aWGneUGJBt-lB0SoB_Y2FXqU,1010
|
|
11
11
|
kinemotion/core/auto_tuning.py,sha256=j6cul_qC6k0XyryCG93C1AWH2MKPj3UBMzuX02xaqfI,11235
|
|
12
12
|
kinemotion/core/cli_utils.py,sha256=Pq1JF7yvK1YbH0tOUWKjplthCbWsJQt4Lv7esPYH4FM,7254
|
|
13
13
|
kinemotion/core/debug_overlay_utils.py,sha256=TyUb5okv5qw8oeaX3jsUO_kpwf1NnaHEAOTm-8LwTno,4587
|
|
14
14
|
kinemotion/core/filtering.py,sha256=f-m-aA59e4WqE6u-9MA51wssu7rI-Y_7n1cG8IWdeRQ,11241
|
|
15
15
|
kinemotion/core/pose.py,sha256=ztemdZ_ysVVK3gbXabm8qS_dr1VfJX9KZjmcO-Z-iNE,8532
|
|
16
|
-
kinemotion/core/smoothing.py,sha256=
|
|
16
|
+
kinemotion/core/smoothing.py,sha256=x4o3BnG6k8OaV3emgpoJDF84CE9k5RYR7BeSYH_-8Es,14092
|
|
17
17
|
kinemotion/core/video_io.py,sha256=0bJTheYidEqxGP5Y2dSO2x6sbOrnBDBu2TEiV8gT23A,7285
|
|
18
18
|
kinemotion/dropjump/__init__.py,sha256=yc1XiZ9vfo5h_n7PKVSiX2TTgaIfGL7Y7SkQtiDZj_E,838
|
|
19
19
|
kinemotion/dropjump/analysis.py,sha256=xx5NWy6s0eb9BEyO_FByY1Ahunaoh3TyaTAxjlPrvxg,27153
|
|
20
20
|
kinemotion/dropjump/cli.py,sha256=J2F8ij-UcybY7YjK_bncQZiHNzrgS3Y7uTBkNo7y_L4,21328
|
|
21
21
|
kinemotion/dropjump/debug_overlay.py,sha256=LkPw6ucb7beoYWS4L-Lvjs1KLCm5wAWDAfiznUeV2IQ,5668
|
|
22
|
-
kinemotion/dropjump/kinematics.py,sha256=
|
|
22
|
+
kinemotion/dropjump/kinematics.py,sha256=VZWdytkw58Vk9dsNe8U15sFB84kfZKLo4argvt0CTPM,16361
|
|
23
23
|
kinemotion/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
kinemotion-0.
|
|
25
|
-
kinemotion-0.
|
|
26
|
-
kinemotion-0.
|
|
27
|
-
kinemotion-0.
|
|
28
|
-
kinemotion-0.
|
|
24
|
+
kinemotion-0.21.0.dist-info/METADATA,sha256=obRgyAAsu-63mzynO4130BaLZiLu6duW9bG-dAMV6PU,20249
|
|
25
|
+
kinemotion-0.21.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
26
|
+
kinemotion-0.21.0.dist-info/entry_points.txt,sha256=zaqnAnjLvcdrk1Qvj5nvXZCZ2gp0prS7it1zTJygcIY,50
|
|
27
|
+
kinemotion-0.21.0.dist-info/licenses/LICENSE,sha256=KZajvqsHw0NoOHOi2q0FZ4NBe9HdV6oey-IPYAtHXfg,1088
|
|
28
|
+
kinemotion-0.21.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|