kinemotion 0.12.0__tar.gz → 0.12.1__tar.gz
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-0.12.0 → kinemotion-0.12.1}/CHANGELOG.md +16 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/PKG-INFO +1 -1
- {kinemotion-0.12.0 → kinemotion-0.12.1}/examples/bulk/bulk_processing.py +3 -10
- {kinemotion-0.12.0 → kinemotion-0.12.1}/examples/bulk/simple_example.py +5 -7
- {kinemotion-0.12.0 → kinemotion-0.12.1}/pyproject.toml +1 -1
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/core/cli_utils.py +0 -2
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/dropjump/kinematics.py +0 -63
- {kinemotion-0.12.0 → kinemotion-0.12.1}/uv.lock +1 -1
- {kinemotion-0.12.0 → kinemotion-0.12.1}/.dockerignore +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/.github/pull_request_template.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/.github/workflows/release.yml +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/.gitignore +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/.pre-commit-config.yaml +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/.tool-versions +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/CLAUDE.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/CODE_OF_CONDUCT.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/CONTRIBUTING.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/Dockerfile +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/GEMINI.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/LICENSE +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/README.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/SECURITY.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/BULK_PROCESSING.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/CAMERA_SETUP.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/CAMERA_SETUP_ES.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/CMJ_GUIDE.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/ERRORS_FINDINGS.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/FRAMERATE.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/IMU_METADATA_PRESERVATION.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/PARAMETERS.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/REAL_TIME_ANALYSIS.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/TRIPLE_EXTENSION.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/docs/VALIDATION_PLAN.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/examples/bulk/README.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/examples/programmatic_usage.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/samples/cmjs/README.md +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/__init__.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/api.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/cli.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/cmj/__init__.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/cmj/analysis.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/cmj/cli.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/cmj/debug_overlay.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/cmj/joint_angles.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/cmj/kinematics.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/core/__init__.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/core/auto_tuning.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/core/debug_overlay_utils.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/core/filtering.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/core/pose.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/core/smoothing.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/core/video_io.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/dropjump/__init__.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/dropjump/analysis.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/dropjump/cli.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/dropjump/debug_overlay.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/src/kinemotion/py.typed +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/__init__.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/test_adaptive_threshold.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/test_api.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/test_aspect_ratio.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/test_cmj_analysis.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/test_cmj_kinematics.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/test_com_estimation.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/test_contact_detection.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/test_filtering.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/test_kinematics.py +0 -0
- {kinemotion-0.12.0 → kinemotion-0.12.1}/tests/test_polyorder.py +0 -0
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
<!-- version list -->
|
|
9
9
|
|
|
10
|
+
## v0.12.1 (2025-11-06)
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- **core**: Remove unreachable duplicate return statement
|
|
15
|
+
([`294115d`](https://github.com/feniix/kinemotion/commit/294115da761b2851ecc4405a6503138851a56ad1))
|
|
16
|
+
|
|
17
|
+
- **examples**: Remove drop_height from API examples
|
|
18
|
+
([`f3da09e`](https://github.com/feniix/kinemotion/commit/f3da09ef4ab050b13b80b9fdd8c7734e4556647a))
|
|
19
|
+
|
|
20
|
+
### Refactoring
|
|
21
|
+
|
|
22
|
+
- **dropjump**: Remove unused calibration parameters
|
|
23
|
+
([`1a7572c`](https://github.com/feniix/kinemotion/commit/1a7572c83ff4e990e39dcb96ff61220adf40818e))
|
|
24
|
+
|
|
25
|
+
|
|
10
26
|
## v0.12.0 (2025-11-06)
|
|
11
27
|
|
|
12
28
|
### Documentation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kinemotion
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.1
|
|
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
|
|
@@ -18,9 +18,9 @@ def example_simple_bulk() -> None:
|
|
|
18
18
|
print("=" * 80)
|
|
19
19
|
|
|
20
20
|
video_configs = [
|
|
21
|
-
VideoConfig(video_path="video1.mp4"
|
|
22
|
-
VideoConfig(video_path="video2.mp4"
|
|
23
|
-
VideoConfig(video_path="video3.mp4"
|
|
21
|
+
VideoConfig(video_path="video1.mp4"),
|
|
22
|
+
VideoConfig(video_path="video2.mp4"),
|
|
23
|
+
VideoConfig(video_path="video3.mp4"),
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
# Process videos with 4 parallel workers
|
|
@@ -41,21 +41,18 @@ def example_advanced_configuration() -> None:
|
|
|
41
41
|
# Fast analysis for quick screening
|
|
42
42
|
VideoConfig(
|
|
43
43
|
video_path="athlete1_trial1.mp4",
|
|
44
|
-
drop_height=0.40,
|
|
45
44
|
quality="fast",
|
|
46
45
|
json_output="results/athlete1_trial1.json",
|
|
47
46
|
),
|
|
48
47
|
# Balanced analysis (default)
|
|
49
48
|
VideoConfig(
|
|
50
49
|
video_path="athlete1_trial2.mp4",
|
|
51
|
-
drop_height=0.40,
|
|
52
50
|
quality="balanced",
|
|
53
51
|
json_output="results/athlete1_trial2.json",
|
|
54
52
|
),
|
|
55
53
|
# Research-grade accurate analysis with debug video
|
|
56
54
|
VideoConfig(
|
|
57
55
|
video_path="athlete1_trial3.mp4",
|
|
58
|
-
drop_height=0.40,
|
|
59
56
|
quality="accurate",
|
|
60
57
|
output_video="debug/athlete1_trial3_debug.mp4",
|
|
61
58
|
json_output="results/athlete1_trial3.json",
|
|
@@ -101,7 +98,6 @@ def example_process_directory() -> list[VideoResult]:
|
|
|
101
98
|
dir_configs = [
|
|
102
99
|
VideoConfig(
|
|
103
100
|
video_path=str(video_file),
|
|
104
|
-
drop_height=0.40,
|
|
105
101
|
quality="balanced",
|
|
106
102
|
json_output=f"results/{video_file.stem}.json",
|
|
107
103
|
)
|
|
@@ -187,7 +183,6 @@ def example_custom_parameters() -> None:
|
|
|
187
183
|
# Low quality video - use more aggressive smoothing
|
|
188
184
|
VideoConfig(
|
|
189
185
|
video_path="low_quality.mp4",
|
|
190
|
-
drop_height=0.40,
|
|
191
186
|
smoothing_window=7, # More smoothing
|
|
192
187
|
velocity_threshold=0.025, # Higher threshold
|
|
193
188
|
quality="accurate",
|
|
@@ -195,7 +190,6 @@ def example_custom_parameters() -> None:
|
|
|
195
190
|
# High speed video - adjust for higher framerate
|
|
196
191
|
VideoConfig(
|
|
197
192
|
video_path="high_speed_120fps.mp4",
|
|
198
|
-
drop_height=0.40,
|
|
199
193
|
quality="accurate",
|
|
200
194
|
# Auto-tuning will handle FPS adjustments
|
|
201
195
|
),
|
|
@@ -290,7 +284,6 @@ def example_single_video() -> None:
|
|
|
290
284
|
# Process single video with verbose output
|
|
291
285
|
metrics = process_video(
|
|
292
286
|
video_path="sample.mp4",
|
|
293
|
-
drop_height=0.40,
|
|
294
287
|
quality="balanced",
|
|
295
288
|
output_video="sample_debug.mp4",
|
|
296
289
|
json_output="sample_results.json",
|
|
@@ -12,10 +12,9 @@ def process_single_video_example() -> None:
|
|
|
12
12
|
"""Process a single video - the simplest usage."""
|
|
13
13
|
print("Processing single video...")
|
|
14
14
|
|
|
15
|
-
# Process with just the
|
|
15
|
+
# Process with just the video path
|
|
16
16
|
metrics = process_video(
|
|
17
17
|
video_path="my_video.mp4",
|
|
18
|
-
drop_height=0.40, # 40cm drop box
|
|
19
18
|
verbose=True,
|
|
20
19
|
)
|
|
21
20
|
|
|
@@ -36,10 +35,10 @@ def process_multiple_videos_example() -> None:
|
|
|
36
35
|
|
|
37
36
|
# Configure videos to process
|
|
38
37
|
configs = [
|
|
39
|
-
VideoConfig("athlete1_jump1.mp4"
|
|
40
|
-
VideoConfig("athlete1_jump2.mp4"
|
|
41
|
-
VideoConfig("athlete1_jump3.mp4"
|
|
42
|
-
VideoConfig("athlete2_jump1.mp4",
|
|
38
|
+
VideoConfig("athlete1_jump1.mp4"),
|
|
39
|
+
VideoConfig("athlete1_jump2.mp4"),
|
|
40
|
+
VideoConfig("athlete1_jump3.mp4"),
|
|
41
|
+
VideoConfig("athlete2_jump1.mp4", quality="accurate"),
|
|
43
42
|
]
|
|
44
43
|
|
|
45
44
|
# Process all videos using 4 parallel workers
|
|
@@ -74,7 +73,6 @@ def process_with_outputs_example() -> None:
|
|
|
74
73
|
|
|
75
74
|
metrics = process_video(
|
|
76
75
|
video_path="my_video.mp4",
|
|
77
|
-
drop_height=0.40,
|
|
78
76
|
output_video="debug_output.mp4", # Save annotated video
|
|
79
77
|
json_output="results.json", # Save metrics as JSON
|
|
80
78
|
quality="accurate", # Use highest quality analysis
|
|
@@ -255,55 +255,6 @@ def _find_precise_phase_timing(
|
|
|
255
255
|
return contact_start_frac, contact_end_frac
|
|
256
256
|
|
|
257
257
|
|
|
258
|
-
def _calculate_calibration_scale(
|
|
259
|
-
drop_height_m: float | None,
|
|
260
|
-
phases: list[tuple[int, int, ContactState]],
|
|
261
|
-
air_phases_indexed: list[tuple[int, int, int]],
|
|
262
|
-
foot_y_positions: np.ndarray,
|
|
263
|
-
) -> float:
|
|
264
|
-
"""Calculate calibration scale factor from known drop height.
|
|
265
|
-
|
|
266
|
-
Args:
|
|
267
|
-
drop_height_m: Known drop height in meters
|
|
268
|
-
phases: All phase tuples
|
|
269
|
-
air_phases_indexed: Air phases with indices
|
|
270
|
-
foot_y_positions: Vertical position array
|
|
271
|
-
|
|
272
|
-
Returns:
|
|
273
|
-
Scale factor (1.0 if no calibration possible)
|
|
274
|
-
"""
|
|
275
|
-
scale_factor = 1.0
|
|
276
|
-
|
|
277
|
-
if drop_height_m is None or len(phases) < 2:
|
|
278
|
-
return scale_factor
|
|
279
|
-
|
|
280
|
-
if not air_phases_indexed:
|
|
281
|
-
return scale_factor
|
|
282
|
-
|
|
283
|
-
# Get first air phase (the drop)
|
|
284
|
-
first_air_start, first_air_end, _ = air_phases_indexed[0]
|
|
285
|
-
|
|
286
|
-
# Initial position: at start of drop (on the box)
|
|
287
|
-
lookback_start = max(0, first_air_start - 5)
|
|
288
|
-
if lookback_start < first_air_start:
|
|
289
|
-
initial_position = float(
|
|
290
|
-
np.mean(foot_y_positions[lookback_start:first_air_start])
|
|
291
|
-
)
|
|
292
|
-
else:
|
|
293
|
-
initial_position = float(foot_y_positions[first_air_start])
|
|
294
|
-
|
|
295
|
-
# Landing position: at the ground after drop
|
|
296
|
-
landing_position = float(foot_y_positions[first_air_end])
|
|
297
|
-
|
|
298
|
-
# Drop distance in normalized coordinates (y increases downward)
|
|
299
|
-
drop_normalized = landing_position - initial_position
|
|
300
|
-
|
|
301
|
-
if drop_normalized > 0.01: # Sanity check
|
|
302
|
-
scale_factor = drop_height_m / drop_normalized
|
|
303
|
-
|
|
304
|
-
return scale_factor
|
|
305
|
-
|
|
306
|
-
|
|
307
258
|
def _analyze_flight_phase(
|
|
308
259
|
metrics: DropJumpMetrics,
|
|
309
260
|
phases: list[tuple[int, int, ContactState]],
|
|
@@ -311,9 +262,6 @@ def _analyze_flight_phase(
|
|
|
311
262
|
contact_end: int,
|
|
312
263
|
foot_y_positions: np.ndarray,
|
|
313
264
|
fps: float,
|
|
314
|
-
drop_height_m: float | None,
|
|
315
|
-
scale_factor: float,
|
|
316
|
-
kinematic_correction_factor: float,
|
|
317
265
|
smoothing_window: int,
|
|
318
266
|
polyorder: int,
|
|
319
267
|
) -> None:
|
|
@@ -329,9 +277,6 @@ def _analyze_flight_phase(
|
|
|
329
277
|
contact_end: End of contact phase
|
|
330
278
|
foot_y_positions: Vertical position array
|
|
331
279
|
fps: Video frame rate
|
|
332
|
-
drop_height_m: Known drop height (optional, for RSI calculation)
|
|
333
|
-
scale_factor: Calibration scale factor
|
|
334
|
-
kinematic_correction_factor: Correction for kinematic method
|
|
335
280
|
smoothing_window: Window size for acceleration computation
|
|
336
281
|
polyorder: Polynomial order for Savitzky-Golay filter
|
|
337
282
|
"""
|
|
@@ -489,11 +434,6 @@ def calculate_drop_jump_metrics(
|
|
|
489
434
|
metrics.contact_start_frame_precise = contact_start_frac
|
|
490
435
|
metrics.contact_end_frame_precise = contact_end_frac
|
|
491
436
|
|
|
492
|
-
# Calculate calibration scale factor
|
|
493
|
-
scale_factor = _calculate_calibration_scale(
|
|
494
|
-
drop_height_m, phases, air_phases_indexed, foot_y_positions
|
|
495
|
-
)
|
|
496
|
-
|
|
497
437
|
# Analyze flight phase and calculate jump height
|
|
498
438
|
_analyze_flight_phase(
|
|
499
439
|
metrics,
|
|
@@ -502,9 +442,6 @@ def calculate_drop_jump_metrics(
|
|
|
502
442
|
contact_end,
|
|
503
443
|
foot_y_positions,
|
|
504
444
|
fps,
|
|
505
|
-
drop_height_m,
|
|
506
|
-
scale_factor,
|
|
507
|
-
kinematic_correction_factor,
|
|
508
445
|
smoothing_window,
|
|
509
446
|
polyorder,
|
|
510
447
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|