kinemotion 0.11.7__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.

Files changed (71) hide show
  1. {kinemotion-0.11.7 → kinemotion-0.12.1}/.pre-commit-config.yaml +1 -1
  2. {kinemotion-0.11.7 → kinemotion-0.12.1}/CHANGELOG.md +45 -0
  3. {kinemotion-0.11.7 → kinemotion-0.12.1}/CLAUDE.md +2 -0
  4. {kinemotion-0.11.7 → kinemotion-0.12.1}/PKG-INFO +1 -1
  5. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/CAMERA_SETUP.md +1 -1
  6. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/CAMERA_SETUP_ES.md +1 -1
  7. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/CMJ_GUIDE.md +6 -6
  8. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/REAL_TIME_ANALYSIS.md +4 -4
  9. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/TRIPLE_EXTENSION.md +10 -10
  10. {kinemotion-0.11.7 → kinemotion-0.12.1}/examples/bulk/bulk_processing.py +3 -10
  11. {kinemotion-0.11.7 → kinemotion-0.12.1}/examples/bulk/simple_example.py +5 -7
  12. {kinemotion-0.11.7 → kinemotion-0.12.1}/pyproject.toml +1 -1
  13. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/api.py +6 -11
  14. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/cmj/debug_overlay.py +9 -12
  15. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/core/cli_utils.py +7 -101
  16. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/core/debug_overlay_utils.py +1 -24
  17. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/core/video_io.py +1 -5
  18. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/dropjump/analysis.py +69 -0
  19. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/dropjump/cli.py +5 -26
  20. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/dropjump/kinematics.py +34 -136
  21. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/test_api.py +15 -21
  22. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/test_kinematics.py +7 -5
  23. {kinemotion-0.11.7 → kinemotion-0.12.1}/uv.lock +1 -1
  24. {kinemotion-0.11.7 → kinemotion-0.12.1}/.dockerignore +0 -0
  25. {kinemotion-0.11.7 → kinemotion-0.12.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  26. {kinemotion-0.11.7 → kinemotion-0.12.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  27. {kinemotion-0.11.7 → kinemotion-0.12.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  28. {kinemotion-0.11.7 → kinemotion-0.12.1}/.github/pull_request_template.md +0 -0
  29. {kinemotion-0.11.7 → kinemotion-0.12.1}/.github/workflows/release.yml +0 -0
  30. {kinemotion-0.11.7 → kinemotion-0.12.1}/.gitignore +0 -0
  31. {kinemotion-0.11.7 → kinemotion-0.12.1}/.tool-versions +0 -0
  32. {kinemotion-0.11.7 → kinemotion-0.12.1}/CODE_OF_CONDUCT.md +0 -0
  33. {kinemotion-0.11.7 → kinemotion-0.12.1}/CONTRIBUTING.md +0 -0
  34. {kinemotion-0.11.7 → kinemotion-0.12.1}/Dockerfile +0 -0
  35. {kinemotion-0.11.7 → kinemotion-0.12.1}/GEMINI.md +0 -0
  36. {kinemotion-0.11.7 → kinemotion-0.12.1}/LICENSE +0 -0
  37. {kinemotion-0.11.7 → kinemotion-0.12.1}/README.md +0 -0
  38. {kinemotion-0.11.7 → kinemotion-0.12.1}/SECURITY.md +0 -0
  39. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/BULK_PROCESSING.md +0 -0
  40. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/ERRORS_FINDINGS.md +0 -0
  41. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/FRAMERATE.md +0 -0
  42. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/IMU_METADATA_PRESERVATION.md +0 -0
  43. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/PARAMETERS.md +0 -0
  44. {kinemotion-0.11.7 → kinemotion-0.12.1}/docs/VALIDATION_PLAN.md +0 -0
  45. {kinemotion-0.11.7 → kinemotion-0.12.1}/examples/bulk/README.md +0 -0
  46. {kinemotion-0.11.7 → kinemotion-0.12.1}/examples/programmatic_usage.py +0 -0
  47. {kinemotion-0.11.7 → kinemotion-0.12.1}/samples/cmjs/README.md +0 -0
  48. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/__init__.py +0 -0
  49. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/cli.py +0 -0
  50. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/cmj/__init__.py +0 -0
  51. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/cmj/analysis.py +0 -0
  52. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/cmj/cli.py +0 -0
  53. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/cmj/joint_angles.py +0 -0
  54. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/cmj/kinematics.py +0 -0
  55. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/core/__init__.py +0 -0
  56. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/core/auto_tuning.py +0 -0
  57. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/core/filtering.py +0 -0
  58. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/core/pose.py +0 -0
  59. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/core/smoothing.py +0 -0
  60. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/dropjump/__init__.py +0 -0
  61. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/dropjump/debug_overlay.py +0 -0
  62. {kinemotion-0.11.7 → kinemotion-0.12.1}/src/kinemotion/py.typed +0 -0
  63. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/__init__.py +0 -0
  64. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/test_adaptive_threshold.py +0 -0
  65. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/test_aspect_ratio.py +0 -0
  66. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/test_cmj_analysis.py +0 -0
  67. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/test_cmj_kinematics.py +0 -0
  68. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/test_com_estimation.py +0 -0
  69. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/test_contact_detection.py +0 -0
  70. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/test_filtering.py +0 -0
  71. {kinemotion-0.11.7 → kinemotion-0.12.1}/tests/test_polyorder.py +0 -0
@@ -39,7 +39,7 @@ repos:
39
39
  additional_dependencies:
40
40
  - mdformat-gfm>=0.3.5 # GitHub Flavored Markdown
41
41
  - mdformat-tables # Table formatting
42
- exclude: ^CLAUDE\.md$
42
+ exclude: (^CLAUDE\.md$|^CHANGELOG\.md$)
43
43
 
44
44
  - repo: https://github.com/compilerla/conventional-pre-commit
45
45
  rev: v4.3.0
@@ -7,6 +7,51 @@ 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
+
26
+ ## v0.12.0 (2025-11-06)
27
+
28
+ ### Documentation
29
+
30
+ - Update claude.md
31
+ ([`b4d93d9`](https://github.com/feniix/kinemotion/commit/b4d93d94259fbfe86101c256910fcfc07c8dfcc2))
32
+
33
+ ### Features
34
+
35
+ - **dropjump**: Calculate jump height from flight time like CMJ
36
+ ([`f7d96a2`](https://github.com/feniix/kinemotion/commit/f7d96a253b287d58215fd64bd1e598784cb098f4))
37
+
38
+ - **dropjump**: Improve landing detection with position stabilization
39
+ ([`6d19938`](https://github.com/feniix/kinemotion/commit/6d199382485a80a975911c51444b2c18aa32c428))
40
+
41
+ ### Refactoring
42
+
43
+ - **core**: Remove unused code and fix vulture warnings
44
+ ([`16328e2`](https://github.com/feniix/kinemotion/commit/16328e299a0e15f7f0f0e87d133e1f662dc59d0b))
45
+
46
+ - **core**: Rename AutoTunedParams to AnalysisParameters for consistency
47
+ ([`2b6e59b`](https://github.com/feniix/kinemotion/commit/2b6e59b832769224b600e23bf4141af5d6159169))
48
+
49
+ ### Testing
50
+
51
+ - Update tests for kinematic-based height calculation
52
+ ([`308469e`](https://github.com/feniix/kinemotion/commit/308469e978c53a971a4a20352cfffd72a3c9e6cd))
53
+
54
+
10
55
  ## v0.11.7 (2025-11-06)
11
56
 
12
57
  ### Bug Fixes
@@ -254,6 +254,8 @@ chore(release): 0.11.0 [skip ci]
254
254
  feat!: change API signature for process_video
255
255
  ```
256
256
 
257
+ **Important**: Commit messages must never reference Claude or AI assistance. Keep messages professional and focused on the technical changes.
258
+
257
259
  ## MCP Servers
258
260
 
259
261
  Configured in `.mcp.json`: web-search, sequential-thinking, context7, etc.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kinemotion
3
- Version: 0.11.7
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
@@ -12,7 +12,7 @@ Proper camera positioning is critical for accurate drop jump analysis. The curre
12
12
 
13
13
  ### Required Camera Position
14
14
 
15
- **Camera must be positioned at a side view angle, perpendicular to the sagittal plane (90°)**
15
+ Camera must be positioned at a side view angle, perpendicular to the sagittal plane (90°).
16
16
 
17
17
  #### Camera Positioning Diagram
18
18
 
@@ -12,7 +12,7 @@ El posicionamiento adecuado de la cámara es crítico para un análisis preciso
12
12
 
13
13
  ### Posición Requerida de la Cámara
14
14
 
15
- **La cámara debe posicionarse en ángulo de vista lateral, perpendicular al plano sagital (90°)**
15
+ La cámara debe posicionarse en ángulo de vista lateral, perpendicular al plano sagital (90°).
16
16
 
17
17
  #### Diagrama de Posicionamiento de Cámara
18
18
 
@@ -61,7 +61,7 @@ print(f"Eccentric duration: {metrics.eccentric_duration*1000:.0f}ms")
61
61
 
62
62
  ### Movement Characteristics
63
63
 
64
- 3. **Countermovement Depth** (m) - Vertical distance during eccentric phase
64
+ 1. **Countermovement Depth** (m) - Vertical distance during eccentric phase
65
65
 
66
66
  - Represents how deep the athlete squats
67
67
  - Typical range: 0.20-0.40m
@@ -90,7 +90,7 @@ print(f"Eccentric duration: {metrics.eccentric_duration*1000:.0f}ms")
90
90
 
91
91
  ### Velocity Profile
92
92
 
93
- 8. **Peak Eccentric Velocity** (m/s) - Maximum downward speed
93
+ 1. **Peak Eccentric Velocity** (m/s) - Maximum downward speed
94
94
 
95
95
  - Indicates countermovement speed
96
96
  - Typical range: 0.5-1.5 m/s
@@ -102,10 +102,10 @@ print(f"Eccentric duration: {metrics.eccentric_duration*1000:.0f}ms")
102
102
 
103
103
  ### Triple Extension (in debug video)
104
104
 
105
- 10. **Ankle Angle** - Dorsiflexion/plantarflexion
106
- 01. **Knee Angle** - Flexion/extension
107
- 01. **Hip Angle** - Flexion/extension
108
- 01. **Trunk Tilt** - Forward/backward lean
105
+ 1. **Ankle Angle** - Dorsiflexion/plantarflexion
106
+ 1. **Knee Angle** - Flexion/extension
107
+ 1. **Hip Angle** - Flexion/extension
108
+ 1. **Trunk Tilt** - Forward/backward lean
109
109
 
110
110
  **Note**: Ankle/knee angles have limited visibility in lateral view videos (~20-30% of frames). Trunk angle is available throughout. See docs/TRIPLE_EXTENSION.md for details.
111
111
 
@@ -244,7 +244,7 @@ ______________________________________________________________________
244
244
 
245
245
  **Test video**: 236 frames @ 29.58fps
246
246
 
247
- ```
247
+ ```text
248
248
  Processing time breakdown:
249
249
  - MediaPipe tracking: ~5-6 seconds
250
250
  - Smoothing: ~0.1 seconds
@@ -681,7 +681,7 @@ ______________________________________________________________________
681
681
 
682
682
  ## Recommendation Matrix
683
683
 
684
- ### Choose Offline (Current) If:
684
+ ### Choose Offline (Current) If
685
685
 
686
686
  - ✅ Maximum accuracy required (research, validation)
687
687
  - ✅ Processing pre-recorded videos
@@ -689,7 +689,7 @@ ______________________________________________________________________
689
689
  - ✅ Want triple extension with full coverage
690
690
  - ✅ Publication-quality data needed
691
691
 
692
- ### Choose Near Real-Time If:
692
+ ### Choose Near Real-Time If
693
693
 
694
694
  - ✅ Need quick results (1-2 sec acceptable)
695
695
  - ✅ Coaching/training applications
@@ -697,7 +697,7 @@ ______________________________________________________________________
697
697
  - ✅ Want to maintain accuracy
698
698
  - ✅ Building mobile/web app
699
699
 
700
- ### Choose True Real-Time If:
700
+ ### Choose True Real-Time If
701
701
 
702
702
  - ⚠️ Instant feedback critical (\<100ms)
703
703
  - ⚠️ Interactive applications (games, VR)
@@ -49,7 +49,7 @@ The CMJ debug video now includes **triple extension tracking** - real-time visua
49
49
 
50
50
  **At Lowest Point (Countermovement Bottom):**
51
51
 
52
- ```
52
+ ```text
53
53
  Ankle: 70-90° (neutral to slight dorsiflexion)
54
54
  Knee: 90-110° (moderate squat)
55
55
  Hip: 90-110° (hip flexion)
@@ -58,7 +58,7 @@ Trunk: 0-20° (slight forward lean)
58
58
 
59
59
  **At Takeoff (Leaving Ground):**
60
60
 
61
- ```
61
+ ```text
62
62
  Ankle: 110-130° (strong plantarflexion)
63
63
  Knee: 160-180° (near full extension)
64
64
  Hip: 170-180° (full extension)
@@ -67,7 +67,7 @@ Trunk: 0-10° (nearly vertical)
67
67
 
68
68
  **During Flight:**
69
69
 
70
- ```
70
+ ```text
71
71
  All joints: ~180° (full extension)
72
72
  ```
73
73
 
@@ -139,23 +139,23 @@ All joints: ~180° (full extension)
139
139
 
140
140
  ### Poor Extension Patterns
141
141
 
142
- **Problem 1: Incomplete knee extension**
142
+ #### Problem 1: Incomplete knee extension
143
143
 
144
- ```
144
+ ```text
145
145
  Takeoff: Ankle 120°, Knee 150°, Hip 175°
146
146
  → Power leak: Not fully utilizing leg strength
147
147
  ```
148
148
 
149
- **Problem 2: Sequential extension (not simultaneous)**
149
+ #### Problem 2: Sequential extension (not simultaneous)
150
150
 
151
- ```
151
+ ```text
152
152
  Early concentric: Hip 170°, Knee 120°, Ankle 80°
153
153
  → Poor coordination: Extending in sequence instead of together
154
154
  ```
155
155
 
156
- **Problem 3: Excessive trunk lean**
156
+ #### Problem 3: Excessive trunk lean
157
157
 
158
- ```
158
+ ```text
159
159
  Takeoff: Trunk 30° forward
160
160
  → Sub-optimal: Reduces vertical force component
161
161
  ```
@@ -272,7 +272,7 @@ The triple extension feature has been tested with:
272
272
 
273
273
  **Debug video shows:**
274
274
 
275
- ```
275
+ ```text
276
276
  Frame 140-155 (Concentric phase):
277
277
  ┌─────────────────────┐
278
278
  │ TRIPLE EXTENSION │
@@ -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", drop_height=0.40),
22
- VideoConfig(video_path="video2.mp4", drop_height=0.30),
23
- VideoConfig(video_path="video3.mp4", drop_height=0.50),
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 required parameters
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", drop_height=0.40),
40
- VideoConfig("athlete1_jump2.mp4", drop_height=0.40),
41
- VideoConfig("athlete1_jump3.mp4", drop_height=0.40),
42
- VideoConfig("athlete2_jump1.mp4", drop_height=0.30), # Different drop height
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kinemotion"
3
- version = "0.11.7"
3
+ version = "0.12.1"
4
4
  description = "Video-based kinematic analysis for athletic performance"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10,<3.13"
@@ -337,7 +337,6 @@ class VideoConfig:
337
337
  """Configuration for processing a single video."""
338
338
 
339
339
  video_path: str
340
- drop_height: float
341
340
  quality: str = "balanced"
342
341
  output_video: str | None = None
343
342
  json_output: str | None = None
@@ -352,7 +351,6 @@ class VideoConfig:
352
351
 
353
352
  def process_video(
354
353
  video_path: str,
355
- drop_height: float,
356
354
  quality: str = "balanced",
357
355
  output_video: str | None = None,
358
356
  json_output: str | None = None,
@@ -368,9 +366,10 @@ def process_video(
368
366
  """
369
367
  Process a single drop jump video and return metrics.
370
368
 
369
+ Jump height is calculated from flight time using kinematic formula (h = g*t²/8).
370
+
371
371
  Args:
372
372
  video_path: Path to the input video file
373
- drop_height: Height of drop box/platform in meters (e.g., 0.40 for 40cm)
374
373
  quality: Analysis quality preset ("fast", "balanced", or "accurate")
375
374
  output_video: Optional path for debug video output
376
375
  json_output: Optional path for JSON metrics output
@@ -459,15 +458,12 @@ def process_video(
459
458
  # Calculate metrics
460
459
  if verbose:
461
460
  print("Calculating metrics...")
462
- print(
463
- f"Using drop height calibration: {drop_height}m ({drop_height*100:.0f}cm)"
464
- )
465
461
 
466
462
  metrics = calculate_drop_jump_metrics(
467
463
  contact_states,
468
464
  vertical_positions,
469
465
  video.fps,
470
- drop_height_m=drop_height,
466
+ drop_height_m=None,
471
467
  drop_start_frame=drop_start_frame,
472
468
  velocity_threshold=params.velocity_threshold,
473
469
  smoothing_window=params.smoothing_window,
@@ -513,9 +509,9 @@ def process_videos_bulk(
513
509
 
514
510
  Example:
515
511
  >>> configs = [
516
- ... VideoConfig("video1.mp4", drop_height=0.40),
517
- ... VideoConfig("video2.mp4", drop_height=0.30, quality="accurate"),
518
- ... VideoConfig("video3.mp4", drop_height=0.50, output_video="debug3.mp4"),
512
+ ... VideoConfig("video1.mp4"),
513
+ ... VideoConfig("video2.mp4", quality="accurate"),
514
+ ... VideoConfig("video3.mp4", output_video="debug3.mp4"),
519
515
  ... ]
520
516
  >>> results = process_videos_bulk(configs, max_workers=4)
521
517
  >>> for result in results:
@@ -573,7 +569,6 @@ def _process_video_wrapper(config: VideoConfig) -> VideoResult:
573
569
  try:
574
570
  metrics = process_video(
575
571
  video_path=config.video_path,
576
- drop_height=config.drop_height,
577
572
  quality=config.quality,
578
573
  output_video=config.output_video,
579
574
  json_output=config.json_output,
@@ -242,18 +242,15 @@ class CMJDebugOverlayRenderer(BaseDebugOverlayRenderer):
242
242
  y_offset += 30
243
243
 
244
244
  # Draw angle arcs at joints for visual feedback (only if angle is available)
245
- if angles.get("ankle_angle") is not None:
246
- self._draw_angle_arc(
247
- frame, landmarks, f"{side_used}_ankle", angles["ankle_angle"]
248
- )
249
- if angles.get("knee_angle") is not None:
250
- self._draw_angle_arc(
251
- frame, landmarks, f"{side_used}_knee", angles["knee_angle"]
252
- )
253
- if angles.get("hip_angle") is not None:
254
- self._draw_angle_arc(
255
- frame, landmarks, f"{side_used}_hip", angles["hip_angle"]
256
- )
245
+ ankle_angle = angles.get("ankle_angle")
246
+ if ankle_angle is not None:
247
+ self._draw_angle_arc(frame, landmarks, f"{side_used}_ankle", ankle_angle)
248
+ knee_angle = angles.get("knee_angle")
249
+ if knee_angle is not None:
250
+ self._draw_angle_arc(frame, landmarks, f"{side_used}_knee", knee_angle)
251
+ hip_angle = angles.get("hip_angle")
252
+ if hip_angle is not None:
253
+ self._draw_angle_arc(frame, landmarks, f"{side_used}_hip", hip_angle)
257
254
 
258
255
  def _draw_angle_arc(
259
256
  self,
@@ -5,7 +5,7 @@ from typing import Any, Protocol
5
5
 
6
6
  import click
7
7
 
8
- from .auto_tuning import AutoTunedParams, QualityPreset, VideoCharacteristics
8
+ from .auto_tuning import AnalysisParameters, QualityPreset, VideoCharacteristics
9
9
  from .pose import PoseTracker
10
10
  from .smoothing import smooth_landmarks, smooth_landmarks_advanced
11
11
  from .video_io import VideoProcessor
@@ -85,8 +85,8 @@ def track_all_frames(video: VideoProcessor, tracker: PoseTracker) -> tuple[list,
85
85
 
86
86
 
87
87
  def apply_expert_param_overrides(
88
- params: AutoTunedParams, expert_params: ExpertParameters
89
- ) -> AutoTunedParams:
88
+ params: AnalysisParameters, expert_params: ExpertParameters
89
+ ) -> AnalysisParameters:
90
90
  """Apply expert parameter overrides to auto-tuned parameters.
91
91
 
92
92
  Args:
@@ -110,7 +110,7 @@ def apply_expert_param_overrides(
110
110
  def print_auto_tuned_params(
111
111
  video: VideoProcessor,
112
112
  quality_preset: QualityPreset,
113
- params: AutoTunedParams,
113
+ params: AnalysisParameters,
114
114
  characteristics: VideoCharacteristics | None = None,
115
115
  extra_params: dict[str, Any] | None = None,
116
116
  ) -> None:
@@ -159,7 +159,9 @@ def print_auto_tuned_params(
159
159
  click.echo("=" * 60 + "\n", err=True)
160
160
 
161
161
 
162
- def smooth_landmark_sequence(landmarks_sequence: list, params: AutoTunedParams) -> list:
162
+ def smooth_landmark_sequence(
163
+ landmarks_sequence: list, params: AnalysisParameters
164
+ ) -> list:
163
165
  """Apply smoothing to landmark sequence.
164
166
 
165
167
  Args:
@@ -208,99 +210,3 @@ def common_output_options(func: Callable) -> Callable: # type: ignore[type-arg]
208
210
  help="Path for JSON metrics output (default: stdout)",
209
211
  )(func)
210
212
  return func
211
-
212
-
213
- def common_quality_options(func: Callable) -> Callable: # type: ignore[type-arg]
214
- """Add quality and verbose options to CLI command."""
215
- func = click.option(
216
- "--quality",
217
- type=click.Choice(["fast", "balanced", "accurate"], case_sensitive=False),
218
- default="balanced",
219
- help=(
220
- "Analysis quality preset: "
221
- "fast (quick, less precise), "
222
- "balanced (default, good for most cases), "
223
- "accurate (research-grade, slower)"
224
- ),
225
- show_default=True,
226
- )(func)
227
- func = click.option(
228
- "--verbose",
229
- "-v",
230
- is_flag=True,
231
- help="Show auto-selected parameters and analysis details",
232
- )(func)
233
- return func
234
-
235
-
236
- def common_batch_options(func: Callable) -> Callable: # type: ignore[type-arg]
237
- """Add batch processing options to CLI command."""
238
- func = click.option(
239
- "--batch",
240
- is_flag=True,
241
- help="Enable batch processing mode for multiple videos",
242
- )(func)
243
- func = click.option(
244
- "--workers",
245
- type=int,
246
- default=4,
247
- help="Number of parallel workers for batch processing (default: 4)",
248
- show_default=True,
249
- )(func)
250
- func = click.option(
251
- "--output-dir",
252
- type=click.Path(),
253
- help="Directory for debug video outputs (batch mode only)",
254
- )(func)
255
- func = click.option(
256
- "--json-output-dir",
257
- type=click.Path(),
258
- help="Directory for JSON metrics outputs (batch mode only)",
259
- )(func)
260
- func = click.option(
261
- "--csv-summary",
262
- type=click.Path(),
263
- help="Path for CSV summary export (batch mode only)",
264
- )(func)
265
- return func
266
-
267
-
268
- def common_expert_options(func: Callable) -> Callable: # type: ignore[type-arg]
269
- """Add expert parameter options to CLI command."""
270
- func = click.option(
271
- "--smoothing-window",
272
- type=int,
273
- default=None,
274
- help="[EXPERT] Override auto-tuned smoothing window size",
275
- )(func)
276
- func = click.option(
277
- "--velocity-threshold",
278
- type=float,
279
- default=None,
280
- help="[EXPERT] Override auto-tuned velocity threshold",
281
- )(func)
282
- func = click.option(
283
- "--min-contact-frames",
284
- type=int,
285
- default=None,
286
- help="[EXPERT] Override auto-tuned minimum contact frames",
287
- )(func)
288
- func = click.option(
289
- "--visibility-threshold",
290
- type=float,
291
- default=None,
292
- help="[EXPERT] Override visibility threshold for landmarks",
293
- )(func)
294
- func = click.option(
295
- "--detection-confidence",
296
- type=float,
297
- default=None,
298
- help="[EXPERT] Override MediaPipe detection confidence (0.0-1.0)",
299
- )(func)
300
- func = click.option(
301
- "--tracking-confidence",
302
- type=float,
303
- default=None,
304
- help="[EXPERT] Override MediaPipe tracking confidence (0.0-1.0)",
305
- )(func)
306
- return func
@@ -48,29 +48,6 @@ def create_video_writer(
48
48
  return writer, needs_resize
49
49
 
50
50
 
51
- def prepare_frame_for_overlay(
52
- frame: np.ndarray, needs_resize: bool, display_width: int, display_height: int
53
- ) -> np.ndarray:
54
- """
55
- Prepare frame for overlay rendering by resizing if needed.
56
-
57
- Args:
58
- frame: Original video frame
59
- needs_resize: Whether frame needs resizing
60
- display_width: Target display width
61
- display_height: Target display height
62
-
63
- Returns:
64
- Prepared frame ready for overlay
65
- """
66
- # Apply SAR correction if needed
67
- if needs_resize:
68
- frame = cv2.resize(
69
- frame, (display_width, display_height), interpolation=cv2.INTER_LINEAR
70
- )
71
- return frame
72
-
73
-
74
51
  def write_overlay_frame(
75
52
  writer: cv2.VideoWriter, frame: np.ndarray, width: int, height: int
76
53
  ) -> None:
@@ -162,5 +139,5 @@ class BaseDebugOverlayRenderer:
162
139
  def __enter__(self) -> "BaseDebugOverlayRenderer":
163
140
  return self
164
141
 
165
- def __exit__(self, exc_type, exc_val, exc_tb) -> None: # type: ignore[no-untyped-def]
142
+ def __exit__(self, _exc_type, _exc_val, _exc_tb) -> None: # type: ignore[no-untyped-def]
166
143
  self.close()
@@ -151,10 +151,6 @@ class VideoProcessor:
151
151
 
152
152
  return frame
153
153
 
154
- def reset(self) -> None:
155
- """Reset video to beginning."""
156
- self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
157
-
158
154
  def close(self) -> None:
159
155
  """Release video capture."""
160
156
  self.cap.release()
@@ -162,5 +158,5 @@ class VideoProcessor:
162
158
  def __enter__(self) -> "VideoProcessor":
163
159
  return self
164
160
 
165
- def __exit__(self, exc_type, exc_val, exc_tb) -> None: # type: ignore[no-untyped-def]
161
+ def __exit__(self, _exc_type, _exc_val, _exc_tb) -> None: # type: ignore[no-untyped-def]
166
162
  self.close()