kinemotion 0.2.0__tar.gz → 0.5.0__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.2.0 → kinemotion-0.5.0}/.gitignore +2 -0
- kinemotion-0.5.0/.pre-commit-config.yaml +33 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/CLAUDE.md +67 -46
- kinemotion-0.5.0/GEMINI.md +162 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/PKG-INFO +36 -86
- {kinemotion-0.2.0 → kinemotion-0.5.0}/README.md +34 -84
- kinemotion-0.5.0/docs/ERRORS_FINDINGS.md +260 -0
- kinemotion-0.5.0/docs/FRAMERATE.md +747 -0
- kinemotion-0.5.0/docs/IMPLEMENTATION_PLAN.md +795 -0
- kinemotion-0.5.0/docs/IMU_METADATA_PRESERVATION.md +124 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/docs/PARAMETERS.md +244 -332
- kinemotion-0.5.0/docs/VALIDATION_PLAN.md +709 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/pyproject.toml +3 -2
- kinemotion-0.5.0/src/kinemotion/cli.py +20 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/src/kinemotion/dropjump/analysis.py +14 -2
- {kinemotion-0.2.0/src/kinemotion → kinemotion-0.5.0/src/kinemotion/dropjump}/cli.py +41 -80
- {kinemotion-0.2.0 → kinemotion-0.5.0}/src/kinemotion/dropjump/kinematics.py +23 -7
- {kinemotion-0.2.0 → kinemotion-0.5.0}/.tool-versions +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/LICENSE +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/examples/programmatic_usage.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/src/kinemotion/__init__.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/src/kinemotion/core/__init__.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/src/kinemotion/core/filtering.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/src/kinemotion/core/pose.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/src/kinemotion/core/smoothing.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/src/kinemotion/core/video_io.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/src/kinemotion/dropjump/__init__.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/src/kinemotion/dropjump/debug_overlay.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/tests/__init__.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/tests/test_adaptive_threshold.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/tests/test_aspect_ratio.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/tests/test_com_estimation.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/tests/test_contact_detection.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/tests/test_filtering.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/tests/test_kinematics.py +0 -0
- {kinemotion-0.2.0 → kinemotion-0.5.0}/tests/test_polyorder.py +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# See https://pre-commit.com for more information
|
|
2
|
+
# See https://pre-commit.com/hooks.html for more hooks
|
|
3
|
+
repos:
|
|
4
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
5
|
+
rev: v6.0.0
|
|
6
|
+
hooks:
|
|
7
|
+
- id: trailing-whitespace
|
|
8
|
+
- id: end-of-file-fixer
|
|
9
|
+
- id: check-yaml
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
args: ['--maxkb=1000']
|
|
12
|
+
- id: check-merge-conflict
|
|
13
|
+
- id: check-toml
|
|
14
|
+
- id: debug-statements
|
|
15
|
+
- id: mixed-line-ending
|
|
16
|
+
|
|
17
|
+
- repo: https://github.com/psf/black
|
|
18
|
+
rev: 23.12.1
|
|
19
|
+
hooks:
|
|
20
|
+
- id: black
|
|
21
|
+
|
|
22
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
23
|
+
rev: v0.1.9
|
|
24
|
+
hooks:
|
|
25
|
+
- id: ruff
|
|
26
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
27
|
+
|
|
28
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
29
|
+
rev: v1.7.1
|
|
30
|
+
hooks:
|
|
31
|
+
- id: mypy
|
|
32
|
+
args: [--ignore-missing-imports, --no-strict-optional]
|
|
33
|
+
exclude: ^tests/
|
|
@@ -11,12 +11,14 @@ Kinemotion: Video-based kinematic analysis tool for athletic performance. Analyz
|
|
|
11
11
|
### Dependencies
|
|
12
12
|
|
|
13
13
|
Managed with `uv` and `asdf`:
|
|
14
|
+
|
|
14
15
|
- Python version: 3.12.7 (specified in `.tool-versions`)
|
|
15
16
|
- **Important**: MediaPipe requires Python 3.12 or earlier (no 3.13 support yet)
|
|
16
17
|
- Install dependencies: `uv sync`
|
|
17
18
|
- Run CLI: `kinemotion dropjump-analyze <video.mp4>`
|
|
18
19
|
|
|
19
20
|
**Production dependencies:**
|
|
21
|
+
|
|
20
22
|
- click: CLI framework
|
|
21
23
|
- opencv-python: Video processing
|
|
22
24
|
- mediapipe: Pose detection and tracking
|
|
@@ -24,6 +26,7 @@ Managed with `uv` and `asdf`:
|
|
|
24
26
|
- scipy: Signal processing (Savitzky-Golay filter)
|
|
25
27
|
|
|
26
28
|
**Development dependencies:**
|
|
29
|
+
|
|
27
30
|
- pytest: Testing framework
|
|
28
31
|
- black: Code formatting
|
|
29
32
|
- ruff: Fast Python linter
|
|
@@ -45,20 +48,22 @@ Managed with `uv` and `asdf`:
|
|
|
45
48
|
|
|
46
49
|
### Module Structure
|
|
47
50
|
|
|
48
|
-
```
|
|
51
|
+
```text
|
|
49
52
|
src/kinemotion/
|
|
50
53
|
├── __init__.py
|
|
51
|
-
├── cli.py #
|
|
54
|
+
├── cli.py # Main CLI entry point (registers subcommands)
|
|
52
55
|
├── core/ # Shared functionality across all jump types
|
|
53
56
|
│ ├── __init__.py
|
|
54
57
|
│ ├── pose.py # MediaPipe Pose integration + CoM
|
|
55
58
|
│ ├── smoothing.py # Savitzky-Golay landmark smoothing
|
|
56
|
-
│
|
|
59
|
+
│ ├── filtering.py # Outlier rejection + bilateral filtering
|
|
60
|
+
│ └── video_io.py # Video processing (VideoProcessor class)
|
|
57
61
|
└── dropjump/ # Drop jump specific analysis
|
|
58
62
|
├── __init__.py
|
|
63
|
+
├── cli.py # Drop jump CLI command (dropjump-analyze)
|
|
59
64
|
├── analysis.py # Ground contact state detection
|
|
60
65
|
├── kinematics.py # Drop jump metrics calculations
|
|
61
|
-
└──
|
|
66
|
+
└── debug_overlay.py # Debug video overlay rendering
|
|
62
67
|
|
|
63
68
|
tests/
|
|
64
69
|
├── test_adaptive_threshold.py # Adaptive threshold tests
|
|
@@ -70,14 +75,25 @@ tests/
|
|
|
70
75
|
└── test_polyorder.py # Polynomial order tests
|
|
71
76
|
|
|
72
77
|
docs/
|
|
73
|
-
|
|
78
|
+
├── PARAMETERS.md # Comprehensive guide to all CLI parameters
|
|
79
|
+
└── IMPLEMENTATION_PLAN.md # Implementation plan and fix guide
|
|
74
80
|
```
|
|
75
81
|
|
|
76
82
|
**Design Rationale:**
|
|
83
|
+
|
|
77
84
|
- `core/` contains shared code reusable across different jump types (CMJ, squat jumps, etc.)
|
|
78
|
-
- `dropjump/` contains drop jump specific logic and
|
|
79
|
-
-
|
|
80
|
-
-
|
|
85
|
+
- `dropjump/` contains drop jump specific logic, metrics, and CLI command
|
|
86
|
+
- Each jump type module contains its own CLI command definition
|
|
87
|
+
- Main `cli.py` is just an entry point that registers subcommands from each module
|
|
88
|
+
- Future jump types (CMJ, squat) will be sibling modules to `dropjump/` with their own cli.py
|
|
89
|
+
- Single CLI group with subcommands for different analysis types
|
|
90
|
+
|
|
91
|
+
**CLI Architecture:**
|
|
92
|
+
|
|
93
|
+
- `src/kinemotion/cli.py` (20 lines): Main CLI group + command registration
|
|
94
|
+
- `src/kinemotion/dropjump/cli.py` (358 lines): Complete dropjump-analyze command
|
|
95
|
+
- Commands registered using Click's `cli.add_command()` pattern
|
|
96
|
+
- Modular design allows easy addition of new jump type analysis commands
|
|
81
97
|
|
|
82
98
|
### Analysis Pipeline
|
|
83
99
|
|
|
@@ -123,7 +139,7 @@ docs/
|
|
|
123
139
|
- **Configurable thresholds**: CLI flags allow tuning for different video qualities and athletes
|
|
124
140
|
- **Calibrated jump height**: Position-based measurement with drop height calibration for accuracy
|
|
125
141
|
- Optional `--drop-height` parameter uses known drop box height to calibrate measurements
|
|
126
|
-
-
|
|
142
|
+
- **⚠️ Accuracy claim unvalidated** - theoretical benefit estimated, not empirically tested
|
|
127
143
|
- Fallback to empirically-corrected kinematic formula when no calibration provided
|
|
128
144
|
- **Aspect ratio preservation**: Output video ALWAYS matches source video dimensions
|
|
129
145
|
- Handles SAR (Sample Aspect Ratio) metadata from mobile videos
|
|
@@ -162,6 +178,7 @@ The codebase enforces strict code quality standards using multiple tools:
|
|
|
162
178
|
### When Contributing Code
|
|
163
179
|
|
|
164
180
|
Always run before committing:
|
|
181
|
+
|
|
165
182
|
```bash
|
|
166
183
|
# Format code
|
|
167
184
|
uv run black src/
|
|
@@ -177,17 +194,18 @@ uv run pytest
|
|
|
177
194
|
```
|
|
178
195
|
|
|
179
196
|
Or run all checks at once:
|
|
197
|
+
|
|
180
198
|
```bash
|
|
181
199
|
uv run ruff check && uv run mypy src/kinemotion && uv run pytest
|
|
182
200
|
```
|
|
183
201
|
|
|
184
202
|
## Critical Implementation Details
|
|
185
203
|
|
|
186
|
-
### Aspect Ratio Preservation & SAR Handling (
|
|
204
|
+
### Aspect Ratio Preservation & SAR Handling (core/video_io.py)
|
|
187
205
|
|
|
188
206
|
**IMPORTANT**: The tool preserves the exact aspect ratio of the source video, including SAR (Sample Aspect Ratio) metadata. No dimensions are hardcoded.
|
|
189
207
|
|
|
190
|
-
#### VideoProcessor (`
|
|
208
|
+
#### VideoProcessor (`core/video_io.py:15-110`)
|
|
191
209
|
|
|
192
210
|
- Reads the **first actual frame** to get true encoded dimensions (not OpenCV properties)
|
|
193
211
|
- Critical for mobile videos with rotation metadata
|
|
@@ -206,13 +224,14 @@ if ret:
|
|
|
206
224
|
```
|
|
207
225
|
|
|
208
226
|
**Never do this:**
|
|
227
|
+
|
|
209
228
|
```python
|
|
210
229
|
# Wrong - may return incorrect dimensions with rotated videos
|
|
211
230
|
self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
|
212
231
|
self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
213
232
|
```
|
|
214
233
|
|
|
215
|
-
#### DebugOverlayRenderer (`dropjump/
|
|
234
|
+
#### DebugOverlayRenderer (`dropjump/debug_overlay.py`)
|
|
216
235
|
|
|
217
236
|
- Creates output video with **display dimensions** (respecting SAR)
|
|
218
237
|
- Resizes frames from encoded dimensions to display dimensions if needed (INTER_LANCZOS4)
|
|
@@ -230,12 +249,14 @@ self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
|
230
249
|
Instead of simple frame-to-frame differences, velocity is computed as the derivative of the smoothed position trajectory using Savitzky-Golay filter:
|
|
231
250
|
|
|
232
251
|
**Advantages:**
|
|
252
|
+
|
|
233
253
|
- **Smoother velocity curves**: Eliminates noise from frame-to-frame jitter
|
|
234
254
|
- **More accurate threshold crossings**: Clean transitions without false positives
|
|
235
255
|
- **Better interpolation**: Smoother velocity gradient for sub-frame precision
|
|
236
256
|
- **Consistent with smoothing**: Uses same polynomial fit as position smoothing
|
|
237
257
|
|
|
238
258
|
**Implementation:**
|
|
259
|
+
|
|
239
260
|
```python
|
|
240
261
|
# OLD: Simple differences (noisy)
|
|
241
262
|
velocities = np.abs(np.diff(foot_positions, prepend=foot_positions[0]))
|
|
@@ -245,6 +266,7 @@ velocities = savgol_filter(positions, window_length=5, polyorder=2, deriv=1, del
|
|
|
245
266
|
```
|
|
246
267
|
|
|
247
268
|
**Key Function:**
|
|
269
|
+
|
|
248
270
|
- `compute_velocity_from_derivative()`: Computes first derivative using Savitzky-Golay filter
|
|
249
271
|
|
|
250
272
|
#### Sub-Frame Interpolation Algorithm
|
|
@@ -252,9 +274,11 @@ velocities = savgol_filter(positions, window_length=5, polyorder=2, deriv=1, del
|
|
|
252
274
|
At 30fps, each frame represents 33.3ms. Contact events (landing, takeoff) rarely occur exactly at frame boundaries. Sub-frame interpolation estimates the exact moment between frames when velocity crosses the threshold.
|
|
253
275
|
|
|
254
276
|
**Algorithm:**
|
|
277
|
+
|
|
255
278
|
1. Calculate smooth velocity using derivative: `v = derivative(smooth_position)`
|
|
256
279
|
2. Find frames where velocity crosses threshold (e.g., from 0.025 to 0.015, threshold 0.020)
|
|
257
280
|
3. Use linear interpolation to find exact crossing point:
|
|
281
|
+
|
|
258
282
|
```python
|
|
259
283
|
# If v[10] = 0.025 and v[11] = 0.015, threshold = 0.020
|
|
260
284
|
t = (0.020 - 0.025) / (0.015 - 0.025) = 0.5
|
|
@@ -262,11 +286,13 @@ At 30fps, each frame represents 33.3ms. Contact events (landing, takeoff) rarely
|
|
|
262
286
|
```
|
|
263
287
|
|
|
264
288
|
**Key Functions:**
|
|
289
|
+
|
|
265
290
|
- `interpolate_threshold_crossing()`: Linear interpolation of velocity crossing
|
|
266
291
|
- `find_interpolated_phase_transitions()`: Returns fractional frame indices for all phases
|
|
267
292
|
|
|
268
293
|
**Accuracy Improvement:**
|
|
269
|
-
|
|
294
|
+
|
|
295
|
+
```text
|
|
270
296
|
30fps without interpolation: ±33ms (1 frame on each boundary)
|
|
271
297
|
30fps with interpolation: ±10ms (sub-frame precision)
|
|
272
298
|
60fps without interpolation: ±17ms
|
|
@@ -274,6 +300,7 @@ At 30fps, each frame represents 33.3ms. Contact events (landing, takeoff) rarely
|
|
|
274
300
|
```
|
|
275
301
|
|
|
276
302
|
**Velocity Comparison:**
|
|
303
|
+
|
|
277
304
|
```python
|
|
278
305
|
# Frame-to-frame differences: noisy, discontinuous jumps
|
|
279
306
|
v_simple = [0.01, 0.03, 0.02, 0.04, 0.02, 0.01] # Jittery
|
|
@@ -283,6 +310,7 @@ v_deriv = [0.015, 0.022, 0.025, 0.024, 0.018, 0.012] # Smooth
|
|
|
283
310
|
```
|
|
284
311
|
|
|
285
312
|
**Example:**
|
|
313
|
+
|
|
286
314
|
```python
|
|
287
315
|
# Integer frames: contact from frame 49 to 53 (5 frames = 168ms at 30fps)
|
|
288
316
|
# With derivative velocity: contact from 49.0 to 53.0 (4 frames = 135ms)
|
|
@@ -298,12 +326,14 @@ v_deriv = [0.015, 0.022, 0.025, 0.024, 0.018, 0.012] # Smooth
|
|
|
298
326
|
Acceleration (second derivative) reveals characteristic patterns at contact events:
|
|
299
327
|
|
|
300
328
|
**Physical Patterns:**
|
|
329
|
+
|
|
301
330
|
- **Landing impact**: Large acceleration spike as feet decelerate on impact
|
|
302
331
|
- **Takeoff**: Acceleration change as body transitions from static to upward motion
|
|
303
332
|
- **In flight**: Constant acceleration (gravity ≈ -9.81 m/s²)
|
|
304
333
|
- **On ground**: Near-zero acceleration (stationary position)
|
|
305
334
|
|
|
306
335
|
**Implementation:**
|
|
336
|
+
|
|
307
337
|
```python
|
|
308
338
|
# Compute acceleration using Savitzky-Golay second derivative
|
|
309
339
|
acceleration = savgol_filter(positions, window=5, polyorder=2, deriv=2, delta=1.0)
|
|
@@ -317,6 +347,7 @@ takeoff_frame = np.argmax(accel_change[search_window])
|
|
|
317
347
|
```
|
|
318
348
|
|
|
319
349
|
**Key Functions:**
|
|
350
|
+
|
|
320
351
|
- `compute_acceleration_from_derivative()`: Computes second derivative using Savitzky-Golay
|
|
321
352
|
- `refine_transition_with_curvature()`: Searches for acceleration patterns near transitions
|
|
322
353
|
- `find_interpolated_phase_transitions_with_curvature()`: Combines velocity + curvature
|
|
@@ -330,11 +361,13 @@ Curvature analysis refines velocity-based estimates through blending:
|
|
|
330
361
|
3. **Blending**: 70% curvature-based + 30% velocity-based
|
|
331
362
|
|
|
332
363
|
**Why Blending?**
|
|
364
|
+
|
|
333
365
|
- Velocity is reliable for coarse timing
|
|
334
366
|
- Curvature provides fine detail but can be noisy at boundaries
|
|
335
367
|
- Blending prevents large deviations while incorporating physical insights
|
|
336
368
|
|
|
337
369
|
**Algorithm:**
|
|
370
|
+
|
|
338
371
|
```python
|
|
339
372
|
# 1. Get velocity-based estimate
|
|
340
373
|
velocity_estimate = 49.0 # from interpolation
|
|
@@ -349,6 +382,7 @@ blend = 0.7 * 47.2 + 0.3 * 49.0 # = 47.74
|
|
|
349
382
|
```
|
|
350
383
|
|
|
351
384
|
**Accuracy Improvement:**
|
|
385
|
+
|
|
352
386
|
```python
|
|
353
387
|
# Example: Landing detection
|
|
354
388
|
# Velocity only: frame 49.0 (when velocity drops below threshold)
|
|
@@ -357,6 +391,7 @@ blend = 0.7 * 47.2 + 0.3 * 49.0 # = 47.74
|
|
|
357
391
|
```
|
|
358
392
|
|
|
359
393
|
**Optional Feature:**
|
|
394
|
+
|
|
360
395
|
- Enabled by default (`--use-curvature`, default: True)
|
|
361
396
|
- Can be disabled with `--no-curvature` flag for pure velocity-based detection
|
|
362
397
|
- Negligible performance impact (reuses smoothed trajectory)
|
|
@@ -374,12 +409,13 @@ Always convert to Python `int()` in `to_dict()` method:
|
|
|
374
409
|
```
|
|
375
410
|
|
|
376
411
|
**Never do this:**
|
|
412
|
+
|
|
377
413
|
```python
|
|
378
414
|
# Wrong - will fail with "Object of type int64 is not JSON serializable"
|
|
379
415
|
"contact_start_frame": self.contact_start_frame
|
|
380
416
|
```
|
|
381
417
|
|
|
382
|
-
### Video Codec Handling (dropjump/
|
|
418
|
+
### Video Codec Handling (dropjump/debug_overlay.py)
|
|
383
419
|
|
|
384
420
|
- Primary codec: H.264 (avc1) - better quality, smaller file size
|
|
385
421
|
- Fallback codec: MPEG-4 (mp4v) - broader compatibility
|
|
@@ -393,6 +429,7 @@ OpenCV and NumPy use different dimension ordering:
|
|
|
393
429
|
- **OpenCV VideoWriter size**: `(width, height)` tuple
|
|
394
430
|
|
|
395
431
|
Example:
|
|
432
|
+
|
|
396
433
|
```python
|
|
397
434
|
frame.shape # (1080, 1920, 3) - height first
|
|
398
435
|
cv2.VideoWriter(..., (1920, 1080)) # width first
|
|
@@ -407,12 +444,13 @@ Always be careful with dimension ordering to avoid squashed/stretched videos.
|
|
|
407
444
|
1. Update `DropJumpMetrics` class in `dropjump/kinematics.py:10-19`
|
|
408
445
|
2. Add calculation logic in `calculate_drop_jump_metrics()` function
|
|
409
446
|
3. Update `to_dict()` method for JSON serialization (remember to convert NumPy types to Python types)
|
|
410
|
-
4. Optionally add visualization in `DebugOverlayRenderer.render_frame()` in `dropjump/
|
|
447
|
+
4. Optionally add visualization in `DebugOverlayRenderer.render_frame()` in `dropjump/debug_overlay.py`
|
|
411
448
|
5. Add tests in `tests/test_kinematics.py`
|
|
412
449
|
|
|
413
450
|
### Modifying Contact Detection Logic
|
|
414
451
|
|
|
415
452
|
Edit `detect_ground_contact()` in `dropjump/analysis.py:14`. Key parameters:
|
|
453
|
+
|
|
416
454
|
- `velocity_threshold`: Tune for different surface/athlete combinations (default: 0.02)
|
|
417
455
|
- `min_contact_frames`: Adjust for frame rate and contact duration expectations (default: 3)
|
|
418
456
|
- `visibility_threshold`: Minimum landmark visibility score (default: 0.5)
|
|
@@ -420,6 +458,7 @@ Edit `detect_ground_contact()` in `dropjump/analysis.py:14`. Key parameters:
|
|
|
420
458
|
### Adjusting Smoothing
|
|
421
459
|
|
|
422
460
|
Modify `smooth_landmarks()` in `core/smoothing.py:9`:
|
|
461
|
+
|
|
423
462
|
- `window_length`: Controls smoothing strength (must be odd, default: 5)
|
|
424
463
|
- `polyorder`: Polynomial order for Savitzky-Golay filter (default: 2)
|
|
425
464
|
|
|
@@ -427,11 +466,10 @@ Modify `smooth_landmarks()` in `core/smoothing.py:9`:
|
|
|
427
466
|
|
|
428
467
|
**IMPORTANT**: See `docs/PARAMETERS.md` for comprehensive guide on all CLI parameters.
|
|
429
468
|
|
|
430
|
-
Quick reference
|
|
431
|
-
|
|
432
|
-
- **adaptive-threshold**: Auto-calibrate velocity threshold from baseline (↑ accuracy by 2-3%)
|
|
469
|
+
Quick reference for `dropjump-analyze`:
|
|
470
|
+
|
|
433
471
|
- **smoothing-window**: Trajectory smoothness (↑ for noisy video)
|
|
434
|
-
- **velocity-threshold**: Contact sensitivity (↓ to detect brief contacts)
|
|
472
|
+
- **velocity-threshold**: Contact sensitivity (↓ to detect brief contacts)
|
|
435
473
|
- **min-contact-frames**: Temporal filter (↑ to remove false contacts)
|
|
436
474
|
- **visibility-threshold**: Landmark confidence (↓ for occluded landmarks)
|
|
437
475
|
- **detection-confidence**: Pose detection strictness (MediaPipe)
|
|
@@ -439,7 +477,10 @@ Quick reference:
|
|
|
439
477
|
- **drop-height**: Drop box height in meters for calibration (e.g., 0.40 for 40cm)
|
|
440
478
|
- **use-curvature**: Enable trajectory curvature analysis (default: enabled)
|
|
441
479
|
|
|
480
|
+
**Note**: Drop jump analysis always uses foot-based tracking with fixed velocity thresholds because typical drop jump videos are ~3 seconds long without a stationary baseline period. The `--use-com` and `--adaptive-threshold` options (available in `core/` modules) require longer videos (~5+ seconds) with 3 seconds of standing baseline, making them suitable for future jump types like CMJ (countermovement jump) but not drop jumps.
|
|
481
|
+
|
|
442
482
|
The detailed guide includes:
|
|
483
|
+
|
|
443
484
|
- How each parameter works internally
|
|
444
485
|
- Frame rate considerations
|
|
445
486
|
- Scenario-based recommended settings
|
|
@@ -449,6 +490,7 @@ The detailed guide includes:
|
|
|
449
490
|
### Working with Different Video Formats
|
|
450
491
|
|
|
451
492
|
The tool handles various video formats and aspect ratios:
|
|
493
|
+
|
|
452
494
|
- 16:9 landscape (1920x1080)
|
|
453
495
|
- 4:3 standard (640x480)
|
|
454
496
|
- 9:16 portrait (1080x1920)
|
|
@@ -484,6 +526,7 @@ uv run pytest -v
|
|
|
484
526
|
### Code Quality
|
|
485
527
|
|
|
486
528
|
All code passes:
|
|
529
|
+
|
|
487
530
|
- ✅ **Type checking**: Full mypy strict mode compliance
|
|
488
531
|
- ✅ **Linting**: ruff checks with comprehensive rule sets
|
|
489
532
|
- ✅ **Tests**: 25/25 tests passing
|
|
@@ -499,6 +542,7 @@ All code passes:
|
|
|
499
542
|
### Video Dimension Issues
|
|
500
543
|
|
|
501
544
|
If output video has wrong aspect ratio:
|
|
545
|
+
|
|
502
546
|
1. Check `VideoProcessor` is reading first frame correctly
|
|
503
547
|
2. Verify `DebugOverlayRenderer` receives correct width/height from `VideoProcessor`
|
|
504
548
|
3. Check that `write_frame()` validation is enabled (should raise error if dimensions mismatch)
|
|
@@ -507,6 +551,7 @@ If output video has wrong aspect ratio:
|
|
|
507
551
|
### JSON Serialization Errors
|
|
508
552
|
|
|
509
553
|
If you see "Object of type X is not JSON serializable":
|
|
554
|
+
|
|
510
555
|
1. Check `kinematics.py` `to_dict()` method
|
|
511
556
|
2. Ensure all NumPy types are converted to Python types with `int()` or `float()`
|
|
512
557
|
3. Run `tests/test_kinematics.py::test_metrics_to_dict` to verify
|
|
@@ -514,6 +559,7 @@ If you see "Object of type X is not JSON serializable":
|
|
|
514
559
|
### Video Codec Issues
|
|
515
560
|
|
|
516
561
|
If output video won't play:
|
|
562
|
+
|
|
517
563
|
1. Try different output format: `.avi` instead of `.mp4`
|
|
518
564
|
2. Check OpenCV codec support: `cv2.getBuildInformation()`
|
|
519
565
|
3. DebugOverlayRenderer will fallback from H.264 to MPEG-4 automatically
|
|
@@ -521,6 +567,7 @@ If output video won't play:
|
|
|
521
567
|
### Type Checking Issues
|
|
522
568
|
|
|
523
569
|
If mypy reports errors:
|
|
570
|
+
|
|
524
571
|
1. Ensure all function signatures have complete type annotations (parameters and return types)
|
|
525
572
|
2. For numpy types, use explicit casts: `int()`, `float()` when converting to Python types
|
|
526
573
|
3. For third-party libraries without stubs (cv2, mediapipe, scipy), use `# type: ignore` comments sparingly
|
|
@@ -565,38 +612,12 @@ uv run kinemotion dropjump-analyze video.mp4 \
|
|
|
565
612
|
uv run kinemotion dropjump-analyze jump.mp4 \
|
|
566
613
|
--output debug.mp4 \
|
|
567
614
|
--json-output metrics.json
|
|
568
|
-
|
|
569
|
-
# Use center of mass tracking for improved accuracy (3-5% gain)
|
|
570
|
-
uv run kinemotion dropjump-analyze video.mp4 \
|
|
571
|
-
--use-com \
|
|
572
|
-
--output debug.mp4 \
|
|
573
|
-
--json-output metrics.json
|
|
574
|
-
|
|
575
|
-
# Full analysis with CoM tracking and calibration
|
|
576
|
-
uv run kinemotion dropjump-analyze video.mp4 \
|
|
577
|
-
--use-com \
|
|
578
|
-
--drop-height 0.40 \
|
|
579
|
-
--output debug_com.mp4 \
|
|
580
|
-
--json-output metrics.json
|
|
581
|
-
|
|
582
|
-
# Adaptive threshold for auto-calibration (2-3% accuracy gain)
|
|
583
|
-
uv run kinemotion dropjump-analyze video.mp4 \
|
|
584
|
-
--adaptive-threshold \
|
|
585
|
-
--output debug.mp4 \
|
|
586
|
-
--json-output metrics.json
|
|
587
|
-
|
|
588
|
-
# Maximum accuracy: CoM + adaptive threshold + calibration (~93-96%)
|
|
589
|
-
uv run kinemotion dropjump-analyze video.mp4 \
|
|
590
|
-
--adaptive-threshold \
|
|
591
|
-
--use-com \
|
|
592
|
-
--drop-height 0.40 \
|
|
593
|
-
--output debug_max.mp4 \
|
|
594
|
-
--json-output metrics.json
|
|
595
615
|
```
|
|
596
616
|
|
|
597
617
|
## MCP Server Configuration
|
|
598
618
|
|
|
599
619
|
The repository includes MCP server configuration in `.mcp.json`:
|
|
620
|
+
|
|
600
621
|
- **web-search**: DuckDuckGo search via @dannyboy2042/freebird-mcp
|
|
601
622
|
- **sequential**: Sequential thinking via @smithery-ai/server-sequential-thinking
|
|
602
623
|
- **context7**: Library documentation via @upstash/context7-mcp
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# GEMINI.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to the Gemini model when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Repository Purpose
|
|
6
|
+
|
|
7
|
+
Kinemotion is a video-based kinematic analysis tool for athletic performance. It analyzes drop-jump videos to estimate ground contact time, flight time, and jump height. The analysis is done by tracking an athlete's movement using MediaPipe pose tracking and applying advanced kinematic calculations. It supports both traditional foot-based tracking and a more accurate center of mass (CoM) tracking.
|
|
8
|
+
|
|
9
|
+
**IMPORTANT**: The tool's accuracy has not been validated against gold-standard measurements. Any accuracy claims are theoretical.
|
|
10
|
+
|
|
11
|
+
## Project Setup
|
|
12
|
+
|
|
13
|
+
### Dependencies
|
|
14
|
+
|
|
15
|
+
The project uses `uv` for dependency management and `asdf` for Python version management.
|
|
16
|
+
|
|
17
|
+
- **Python Version**: 3.12.7 (specified in `.tool-versions`). MediaPipe requires Python <= 3.12.
|
|
18
|
+
- **Install Dependencies**: `uv sync`
|
|
19
|
+
|
|
20
|
+
**Key Libraries:**
|
|
21
|
+
|
|
22
|
+
- **Production**: `click`, `opencv-python`, `mediapipe`, `numpy`, `scipy`.
|
|
23
|
+
- **Development**: `pytest`, `black`, `ruff`, `mypy`.
|
|
24
|
+
|
|
25
|
+
### Development Commands
|
|
26
|
+
|
|
27
|
+
- **Run CLI**: `uv run kinemotion dropjump-analyze <video_path>`
|
|
28
|
+
- **Install/Sync Dependencies**: `uv sync`
|
|
29
|
+
- **Run Tests**: `uv run pytest`
|
|
30
|
+
- **Format Code**: `uv run black src/`
|
|
31
|
+
- **Lint Code**: `uv run ruff check`
|
|
32
|
+
- **Auto-fix Linting**: `uv run ruff check --fix`
|
|
33
|
+
- **Type Check**: `uv run mypy src/kinemotion`
|
|
34
|
+
- **Run All Checks**: `uv run ruff check && uv run mypy src/kinemotion && uv run pytest`
|
|
35
|
+
|
|
36
|
+
## Architecture
|
|
37
|
+
|
|
38
|
+
### Module Structure
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
src/kinemotion/
|
|
42
|
+
├── cli.py # Main CLI entry point
|
|
43
|
+
├── core/ # Shared functionality (pose, smoothing, filtering, video_io)
|
|
44
|
+
└── dropjump/ # Drop jump specific analysis (cli, analysis, kinematics, debug_overlay)
|
|
45
|
+
tests/ # Unit and integration tests
|
|
46
|
+
docs/ # Documentation (PARAMETERS.md is key)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- `core/` contains reusable code for different jump types.
|
|
50
|
+
- `dropjump/` contains logic specific to drop jumps.
|
|
51
|
+
- The main `cli.py` registers subcommands from modules like `dropjump/cli.py`.
|
|
52
|
+
|
|
53
|
+
### Analysis Pipeline
|
|
54
|
+
|
|
55
|
+
1. **Pose Tracking** (`core/pose.py`): Extracts 13 body landmarks per frame using MediaPipe.
|
|
56
|
+
2. **Center of Mass (CoM) Estimation** (`core/pose.py`): Optional, more accurate tracking using a biomechanical model.
|
|
57
|
+
3. **Smoothing** (`core/smoothing.py`): A Savitzky-Golay filter reduces jitter.
|
|
58
|
+
4. **Contact Detection** (`dropjump/analysis.py`): Analyzes vertical velocity to determine ground contact vs. flight.
|
|
59
|
+
5. **Phase Identification**: Finds continuous ground contact and flight periods.
|
|
60
|
+
6. **Sub-Frame Interpolation** (`dropjump/analysis.py`): Estimates exact transition times between frames using linear interpolation on the velocity curve, improving timing precision significantly.
|
|
61
|
+
7. **Trajectory Curvature Analysis** (`dropjump/analysis.py`): Refines transition timing by detecting acceleration spikes (e.g., landing impact).
|
|
62
|
+
8. **Metrics Calculation** (`dropjump/kinematics.py`): Calculates ground contact time, flight time, and jump height.
|
|
63
|
+
9. **Output**: Provides metrics in JSON format and an optional debug video.
|
|
64
|
+
|
|
65
|
+
## Critical Implementation Details
|
|
66
|
+
|
|
67
|
+
### 1. Aspect Ratio Preservation & SAR Handling (`core/video_io.py`)
|
|
68
|
+
|
|
69
|
+
- **CRITICAL**: The tool must preserve the source video's exact aspect ratio, including Sample Aspect Ratio (SAR) from mobile videos.
|
|
70
|
+
- **DO**: Get frame dimensions from the first actual frame read from the video (`frame.shape[:2]`), not from `cv2.CAP_PROP_*` properties, which can be wrong for rotated videos.
|
|
71
|
+
- **DO**: Use `ffprobe` to extract SAR and calculate correct display dimensions.
|
|
72
|
+
- The `DebugOverlayRenderer` uses these display dimensions for the output video.
|
|
73
|
+
|
|
74
|
+
### 2. Sub-Frame Interpolation (`dropjump/analysis.py`)
|
|
75
|
+
|
|
76
|
+
- **CRITICAL**: Timing precision is achieved by interpolating between frames.
|
|
77
|
+
- **Velocity Calculation**: Velocity is computed as the **first derivative of the smoothed position trajectory** using a Savitzky-Golay filter (`savgol_filter(..., deriv=1)`). This is much smoother and more accurate than simple frame-to-frame differences.
|
|
78
|
+
- **Interpolation**: When velocity crosses the contact threshold between two frames, linear interpolation is used to find the fractional frame index of the crossing. This improves timing accuracy from ~33ms to ~10ms at 30fps.
|
|
79
|
+
|
|
80
|
+
### 3. Trajectory Curvature Analysis (`dropjump/analysis.py`)
|
|
81
|
+
|
|
82
|
+
- **CRITICAL**: Event timing is further refined using acceleration patterns.
|
|
83
|
+
- **Acceleration Calculation**: Acceleration is the **second derivative of the smoothed position** (`savgol_filter(..., deriv=2)`).
|
|
84
|
+
- **Event Detection**:
|
|
85
|
+
- **Landing**: A large acceleration spike (impact deceleration).
|
|
86
|
+
- **Takeoff**: A sharp change in acceleration.
|
|
87
|
+
- **Blending**: The final transition time is a weighted blend: 70% from the curvature-based estimate and 30% from the velocity-based estimate. This is enabled by default via `--use-curvature`.
|
|
88
|
+
|
|
89
|
+
### 4. JSON Serialization of NumPy Types (`dropjump/kinematics.py`)
|
|
90
|
+
|
|
91
|
+
- **CRITICAL**: Standard `json.dump` cannot serialize NumPy integer types (e.g., `np.int64`).
|
|
92
|
+
- **DO**: Explicitly cast all NumPy numbers to standard Python types (`int()`, `float()`) within the `to_dict()` methods of data classes before serialization.
|
|
93
|
+
|
|
94
|
+
### 5. OpenCV Frame Dimensions
|
|
95
|
+
|
|
96
|
+
- **CRITICAL**: Be aware of dimension ordering differences.
|
|
97
|
+
- **NumPy `frame.shape`**: `(height, width, channels)`
|
|
98
|
+
- **OpenCV `cv2.VideoWriter()` size**: `(width, height)`
|
|
99
|
+
- Always pass dimensions to OpenCV functions in `(width, height)` order.
|
|
100
|
+
|
|
101
|
+
## Code Quality & Workflow
|
|
102
|
+
|
|
103
|
+
When contributing code, strictly adhere to the project's quality standards.
|
|
104
|
+
|
|
105
|
+
1. **Format Code**: `uv run black src/`
|
|
106
|
+
2. **Lint and Fix**: `uv run ruff check --fix`
|
|
107
|
+
3. **Type Check**: `uv run mypy src/kinemotion`
|
|
108
|
+
4. **Run Tests**: `uv run pytest`
|
|
109
|
+
|
|
110
|
+
**Run all checks before committing**: `uv run ruff check && uv run mypy src/kinemotion && uv run pytest`
|
|
111
|
+
|
|
112
|
+
- **Type Safety**: The project uses `mypy` in strict mode. All functions must have full type annotations.
|
|
113
|
+
- **Linting**: `ruff` is used for linting. Configuration is in `pyproject.toml`.
|
|
114
|
+
- **Formatting**: `black` is used for code formatting.
|
|
115
|
+
|
|
116
|
+
## Common Development Tasks
|
|
117
|
+
|
|
118
|
+
- **Adding New Metrics**:
|
|
119
|
+
1. Update `DropJumpMetrics` in `dropjump/kinematics.py`.
|
|
120
|
+
2. Add calculation logic in `calculate_drop_jump_metrics()`.
|
|
121
|
+
3. Update `to_dict()` method (remember to cast NumPy types).
|
|
122
|
+
4. (Optional) Add visualization in `DebugOverlayRenderer`.
|
|
123
|
+
5. Add tests in `tests/test_kinematics.py`.
|
|
124
|
+
- **Modifying Contact Detection**: Edit `detect_ground_contact()` in `dropjump/analysis.py`.
|
|
125
|
+
- **Adjusting Smoothing**: Modify `smooth_landmarks()` in `core/smoothing.py`.
|
|
126
|
+
|
|
127
|
+
## Parameter Tuning
|
|
128
|
+
|
|
129
|
+
A comprehensive guide to all CLI parameters is in `docs/PARAMETERS.md`. Refer to it for detailed explanations.
|
|
130
|
+
|
|
131
|
+
**Key `dropjump-analyze` parameters:**
|
|
132
|
+
|
|
133
|
+
- `--smoothing-window`: Controls trajectory smoothness. Increase for noisy video.
|
|
134
|
+
- `--polyorder`: Polynomial order for smoothing. `2` is ideal for jump physics.
|
|
135
|
+
- `--velocity-threshold`: Contact sensitivity. Decrease to detect shorter contacts.
|
|
136
|
+
- `--min-contact-frames`: Temporal filter. Increase to remove false contacts.
|
|
137
|
+
- `--drop-height`: **Important for accuracy.** Calibrates jump height using a known box height in meters.
|
|
138
|
+
- `--use-curvature`: Enables acceleration-based timing refinement (default: True).
|
|
139
|
+
- `--outlier-rejection`: Removes tracking glitches before smoothing (default: True).
|
|
140
|
+
- `--bilateral-filter`: Experimental edge-preserving smoothing alternative to Savitzky-Golay.
|
|
141
|
+
|
|
142
|
+
## Testing
|
|
143
|
+
|
|
144
|
+
- **Run all tests**: `uv run pytest`
|
|
145
|
+
- **Run a specific test file**: `uv run pytest tests/test_contact_detection.py -v`
|
|
146
|
+
- The project has comprehensive test coverage for core functionalities like aspect ratio, contact detection, CoM estimation, and kinematics.
|
|
147
|
+
|
|
148
|
+
## CLI Usage Examples
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Get help for the dropjump command
|
|
152
|
+
uv run kinemotion dropjump-analyze --help
|
|
153
|
+
|
|
154
|
+
# Basic analysis, print JSON to stdout
|
|
155
|
+
uv run kinemotion dropjump-analyze video.mp4
|
|
156
|
+
|
|
157
|
+
# Full analysis: generate debug video, save metrics, and use calibration
|
|
158
|
+
uv run kinemotion dropjump-analyze video.mp4 \
|
|
159
|
+
--output debug_video.mp4 \
|
|
160
|
+
--json-output metrics.json \
|
|
161
|
+
--drop-height 0.40
|
|
162
|
+
```
|