kinemotion 0.35.0__py3-none-any.whl → 0.35.2__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.
- kinemotion/api.py +7 -4
- kinemotion/cmj/analysis.py +51 -3
- kinemotion/cmj/metrics_validator.py +107 -37
- kinemotion/cmj/validation_bounds.py +4 -1
- kinemotion/core/auto_tuning.py +6 -4
- kinemotion/dropjump/kinematics.py +7 -1
- {kinemotion-0.35.0.dist-info → kinemotion-0.35.2.dist-info}/METADATA +1 -1
- {kinemotion-0.35.0.dist-info → kinemotion-0.35.2.dist-info}/RECORD +11 -11
- {kinemotion-0.35.0.dist-info → kinemotion-0.35.2.dist-info}/WHEEL +0 -0
- {kinemotion-0.35.0.dist-info → kinemotion-0.35.2.dist-info}/entry_points.txt +0 -0
- {kinemotion-0.35.0.dist-info → kinemotion-0.35.2.dist-info}/licenses/LICENSE +0 -0
kinemotion/api.py
CHANGED
|
@@ -548,9 +548,12 @@ def process_dropjump_video(
|
|
|
548
548
|
|
|
549
549
|
# Check if drop start was auto-detected
|
|
550
550
|
drop_frame = None
|
|
551
|
-
if drop_start_frame is None and metrics.
|
|
552
|
-
# Auto-detected
|
|
553
|
-
drop_frame = metrics.
|
|
551
|
+
if drop_start_frame is None and metrics.drop_start_frame is not None:
|
|
552
|
+
# Auto-detected drop start from box
|
|
553
|
+
drop_frame = metrics.drop_start_frame
|
|
554
|
+
elif drop_start_frame is not None:
|
|
555
|
+
# Manual drop start provided
|
|
556
|
+
drop_frame = drop_start_frame
|
|
554
557
|
|
|
555
558
|
algorithm_config = AlgorithmConfig(
|
|
556
559
|
detection_method="forward_search",
|
|
@@ -1057,7 +1060,7 @@ def process_cmj_video(
|
|
|
1057
1060
|
|
|
1058
1061
|
# Validate metrics against physiological bounds
|
|
1059
1062
|
validator = CMJMetricsValidator()
|
|
1060
|
-
validation_result = validator.validate(metrics.to_dict()
|
|
1063
|
+
validation_result = validator.validate(metrics.to_dict()) # type: ignore[arg-type]
|
|
1061
1064
|
metrics.validation_result = validation_result
|
|
1062
1065
|
|
|
1063
1066
|
if verbose and validation_result.issues:
|
kinemotion/cmj/analysis.py
CHANGED
|
@@ -422,11 +422,59 @@ def find_landing_frame(
|
|
|
422
422
|
return float(landing_search_start + landing_idx)
|
|
423
423
|
|
|
424
424
|
|
|
425
|
-
def find_standing_end(
|
|
426
|
-
|
|
425
|
+
def find_standing_end(
|
|
426
|
+
velocities: np.ndarray,
|
|
427
|
+
lowest_point: float,
|
|
428
|
+
positions: np.ndarray | None = None,
|
|
429
|
+
accelerations: np.ndarray | None = None,
|
|
430
|
+
) -> float | None:
|
|
431
|
+
"""
|
|
432
|
+
Find end of standing phase before lowest point.
|
|
433
|
+
|
|
434
|
+
Uses acceleration-based detection to identify when downward movement begins.
|
|
435
|
+
Acceleration captures movement initiation even when velocity is negligible,
|
|
436
|
+
making it ideal for detecting slow countermovement starts.
|
|
437
|
+
|
|
438
|
+
Args:
|
|
439
|
+
velocities: Signed velocity array (for backward compatibility)
|
|
440
|
+
lowest_point: Frame index of lowest point
|
|
441
|
+
positions: Position array (unused, kept for backward compatibility)
|
|
442
|
+
accelerations: Acceleration array (if provided, uses
|
|
443
|
+
acceleration-based detection)
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
Frame index where standing ends (countermovement begins), or None
|
|
447
|
+
"""
|
|
427
448
|
if lowest_point <= 20:
|
|
428
449
|
return None
|
|
429
450
|
|
|
451
|
+
# Acceleration-based detection (best for detecting movement initiation)
|
|
452
|
+
if accelerations is not None:
|
|
453
|
+
# Use middle section of standing phase as baseline (avoids initial settling)
|
|
454
|
+
baseline_start = 10
|
|
455
|
+
baseline_end = min(40, int(lowest_point) - 10)
|
|
456
|
+
|
|
457
|
+
if baseline_end <= baseline_start:
|
|
458
|
+
return None
|
|
459
|
+
|
|
460
|
+
# Calculate baseline acceleration statistics
|
|
461
|
+
baseline_accel = accelerations[baseline_start:baseline_end]
|
|
462
|
+
baseline_mean = float(np.mean(baseline_accel))
|
|
463
|
+
baseline_std = float(np.std(baseline_accel))
|
|
464
|
+
|
|
465
|
+
# Threshold: 3 standard deviations above baseline
|
|
466
|
+
# This detects when acceleration significantly increases (movement starts)
|
|
467
|
+
accel_threshold = baseline_mean + 3.0 * baseline_std
|
|
468
|
+
|
|
469
|
+
# Search forward from baseline for acceleration spike
|
|
470
|
+
for i in range(baseline_end, int(lowest_point)):
|
|
471
|
+
if accelerations[i] > accel_threshold:
|
|
472
|
+
# Found start of downward acceleration
|
|
473
|
+
return float(i)
|
|
474
|
+
|
|
475
|
+
return None
|
|
476
|
+
|
|
477
|
+
# Fallback: velocity-based detection (legacy)
|
|
430
478
|
standing_search = velocities[: int(lowest_point)]
|
|
431
479
|
low_vel = np.abs(standing_search) < 0.005
|
|
432
480
|
if np.any(low_vel):
|
|
@@ -479,6 +527,6 @@ def detect_cmj_phases(
|
|
|
479
527
|
takeoff_frame = find_takeoff_frame(velocities, peak_height_frame, fps)
|
|
480
528
|
lowest_point = find_lowest_frame(velocities, positions, takeoff_frame, fps)
|
|
481
529
|
landing_frame = find_landing_frame(accelerations, peak_height_frame, fps)
|
|
482
|
-
standing_end = find_standing_end(velocities, lowest_point)
|
|
530
|
+
standing_end = find_standing_end(velocities, lowest_point, positions, accelerations)
|
|
483
531
|
|
|
484
532
|
return (standing_end, lowest_point, takeoff_frame, landing_frame)
|
|
@@ -68,6 +68,22 @@ class CMJValidationResult(ValidationResult):
|
|
|
68
68
|
class CMJMetricsValidator(MetricsValidator):
|
|
69
69
|
"""Comprehensive CMJ metrics validator."""
|
|
70
70
|
|
|
71
|
+
@staticmethod
|
|
72
|
+
def _get_metric_value(
|
|
73
|
+
data: dict, key_with_suffix: str, key_without_suffix: str
|
|
74
|
+
) -> float | None:
|
|
75
|
+
"""Get metric value, supporting both suffixed and legacy key formats.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
data: Dictionary containing metrics
|
|
79
|
+
key_with_suffix: Key with unit suffix (e.g., "flight_time_ms")
|
|
80
|
+
key_without_suffix: Legacy key without suffix (e.g., "flight_time")
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Metric value or None if not found
|
|
84
|
+
"""
|
|
85
|
+
return data.get(key_with_suffix) or data.get(key_without_suffix)
|
|
86
|
+
|
|
71
87
|
def validate(self, metrics: dict) -> CMJValidationResult:
|
|
72
88
|
"""Validate CMJ metrics comprehensively.
|
|
73
89
|
|
|
@@ -87,25 +103,28 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
87
103
|
|
|
88
104
|
profile = result.athlete_profile
|
|
89
105
|
|
|
106
|
+
# Extract metric values (handle nested "data" structure)
|
|
107
|
+
data = metrics.get("data", metrics) # Support both structures
|
|
108
|
+
|
|
90
109
|
# PRIMARY BOUNDS CHECKS
|
|
91
|
-
self._check_flight_time(
|
|
92
|
-
self._check_jump_height(
|
|
93
|
-
self._check_countermovement_depth(
|
|
94
|
-
self._check_concentric_duration(
|
|
95
|
-
self._check_eccentric_duration(
|
|
96
|
-
self._check_peak_velocities(
|
|
110
|
+
self._check_flight_time(data, result, profile)
|
|
111
|
+
self._check_jump_height(data, result, profile)
|
|
112
|
+
self._check_countermovement_depth(data, result, profile)
|
|
113
|
+
self._check_concentric_duration(data, result, profile)
|
|
114
|
+
self._check_eccentric_duration(data, result, profile)
|
|
115
|
+
self._check_peak_velocities(data, result, profile)
|
|
97
116
|
|
|
98
117
|
# CROSS-VALIDATION CHECKS
|
|
99
|
-
self._check_flight_time_height_consistency(
|
|
100
|
-
self._check_velocity_height_consistency(
|
|
101
|
-
self._check_rsi_validity(
|
|
118
|
+
self._check_flight_time_height_consistency(data, result)
|
|
119
|
+
self._check_velocity_height_consistency(data, result)
|
|
120
|
+
self._check_rsi_validity(data, result, profile)
|
|
102
121
|
|
|
103
122
|
# CONSISTENCY CHECKS
|
|
104
|
-
self._check_depth_height_ratio(
|
|
105
|
-
self._check_contact_depth_ratio(
|
|
123
|
+
self._check_depth_height_ratio(data, result)
|
|
124
|
+
self._check_contact_depth_ratio(data, result)
|
|
106
125
|
|
|
107
126
|
# TRIPLE EXTENSION ANGLES
|
|
108
|
-
self._check_triple_extension(
|
|
127
|
+
self._check_triple_extension(data, result, profile)
|
|
109
128
|
|
|
110
129
|
# Finalize status
|
|
111
130
|
result.finalize_status()
|
|
@@ -116,10 +135,18 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
116
135
|
self, metrics: dict, result: CMJValidationResult, profile: AthleteProfile
|
|
117
136
|
) -> None:
|
|
118
137
|
"""Validate flight time."""
|
|
119
|
-
|
|
120
|
-
|
|
138
|
+
flight_time_raw = self._get_metric_value(
|
|
139
|
+
metrics, "flight_time_ms", "flight_time"
|
|
140
|
+
)
|
|
141
|
+
if flight_time_raw is None:
|
|
121
142
|
return
|
|
122
143
|
|
|
144
|
+
# If value is in seconds (legacy), use as-is; if in ms, convert
|
|
145
|
+
if flight_time_raw < 10: # Likely in seconds
|
|
146
|
+
flight_time = flight_time_raw
|
|
147
|
+
else: # In milliseconds
|
|
148
|
+
flight_time = flight_time_raw / 1000.0
|
|
149
|
+
|
|
123
150
|
bounds = CMJBounds.FLIGHT_TIME
|
|
124
151
|
|
|
125
152
|
if not bounds.is_physically_possible(flight_time):
|
|
@@ -159,7 +186,7 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
159
186
|
self, metrics: dict, result: CMJValidationResult, profile: AthleteProfile
|
|
160
187
|
) -> None:
|
|
161
188
|
"""Validate jump height."""
|
|
162
|
-
jump_height =
|
|
189
|
+
jump_height = self._get_metric_value(metrics, "jump_height_m", "jump_height")
|
|
163
190
|
if jump_height is None:
|
|
164
191
|
return
|
|
165
192
|
|
|
@@ -201,7 +228,9 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
201
228
|
self, metrics: dict, result: CMJValidationResult, profile: AthleteProfile
|
|
202
229
|
) -> None:
|
|
203
230
|
"""Validate countermovement depth."""
|
|
204
|
-
depth =
|
|
231
|
+
depth = self._get_metric_value(
|
|
232
|
+
metrics, "countermovement_depth_m", "countermovement_depth"
|
|
233
|
+
)
|
|
205
234
|
if depth is None:
|
|
206
235
|
return
|
|
207
236
|
|
|
@@ -243,10 +272,19 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
243
272
|
self, metrics: dict, result: CMJValidationResult, profile: AthleteProfile
|
|
244
273
|
) -> None:
|
|
245
274
|
"""Validate concentric duration (contact time)."""
|
|
246
|
-
|
|
247
|
-
|
|
275
|
+
duration_raw = self._get_metric_value(
|
|
276
|
+
metrics, "concentric_duration_ms", "concentric_duration"
|
|
277
|
+
)
|
|
278
|
+
if duration_raw is None:
|
|
248
279
|
return
|
|
249
280
|
|
|
281
|
+
# If value is in seconds (legacy), convert to ms first
|
|
282
|
+
# Values >10 are assumed to be in ms, <10 assumed to be in seconds
|
|
283
|
+
if duration_raw < 10: # Likely in seconds
|
|
284
|
+
duration = duration_raw
|
|
285
|
+
else: # In milliseconds
|
|
286
|
+
duration = duration_raw / 1000.0
|
|
287
|
+
|
|
250
288
|
bounds = CMJBounds.CONCENTRIC_DURATION
|
|
251
289
|
|
|
252
290
|
if not bounds.is_physically_possible(duration):
|
|
@@ -286,10 +324,18 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
286
324
|
self, metrics: dict, result: CMJValidationResult, profile: AthleteProfile
|
|
287
325
|
) -> None:
|
|
288
326
|
"""Validate eccentric duration."""
|
|
289
|
-
|
|
290
|
-
|
|
327
|
+
duration_raw = self._get_metric_value(
|
|
328
|
+
metrics, "eccentric_duration_ms", "eccentric_duration"
|
|
329
|
+
)
|
|
330
|
+
if duration_raw is None:
|
|
291
331
|
return
|
|
292
332
|
|
|
333
|
+
# If value is in seconds (legacy), use as-is; if in ms, convert
|
|
334
|
+
if duration_raw < 10: # Likely in seconds
|
|
335
|
+
duration = duration_raw
|
|
336
|
+
else: # In milliseconds
|
|
337
|
+
duration = duration_raw / 1000.0
|
|
338
|
+
|
|
293
339
|
bounds = CMJBounds.ECCENTRIC_DURATION
|
|
294
340
|
|
|
295
341
|
if not bounds.is_physically_possible(duration):
|
|
@@ -321,7 +367,9 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
321
367
|
) -> None:
|
|
322
368
|
"""Validate peak eccentric and concentric velocities."""
|
|
323
369
|
# Eccentric
|
|
324
|
-
ecc_vel =
|
|
370
|
+
ecc_vel = self._get_metric_value(
|
|
371
|
+
metrics, "peak_eccentric_velocity_m_s", "peak_eccentric_velocity"
|
|
372
|
+
)
|
|
325
373
|
if ecc_vel is not None:
|
|
326
374
|
bounds = CMJBounds.PEAK_ECCENTRIC_VELOCITY
|
|
327
375
|
if not bounds.is_physically_possible(ecc_vel):
|
|
@@ -349,7 +397,9 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
349
397
|
)
|
|
350
398
|
|
|
351
399
|
# Concentric
|
|
352
|
-
con_vel =
|
|
400
|
+
con_vel = self._get_metric_value(
|
|
401
|
+
metrics, "peak_concentric_velocity_m_s", "peak_concentric_velocity"
|
|
402
|
+
)
|
|
353
403
|
if con_vel is not None:
|
|
354
404
|
bounds = CMJBounds.PEAK_CONCENTRIC_VELOCITY
|
|
355
405
|
if not bounds.is_physically_possible(con_vel):
|
|
@@ -390,12 +440,15 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
390
440
|
self, metrics: dict, result: CMJValidationResult
|
|
391
441
|
) -> None:
|
|
392
442
|
"""Verify jump height is consistent with flight time."""
|
|
393
|
-
|
|
394
|
-
jump_height = metrics.get("
|
|
443
|
+
flight_time_ms = metrics.get("flight_time_ms")
|
|
444
|
+
jump_height = metrics.get("jump_height_m")
|
|
395
445
|
|
|
396
|
-
if
|
|
446
|
+
if flight_time_ms is None or jump_height is None:
|
|
397
447
|
return
|
|
398
448
|
|
|
449
|
+
# Convert ms to seconds
|
|
450
|
+
flight_time = flight_time_ms / 1000.0
|
|
451
|
+
|
|
399
452
|
# h = g * t^2 / 8
|
|
400
453
|
g = 9.81
|
|
401
454
|
expected_height = (g * flight_time**2) / 8
|
|
@@ -424,8 +477,8 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
424
477
|
self, metrics: dict, result: CMJValidationResult
|
|
425
478
|
) -> None:
|
|
426
479
|
"""Verify peak velocity is consistent with jump height."""
|
|
427
|
-
velocity = metrics.get("
|
|
428
|
-
jump_height = metrics.get("
|
|
480
|
+
velocity = metrics.get("peak_concentric_velocity_m_s")
|
|
481
|
+
jump_height = metrics.get("jump_height_m")
|
|
429
482
|
|
|
430
483
|
if velocity is None or jump_height is None:
|
|
431
484
|
return
|
|
@@ -461,16 +514,31 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
461
514
|
self, metrics: dict, result: CMJValidationResult, profile: AthleteProfile
|
|
462
515
|
) -> None:
|
|
463
516
|
"""Validate Reactive Strength Index."""
|
|
464
|
-
|
|
465
|
-
|
|
517
|
+
flight_time_raw = self._get_metric_value(
|
|
518
|
+
metrics, "flight_time_ms", "flight_time"
|
|
519
|
+
)
|
|
520
|
+
concentric_duration_raw = self._get_metric_value(
|
|
521
|
+
metrics, "concentric_duration_ms", "concentric_duration"
|
|
522
|
+
)
|
|
466
523
|
|
|
467
524
|
if (
|
|
468
|
-
|
|
469
|
-
or
|
|
470
|
-
or
|
|
525
|
+
flight_time_raw is None
|
|
526
|
+
or concentric_duration_raw is None
|
|
527
|
+
or concentric_duration_raw == 0
|
|
471
528
|
):
|
|
472
529
|
return
|
|
473
530
|
|
|
531
|
+
# Convert to seconds if needed
|
|
532
|
+
if flight_time_raw < 10: # Likely in seconds
|
|
533
|
+
flight_time = flight_time_raw
|
|
534
|
+
else: # In milliseconds
|
|
535
|
+
flight_time = flight_time_raw / 1000.0
|
|
536
|
+
|
|
537
|
+
if concentric_duration_raw < 10: # Likely in seconds
|
|
538
|
+
concentric_duration = concentric_duration_raw
|
|
539
|
+
else: # In milliseconds
|
|
540
|
+
concentric_duration = concentric_duration_raw / 1000.0
|
|
541
|
+
|
|
474
542
|
rsi = flight_time / concentric_duration
|
|
475
543
|
result.rsi = rsi
|
|
476
544
|
|
|
@@ -513,8 +581,8 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
513
581
|
self, metrics: dict, result: CMJValidationResult
|
|
514
582
|
) -> None:
|
|
515
583
|
"""Check countermovement depth to jump height ratio."""
|
|
516
|
-
depth = metrics.get("
|
|
517
|
-
jump_height = metrics.get("
|
|
584
|
+
depth = metrics.get("countermovement_depth_m")
|
|
585
|
+
jump_height = metrics.get("jump_height_m")
|
|
518
586
|
|
|
519
587
|
if (
|
|
520
588
|
depth is None or jump_height is None or depth < 0.05
|
|
@@ -557,12 +625,14 @@ class CMJMetricsValidator(MetricsValidator):
|
|
|
557
625
|
self, metrics: dict, result: CMJValidationResult
|
|
558
626
|
) -> None:
|
|
559
627
|
"""Check contact time to countermovement depth ratio."""
|
|
560
|
-
|
|
561
|
-
depth = metrics.get("
|
|
628
|
+
contact_ms = metrics.get("concentric_duration_ms")
|
|
629
|
+
depth = metrics.get("countermovement_depth_m")
|
|
562
630
|
|
|
563
|
-
if
|
|
631
|
+
if contact_ms is None or depth is None or depth < 0.05:
|
|
564
632
|
return
|
|
565
633
|
|
|
634
|
+
# Convert ms to seconds for ratio calculation
|
|
635
|
+
contact = contact_ms / 1000.0
|
|
566
636
|
ratio = contact / depth
|
|
567
637
|
result.contact_depth_ratio = ratio
|
|
568
638
|
|
|
@@ -323,7 +323,10 @@ def estimate_athlete_profile(
|
|
|
323
323
|
Returns:
|
|
324
324
|
Estimated AthleteProfile
|
|
325
325
|
"""
|
|
326
|
-
|
|
326
|
+
# Support both nested "data" structure and flat structure
|
|
327
|
+
# Extract with unit suffix as used in serialization, or without suffix (legacy)
|
|
328
|
+
data = metrics_dict.get("data", metrics_dict)
|
|
329
|
+
jump_height = data.get("jump_height_m") or data.get("jump_height", 0)
|
|
327
330
|
|
|
328
331
|
if jump_height < 0.20:
|
|
329
332
|
return AthleteProfile.ELDERLY
|
kinemotion/core/auto_tuning.py
CHANGED
|
@@ -108,10 +108,12 @@ def auto_tune_parameters(
|
|
|
108
108
|
# =================================================================
|
|
109
109
|
|
|
110
110
|
# Velocity threshold: Scale inversely with fps
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
|
|
111
|
+
# Empirically validated with 45° oblique videos at 60fps:
|
|
112
|
+
# - Standing (stationary): ~0.001 mean, 0.0011 max
|
|
113
|
+
# - Flight/drop (moving): ~0.005-0.009
|
|
114
|
+
# Target threshold: 0.002 at 60fps for clear separation
|
|
115
|
+
# Formula: threshold = 0.004 * (30 / fps)
|
|
116
|
+
base_velocity_threshold = 0.004 * (30.0 / fps)
|
|
115
117
|
|
|
116
118
|
# Min contact frames: Scale with fps to maintain same time duration
|
|
117
119
|
# Goal: ~100ms minimum contact (3 frames @ 30fps, 6 frames @ 60fps)
|
|
@@ -57,6 +57,7 @@ class DropJumpMetrics:
|
|
|
57
57
|
self.jump_height: float | None = None
|
|
58
58
|
self.jump_height_kinematic: float | None = None # From flight time
|
|
59
59
|
self.jump_height_trajectory: float | None = None # From position tracking
|
|
60
|
+
self.drop_start_frame: int | None = None # Frame when athlete leaves box
|
|
60
61
|
self.contact_start_frame: int | None = None
|
|
61
62
|
self.contact_end_frame: int | None = None
|
|
62
63
|
self.flight_start_frame: int | None = None
|
|
@@ -164,7 +165,7 @@ def _determine_drop_start_frame(
|
|
|
164
165
|
foot_y_positions,
|
|
165
166
|
fps,
|
|
166
167
|
min_stationary_duration=0.5,
|
|
167
|
-
position_change_threshold=0.005
|
|
168
|
+
position_change_threshold=0.01, # Improved from 0.005 for better accuracy
|
|
168
169
|
smoothing_window=smoothing_window,
|
|
169
170
|
)
|
|
170
171
|
return drop_start_frame
|
|
@@ -412,6 +413,11 @@ def calculate_drop_jump_metrics(
|
|
|
412
413
|
drop_start_frame, foot_y_positions, fps, smoothing_window
|
|
413
414
|
)
|
|
414
415
|
|
|
416
|
+
# Store drop start frame in metrics
|
|
417
|
+
metrics.drop_start_frame = (
|
|
418
|
+
drop_start_frame_value if drop_start_frame_value > 0 else None
|
|
419
|
+
)
|
|
420
|
+
|
|
415
421
|
# Find contact phases
|
|
416
422
|
phases = find_contact_phases(contact_states)
|
|
417
423
|
interpolated_phases = find_interpolated_phase_transitions_with_curvature(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kinemotion
|
|
3
|
-
Version: 0.35.
|
|
3
|
+
Version: 0.35.2
|
|
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
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
kinemotion/__init__.py,sha256=sxdDOekOrIgjxm842gy-6zfq7OWmGl9ShJtXCm4JI7c,723
|
|
2
|
-
kinemotion/api.py,sha256=
|
|
2
|
+
kinemotion/api.py,sha256=nbDbyzhXIMA04tGqKPH8R0fR66zgtu14x6NgWroy_QU,39471
|
|
3
3
|
kinemotion/cli.py,sha256=cqYV_7URH0JUDy1VQ_EDLv63FmNO4Ns20m6s1XAjiP4,464
|
|
4
4
|
kinemotion/cmj/__init__.py,sha256=Ynv0-Oco4I3Y1Ubj25m3h9h2XFqeNwpAewXmAYOmwfU,127
|
|
5
|
-
kinemotion/cmj/analysis.py,sha256=
|
|
5
|
+
kinemotion/cmj/analysis.py,sha256=OfNTMLPwZIRYbX-Yd8jgZ-7pqnHRz7L2bWAHVYFsQ60,18955
|
|
6
6
|
kinemotion/cmj/cli.py,sha256=Mj2h9It1jVjAauvtCxfLWTRijj7zbYhxZuebhw2Zz6w,10828
|
|
7
7
|
kinemotion/cmj/debug_overlay.py,sha256=fXmWoHhqMLGo4vTtB6Ezs3yLUDOLw63zLIgU2gFlJQU,15892
|
|
8
8
|
kinemotion/cmj/joint_angles.py,sha256=HmheIEiKcQz39cRezk4h-htorOhGNPsqKIR9RsAEKts,9960
|
|
9
9
|
kinemotion/cmj/kinematics.py,sha256=qRBe87NkX-7HQTQ8RoF-EpvfcffgP5vycJJRrxpHboc,10307
|
|
10
|
-
kinemotion/cmj/metrics_validator.py,sha256=
|
|
11
|
-
kinemotion/cmj/validation_bounds.py,sha256=
|
|
10
|
+
kinemotion/cmj/metrics_validator.py,sha256=V_fmlczYH06SBtwqESv-IfGi3wDsIy3RQbd7VwOyNo0,31359
|
|
11
|
+
kinemotion/cmj/validation_bounds.py,sha256=9ZTo68fl3ooyWjXXyTMRLpK9tFANa_rQf3oHhq7iQGE,11995
|
|
12
12
|
kinemotion/core/__init__.py,sha256=HsqolRa60cW3vrG8F9Lvr9WvWcs5hCmsTzSgo7imi-4,1278
|
|
13
|
-
kinemotion/core/auto_tuning.py,sha256=
|
|
13
|
+
kinemotion/core/auto_tuning.py,sha256=wtCUMOhBChVJNXfEeku3GCMW4qED6MF-O_mv2sPTiVQ,11324
|
|
14
14
|
kinemotion/core/cli_utils.py,sha256=zbnifPhD-OYofJioeYfJtshuWcl8OAEWtqCGVF4ctAI,7966
|
|
15
15
|
kinemotion/core/debug_overlay_utils.py,sha256=TyUb5okv5qw8oeaX3jsUO_kpwf1NnaHEAOTm-8LwTno,4587
|
|
16
16
|
kinemotion/core/experimental.py,sha256=IK05AF4aZS15ke85hF3TWCqRIXU1AlD_XKzFz735Ua8,3640
|
|
@@ -26,12 +26,12 @@ kinemotion/dropjump/__init__.py,sha256=tC3H3BrCg8Oj-db-Vrtx4PH_llR1Ppkd5jwaOjhQc
|
|
|
26
26
|
kinemotion/dropjump/analysis.py,sha256=B_N_51WoChyQ8I7yaeKeqj3vw7NufgV_3QL-FBZEtW4,28752
|
|
27
27
|
kinemotion/dropjump/cli.py,sha256=n_Wfv3AC6YIgRPYhO3F2nTSai0NR7fh95nAoWjryQeY,16250
|
|
28
28
|
kinemotion/dropjump/debug_overlay.py,sha256=LkPw6ucb7beoYWS4L-Lvjs1KLCm5wAWDAfiznUeV2IQ,5668
|
|
29
|
-
kinemotion/dropjump/kinematics.py,sha256=
|
|
29
|
+
kinemotion/dropjump/kinematics.py,sha256=yB4ws4VG59SUGcw1J-uXfDFfCMXBdzRh5C4jo0osXbs,17404
|
|
30
30
|
kinemotion/dropjump/metrics_validator.py,sha256=sx4RodHpeiW8_PRB0GUJvkUWto1Ard1Dvrc9z8eKk7M,9351
|
|
31
31
|
kinemotion/dropjump/validation_bounds.py,sha256=5b4I3CKPybuvrbn-nP5yCcGF_sH4Vtyw3a5AWWvWnBk,4645
|
|
32
32
|
kinemotion/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
-
kinemotion-0.35.
|
|
34
|
-
kinemotion-0.35.
|
|
35
|
-
kinemotion-0.35.
|
|
36
|
-
kinemotion-0.35.
|
|
37
|
-
kinemotion-0.35.
|
|
33
|
+
kinemotion-0.35.2.dist-info/METADATA,sha256=tb4l8YTLu_HqhK7tH5lwKLmQkBXvFDqhIyPPKP6qHgU,26020
|
|
34
|
+
kinemotion-0.35.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
35
|
+
kinemotion-0.35.2.dist-info/entry_points.txt,sha256=zaqnAnjLvcdrk1Qvj5nvXZCZ2gp0prS7it1zTJygcIY,50
|
|
36
|
+
kinemotion-0.35.2.dist-info/licenses/LICENSE,sha256=KZajvqsHw0NoOHOi2q0FZ4NBe9HdV6oey-IPYAtHXfg,1088
|
|
37
|
+
kinemotion-0.35.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|