kinemotion 0.27.0__py3-none-any.whl → 0.28.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 +33 -23
- kinemotion/core/formatting.py +75 -0
- kinemotion/dropjump/kinematics.py +15 -46
- {kinemotion-0.27.0.dist-info → kinemotion-0.28.0.dist-info}/METADATA +1 -1
- {kinemotion-0.27.0.dist-info → kinemotion-0.28.0.dist-info}/RECORD +8 -7
- {kinemotion-0.27.0.dist-info → kinemotion-0.28.0.dist-info}/WHEEL +0 -0
- {kinemotion-0.27.0.dist-info → kinemotion-0.28.0.dist-info}/entry_points.txt +0 -0
- {kinemotion-0.27.0.dist-info → kinemotion-0.28.0.dist-info}/licenses/LICENSE +0 -0
kinemotion/cmj/kinematics.py
CHANGED
|
@@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, TypedDict
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
from numpy.typing import NDArray
|
|
8
8
|
|
|
9
|
+
from ..core.formatting import format_float_metric
|
|
10
|
+
|
|
9
11
|
if TYPE_CHECKING:
|
|
10
12
|
from ..core.metadata import ResultMetadata
|
|
11
13
|
from ..core.quality import QualityAssessment
|
|
@@ -15,14 +17,14 @@ class CMJDataDict(TypedDict, total=False):
|
|
|
15
17
|
"""Type-safe dictionary for CMJ measurement data."""
|
|
16
18
|
|
|
17
19
|
jump_height_m: float
|
|
18
|
-
|
|
20
|
+
flight_time_ms: float
|
|
19
21
|
countermovement_depth_m: float
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
eccentric_duration_ms: float
|
|
23
|
+
concentric_duration_ms: float
|
|
24
|
+
total_movement_time_ms: float
|
|
23
25
|
peak_eccentric_velocity_m_s: float
|
|
24
26
|
peak_concentric_velocity_m_s: float
|
|
25
|
-
|
|
27
|
+
transition_time_ms: float | None
|
|
26
28
|
standing_start_frame: float | None
|
|
27
29
|
lowest_point_frame: float
|
|
28
30
|
takeoff_frame: float
|
|
@@ -43,14 +45,14 @@ class CMJMetrics:
|
|
|
43
45
|
|
|
44
46
|
Attributes:
|
|
45
47
|
jump_height: Maximum jump height in meters
|
|
46
|
-
flight_time: Time spent in the air in
|
|
48
|
+
flight_time: Time spent in the air in milliseconds
|
|
47
49
|
countermovement_depth: Vertical distance traveled during eccentric phase in meters
|
|
48
|
-
eccentric_duration: Time from countermovement start to lowest point in
|
|
49
|
-
concentric_duration: Time from lowest point to takeoff in
|
|
50
|
-
total_movement_time: Total time from countermovement start to takeoff in
|
|
50
|
+
eccentric_duration: Time from countermovement start to lowest point in milliseconds
|
|
51
|
+
concentric_duration: Time from lowest point to takeoff in milliseconds
|
|
52
|
+
total_movement_time: Total time from countermovement start to takeoff in milliseconds
|
|
51
53
|
peak_eccentric_velocity: Maximum downward velocity during countermovement in m/s
|
|
52
54
|
peak_concentric_velocity: Maximum upward velocity during propulsion in m/s
|
|
53
|
-
transition_time: Duration at lowest point (amortization phase) in
|
|
55
|
+
transition_time: Duration at lowest point (amortization phase) in milliseconds
|
|
54
56
|
standing_start_frame: Frame where standing phase ends (countermovement begins)
|
|
55
57
|
lowest_point_frame: Frame at lowest point of countermovement
|
|
56
58
|
takeoff_frame: Frame where athlete leaves ground
|
|
@@ -85,19 +87,27 @@ class CMJMetrics:
|
|
|
85
87
|
Dictionary with nested data and metadata structure.
|
|
86
88
|
"""
|
|
87
89
|
data: CMJDataDict = {
|
|
88
|
-
"jump_height_m":
|
|
89
|
-
"
|
|
90
|
-
"countermovement_depth_m":
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
90
|
+
"jump_height_m": format_float_metric(self.jump_height, 1, 3), # type: ignore[typeddict-item]
|
|
91
|
+
"flight_time_ms": format_float_metric(self.flight_time, 1000, 2), # type: ignore[typeddict-item]
|
|
92
|
+
"countermovement_depth_m": format_float_metric(
|
|
93
|
+
self.countermovement_depth, 1, 3
|
|
94
|
+
), # type: ignore[typeddict-item]
|
|
95
|
+
"eccentric_duration_ms": format_float_metric(
|
|
96
|
+
self.eccentric_duration, 1000, 2
|
|
97
|
+
), # type: ignore[typeddict-item]
|
|
98
|
+
"concentric_duration_ms": format_float_metric(
|
|
99
|
+
self.concentric_duration, 1000, 2
|
|
100
|
+
), # type: ignore[typeddict-item]
|
|
101
|
+
"total_movement_time_ms": format_float_metric(
|
|
102
|
+
self.total_movement_time, 1000, 2
|
|
103
|
+
), # type: ignore[typeddict-item]
|
|
104
|
+
"peak_eccentric_velocity_m_s": format_float_metric(
|
|
105
|
+
self.peak_eccentric_velocity, 1, 4
|
|
106
|
+
), # type: ignore[typeddict-item]
|
|
107
|
+
"peak_concentric_velocity_m_s": format_float_metric(
|
|
108
|
+
self.peak_concentric_velocity, 1, 4
|
|
109
|
+
), # type: ignore[typeddict-item]
|
|
110
|
+
"transition_time_ms": format_float_metric(self.transition_time, 1000, 2),
|
|
101
111
|
"standing_start_frame": (
|
|
102
112
|
float(self.standing_start_frame)
|
|
103
113
|
if self.standing_start_frame is not None
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Formatting utilities for consistent numeric output across jump analysis types.
|
|
2
|
+
|
|
3
|
+
This module provides shared helpers for formatting numeric values with appropriate
|
|
4
|
+
precision based on measurement type and capabilities of video-based analysis.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# Standard precision values for different measurement types
|
|
8
|
+
# These values are chosen based on:
|
|
9
|
+
# - Video analysis capabilities (30-240 fps)
|
|
10
|
+
# - Typical measurement uncertainty in video-based biomechanics
|
|
11
|
+
# - Balance between accuracy and readability
|
|
12
|
+
|
|
13
|
+
PRECISION_TIME_MS = 2 # Time in milliseconds: ±0.01ms (e.g., 534.12)
|
|
14
|
+
PRECISION_DISTANCE_M = 3 # Distance in meters: ±1mm (e.g., 0.352)
|
|
15
|
+
PRECISION_VELOCITY_M_S = 4 # Velocity in m/s: ±0.0001 m/s (e.g., 2.6340)
|
|
16
|
+
PRECISION_FRAME = 3 # Sub-frame interpolation precision (e.g., 154.342)
|
|
17
|
+
PRECISION_NORMALIZED = 4 # Normalized values 0-1 ratios (e.g., 0.0582)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def format_float_metric(
|
|
21
|
+
value: float | None,
|
|
22
|
+
multiplier: float = 1.0,
|
|
23
|
+
decimals: int = 2,
|
|
24
|
+
) -> float | None:
|
|
25
|
+
"""Format a float metric value with optional scaling and rounding.
|
|
26
|
+
|
|
27
|
+
This helper ensures consistent precision across all jump analysis outputs,
|
|
28
|
+
preventing false precision in measurements while maintaining appropriate
|
|
29
|
+
accuracy for the measurement type.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
value: The value to format, or None
|
|
33
|
+
multiplier: Factor to multiply value by (e.g., 1000 for seconds→milliseconds)
|
|
34
|
+
decimals: Number of decimal places to round to
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Formatted value rounded to specified decimals, or None if input is None
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
>>> format_float_metric(0.534123, 1000, 2) # seconds to ms
|
|
41
|
+
534.12
|
|
42
|
+
>>> format_float_metric(0.3521234, 1, 3) # meters
|
|
43
|
+
0.352
|
|
44
|
+
>>> format_float_metric(None, 1, 2)
|
|
45
|
+
None
|
|
46
|
+
>>> format_float_metric(-1.23456, 1, 4) # negative values preserved
|
|
47
|
+
-1.2346
|
|
48
|
+
"""
|
|
49
|
+
if value is None:
|
|
50
|
+
return None
|
|
51
|
+
return round(value * multiplier, decimals)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def format_int_metric(value: float | int | None) -> int | None:
|
|
55
|
+
"""Format a value as an integer.
|
|
56
|
+
|
|
57
|
+
Used for frame numbers and other integer-valued metrics.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
value: The value to format, or None
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Value converted to int, or None if input is None
|
|
64
|
+
|
|
65
|
+
Examples:
|
|
66
|
+
>>> format_int_metric(42.7)
|
|
67
|
+
42
|
|
68
|
+
>>> format_int_metric(None)
|
|
69
|
+
None
|
|
70
|
+
>>> format_int_metric(154)
|
|
71
|
+
154
|
|
72
|
+
"""
|
|
73
|
+
if value is None:
|
|
74
|
+
return None
|
|
75
|
+
return int(value)
|
|
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, TypedDict
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
from numpy.typing import NDArray
|
|
7
7
|
|
|
8
|
+
from ..core.formatting import format_float_metric, format_int_metric
|
|
8
9
|
from ..core.smoothing import compute_acceleration_from_derivative
|
|
9
10
|
from .analysis import (
|
|
10
11
|
ContactState,
|
|
@@ -19,38 +20,6 @@ if TYPE_CHECKING:
|
|
|
19
20
|
from ..core.quality import QualityAssessment
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def _format_float_metric(
|
|
23
|
-
value: float | None, multiplier: float = 1, decimals: int = 2
|
|
24
|
-
) -> float | None:
|
|
25
|
-
"""Format a float metric value with optional scaling and rounding.
|
|
26
|
-
|
|
27
|
-
Args:
|
|
28
|
-
value: The value to format, or None
|
|
29
|
-
multiplier: Factor to multiply value by (default: 1)
|
|
30
|
-
decimals: Number of decimal places to round to (default: 2)
|
|
31
|
-
|
|
32
|
-
Returns:
|
|
33
|
-
Formatted value rounded to specified decimals, or None if input is None
|
|
34
|
-
"""
|
|
35
|
-
if value is None:
|
|
36
|
-
return None
|
|
37
|
-
return round(value * multiplier, decimals)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _format_int_metric(value: float | int | None) -> int | None:
|
|
41
|
-
"""Format a value as an integer.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
value: The value to format, or None
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
Value converted to int, or None if input is None
|
|
48
|
-
"""
|
|
49
|
-
if value is None:
|
|
50
|
-
return None
|
|
51
|
-
return int(value)
|
|
52
|
-
|
|
53
|
-
|
|
54
23
|
class DropJumpDataDict(TypedDict, total=False):
|
|
55
24
|
"""Type-safe dictionary for drop jump measurement data."""
|
|
56
25
|
|
|
@@ -108,32 +77,32 @@ class DropJumpMetrics:
|
|
|
108
77
|
Dictionary containing formatted metric values.
|
|
109
78
|
"""
|
|
110
79
|
return {
|
|
111
|
-
"ground_contact_time_ms":
|
|
80
|
+
"ground_contact_time_ms": format_float_metric(
|
|
112
81
|
self.ground_contact_time, 1000, 2
|
|
113
82
|
),
|
|
114
|
-
"flight_time_ms":
|
|
115
|
-
"jump_height_m":
|
|
116
|
-
"jump_height_kinematic_m":
|
|
83
|
+
"flight_time_ms": format_float_metric(self.flight_time, 1000, 2),
|
|
84
|
+
"jump_height_m": format_float_metric(self.jump_height, 1, 3),
|
|
85
|
+
"jump_height_kinematic_m": format_float_metric(
|
|
117
86
|
self.jump_height_kinematic, 1, 3
|
|
118
87
|
),
|
|
119
|
-
"jump_height_trajectory_normalized":
|
|
88
|
+
"jump_height_trajectory_normalized": format_float_metric(
|
|
120
89
|
self.jump_height_trajectory, 1, 4
|
|
121
90
|
),
|
|
122
|
-
"contact_start_frame":
|
|
123
|
-
"contact_end_frame":
|
|
124
|
-
"flight_start_frame":
|
|
125
|
-
"flight_end_frame":
|
|
126
|
-
"peak_height_frame":
|
|
127
|
-
"contact_start_frame_precise":
|
|
91
|
+
"contact_start_frame": format_int_metric(self.contact_start_frame),
|
|
92
|
+
"contact_end_frame": format_int_metric(self.contact_end_frame),
|
|
93
|
+
"flight_start_frame": format_int_metric(self.flight_start_frame),
|
|
94
|
+
"flight_end_frame": format_int_metric(self.flight_end_frame),
|
|
95
|
+
"peak_height_frame": format_int_metric(self.peak_height_frame),
|
|
96
|
+
"contact_start_frame_precise": format_float_metric(
|
|
128
97
|
self.contact_start_frame_precise, 1, 3
|
|
129
98
|
),
|
|
130
|
-
"contact_end_frame_precise":
|
|
99
|
+
"contact_end_frame_precise": format_float_metric(
|
|
131
100
|
self.contact_end_frame_precise, 1, 3
|
|
132
101
|
),
|
|
133
|
-
"flight_start_frame_precise":
|
|
102
|
+
"flight_start_frame_precise": format_float_metric(
|
|
134
103
|
self.flight_start_frame_precise, 1, 3
|
|
135
104
|
),
|
|
136
|
-
"flight_end_frame_precise":
|
|
105
|
+
"flight_end_frame_precise": format_float_metric(
|
|
137
106
|
self.flight_end_frame_precise, 1, 3
|
|
138
107
|
),
|
|
139
108
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kinemotion
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.28.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,12 +6,13 @@ kinemotion/cmj/analysis.py,sha256=4HYGn4VDIB6oExAees-VcPfpNgWOltpgwjyNTU7YAb4,18
|
|
|
6
6
|
kinemotion/cmj/cli.py,sha256=12FEfWrseG4kCUbgHHdBPkWp6zzVQ0VAzfgNJotArmM,10792
|
|
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=-iBFg2AkQR4LaThCQzO09fx6qJed27ZfMDQJgE7Si4k,9772
|
|
10
10
|
kinemotion/core/__init__.py,sha256=HsqolRa60cW3vrG8F9Lvr9WvWcs5hCmsTzSgo7imi-4,1278
|
|
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
|
+
kinemotion/core/formatting.py,sha256=G_3eqgOtym9RFOZVEwCxye4A2cyrmgvtQ214vIshowU,2480
|
|
15
16
|
kinemotion/core/metadata.py,sha256=PyGHL6sx7Hj21lyorg2VsWP9BGTj_y_-wWU6eKCEfJo,6817
|
|
16
17
|
kinemotion/core/pose.py,sha256=ztemdZ_ysVVK3gbXabm8qS_dr1VfJX9KZjmcO-Z-iNE,8532
|
|
17
18
|
kinemotion/core/quality.py,sha256=OC9nuf5IrQ9xURf3eA50VoNWOqkGwbjJpS90q2FDQzA,13082
|
|
@@ -21,10 +22,10 @@ kinemotion/dropjump/__init__.py,sha256=yc1XiZ9vfo5h_n7PKVSiX2TTgaIfGL7Y7SkQtiDZj
|
|
|
21
22
|
kinemotion/dropjump/analysis.py,sha256=BQ5NqSPNJjFQOb-W4bXSLvjCgWd-nvqx5NElyeqZJC4,29067
|
|
22
23
|
kinemotion/dropjump/cli.py,sha256=ZyroaYPwz8TgfL39Wcaj6m68Awl6lYXC75ttaflU-c0,16236
|
|
23
24
|
kinemotion/dropjump/debug_overlay.py,sha256=LkPw6ucb7beoYWS4L-Lvjs1KLCm5wAWDAfiznUeV2IQ,5668
|
|
24
|
-
kinemotion/dropjump/kinematics.py,sha256=
|
|
25
|
+
kinemotion/dropjump/kinematics.py,sha256=Yr3G7AQwtYy1dxyeOAYfqqgd4pzoZwWQAhZzxI5RbnE,16658
|
|
25
26
|
kinemotion/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
-
kinemotion-0.
|
|
27
|
-
kinemotion-0.
|
|
28
|
-
kinemotion-0.
|
|
29
|
-
kinemotion-0.
|
|
30
|
-
kinemotion-0.
|
|
27
|
+
kinemotion-0.28.0.dist-info/METADATA,sha256=RIUN7r__qFVHSNzj2CglERzONmcmLiIYrDZLkpztZu8,23244
|
|
28
|
+
kinemotion-0.28.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
29
|
+
kinemotion-0.28.0.dist-info/entry_points.txt,sha256=zaqnAnjLvcdrk1Qvj5nvXZCZ2gp0prS7it1zTJygcIY,50
|
|
30
|
+
kinemotion-0.28.0.dist-info/licenses/LICENSE,sha256=KZajvqsHw0NoOHOi2q0FZ4NBe9HdV6oey-IPYAtHXfg,1088
|
|
31
|
+
kinemotion-0.28.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|