kinemotion 0.10.1__tar.gz → 0.10.2__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.10.1 → kinemotion-0.10.2}/CHANGELOG.md +8 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/PKG-INFO +1 -1
- {kinemotion-0.10.1 → kinemotion-0.10.2}/pyproject.toml +1 -1
- {kinemotion-0.10.1 → kinemotion-0.10.2}/tests/test_adaptive_threshold.py +17 -16
- {kinemotion-0.10.1 → kinemotion-0.10.2}/tests/test_aspect_ratio.py +2 -1
- {kinemotion-0.10.1 → kinemotion-0.10.2}/tests/test_filtering.py +10 -5
- {kinemotion-0.10.1 → kinemotion-0.10.2}/tests/test_polyorder.py +8 -4
- {kinemotion-0.10.1 → kinemotion-0.10.2}/uv.lock +1 -1
- {kinemotion-0.10.1 → kinemotion-0.10.2}/.dockerignore +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/.github/pull_request_template.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/.github/workflows/release.yml +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/.gitignore +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/.pre-commit-config.yaml +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/.tool-versions +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/CLAUDE.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/CODE_OF_CONDUCT.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/CONTRIBUTING.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/Dockerfile +1 -1
- {kinemotion-0.10.1 → kinemotion-0.10.2}/GEMINI.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/LICENSE +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/README.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/SECURITY.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/docs/BULK_PROCESSING.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/docs/ERRORS_FINDINGS.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/docs/FRAMERATE.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/docs/IMU_METADATA_PRESERVATION.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/docs/PARAMETERS.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/docs/VALIDATION_PLAN.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/examples/bulk/README.md +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/examples/bulk/bulk_processing.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/examples/bulk/simple_example.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/examples/programmatic_usage.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/__init__.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/api.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/cli.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/core/__init__.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/core/auto_tuning.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/core/filtering.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/core/pose.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/core/smoothing.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/core/video_io.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/dropjump/__init__.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/dropjump/analysis.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/dropjump/cli.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/dropjump/debug_overlay.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/dropjump/kinematics.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/src/kinemotion/py.typed +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/tests/__init__.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/tests/test_api.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/tests/test_com_estimation.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/tests/test_contact_detection.py +0 -0
- {kinemotion-0.10.1 → kinemotion-0.10.2}/tests/test_kinematics.py +0 -0
|
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
<!-- version list -->
|
|
9
9
|
|
|
10
|
+
## v0.10.2 (2025-11-03)
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- Replace legacy numpy random functions with Generator API
|
|
15
|
+
([`5cfa31b`](https://github.com/feniix/kinemotion/commit/5cfa31bce040eadfc53d52654c2e75087ef087a5))
|
|
16
|
+
|
|
17
|
+
|
|
10
18
|
## v0.10.1 (2025-11-03)
|
|
11
19
|
|
|
12
20
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kinemotion
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.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
|
|
@@ -14,8 +14,8 @@ def test_adaptive_threshold_basic() -> None:
|
|
|
14
14
|
baseline_frames = int(3 * fps) # 90 frames
|
|
15
15
|
|
|
16
16
|
# Baseline: small random noise around position 0.5
|
|
17
|
-
np.random.
|
|
18
|
-
baseline_positions = 0.5 +
|
|
17
|
+
rng = np.random.default_rng(42)
|
|
18
|
+
baseline_positions = 0.5 + rng.normal(0, 0.005, baseline_frames)
|
|
19
19
|
|
|
20
20
|
# Movement: larger position changes
|
|
21
21
|
movement_positions = np.linspace(0.5, 0.7, 60)
|
|
@@ -36,8 +36,8 @@ def test_adaptive_threshold_high_noise() -> None:
|
|
|
36
36
|
baseline_frames = int(3 * fps)
|
|
37
37
|
|
|
38
38
|
# High noise baseline
|
|
39
|
-
np.random.
|
|
40
|
-
baseline_positions = 0.5 +
|
|
39
|
+
rng = np.random.default_rng(42)
|
|
40
|
+
baseline_positions = 0.5 + rng.normal(0, 0.015, baseline_frames)
|
|
41
41
|
movement_positions = np.linspace(0.5, 0.8, 60)
|
|
42
42
|
positions = np.concatenate([baseline_positions, movement_positions])
|
|
43
43
|
|
|
@@ -56,8 +56,8 @@ def test_adaptive_threshold_low_noise() -> None:
|
|
|
56
56
|
baseline_frames = int(3 * fps)
|
|
57
57
|
|
|
58
58
|
# Very low noise baseline
|
|
59
|
-
np.random.
|
|
60
|
-
baseline_positions = 0.5 +
|
|
59
|
+
rng = np.random.default_rng(42)
|
|
60
|
+
baseline_positions = 0.5 + rng.normal(0, 0.002, baseline_frames)
|
|
61
61
|
movement_positions = np.linspace(0.5, 0.7, 60)
|
|
62
62
|
positions = np.concatenate([baseline_positions, movement_positions])
|
|
63
63
|
|
|
@@ -90,8 +90,8 @@ def test_adaptive_threshold_maximum_bound() -> None:
|
|
|
90
90
|
baseline_frames = int(3 * fps)
|
|
91
91
|
|
|
92
92
|
# Extreme noise baseline
|
|
93
|
-
np.random.
|
|
94
|
-
baseline_positions = 0.5 +
|
|
93
|
+
rng = np.random.default_rng(42)
|
|
94
|
+
baseline_positions = 0.5 + rng.normal(0, 0.05, baseline_frames)
|
|
95
95
|
movement_positions = np.linspace(0.5, 0.8, 60)
|
|
96
96
|
positions = np.concatenate([baseline_positions, movement_positions])
|
|
97
97
|
|
|
@@ -106,7 +106,8 @@ def test_adaptive_threshold_short_video() -> None:
|
|
|
106
106
|
fps = 30.0
|
|
107
107
|
|
|
108
108
|
# Only 60 frames (2 seconds) - less than 3 second baseline
|
|
109
|
-
|
|
109
|
+
rng = np.random.default_rng(42)
|
|
110
|
+
positions = 0.5 + rng.normal(0, 0.01, 60)
|
|
110
111
|
|
|
111
112
|
threshold = calculate_adaptive_threshold(positions, fps, baseline_duration=3.0)
|
|
112
113
|
|
|
@@ -137,8 +138,8 @@ def test_adaptive_threshold_different_fps() -> None:
|
|
|
137
138
|
fps = 60.0
|
|
138
139
|
baseline_frames = int(3 * fps) # 180 frames
|
|
139
140
|
|
|
140
|
-
np.random.
|
|
141
|
-
baseline_positions = 0.5 +
|
|
141
|
+
rng = np.random.default_rng(42)
|
|
142
|
+
baseline_positions = 0.5 + rng.normal(0, 0.008, baseline_frames)
|
|
142
143
|
movement_positions = np.linspace(0.5, 0.7, 120)
|
|
143
144
|
positions = np.concatenate([baseline_positions, movement_positions])
|
|
144
145
|
|
|
@@ -153,8 +154,8 @@ def test_adaptive_threshold_custom_multiplier() -> None:
|
|
|
153
154
|
fps = 30.0
|
|
154
155
|
baseline_frames = int(3 * fps)
|
|
155
156
|
|
|
156
|
-
np.random.
|
|
157
|
-
baseline_positions = 0.5 +
|
|
157
|
+
rng = np.random.default_rng(42)
|
|
158
|
+
baseline_positions = 0.5 + rng.normal(0, 0.008, baseline_frames)
|
|
158
159
|
movement_positions = np.linspace(0.5, 0.7, 60)
|
|
159
160
|
positions = np.concatenate([baseline_positions, movement_positions])
|
|
160
161
|
|
|
@@ -181,9 +182,9 @@ def test_adaptive_threshold_baseline_duration() -> None:
|
|
|
181
182
|
fps = 30.0
|
|
182
183
|
|
|
183
184
|
# Long video with different noise in different sections
|
|
184
|
-
np.random.
|
|
185
|
-
first_3s = 0.5 +
|
|
186
|
-
next_2s = 0.5 +
|
|
185
|
+
rng = np.random.default_rng(42)
|
|
186
|
+
first_3s = 0.5 + rng.normal(0, 0.005, int(3 * fps)) # Low noise
|
|
187
|
+
next_2s = 0.5 + rng.normal(0, 0.015, int(2 * fps)) # High noise
|
|
187
188
|
movement = np.linspace(0.5, 0.7, 60)
|
|
188
189
|
|
|
189
190
|
positions = np.concatenate([first_3s, next_2s, movement])
|
|
@@ -21,9 +21,10 @@ def create_test_video(
|
|
|
21
21
|
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
|
22
22
|
writer = cv2.VideoWriter(temp_path, fourcc, fps, (width, height))
|
|
23
23
|
|
|
24
|
+
rng = np.random.default_rng(42)
|
|
24
25
|
for _ in range(num_frames):
|
|
25
26
|
# Create a random frame
|
|
26
|
-
frame =
|
|
27
|
+
frame = rng.integers(0, 255, (height, width, 3), dtype=np.uint8)
|
|
27
28
|
writer.write(frame)
|
|
28
29
|
|
|
29
30
|
writer.release()
|
|
@@ -35,8 +35,9 @@ def test_detect_outliers_ransac_finds_glitches() -> None:
|
|
|
35
35
|
def test_detect_outliers_ransac_handles_clean_data() -> None:
|
|
36
36
|
"""Test that RANSAC does not flag valid points as outliers."""
|
|
37
37
|
# Create smooth motion with small noise
|
|
38
|
+
rng = np.random.default_rng(42)
|
|
38
39
|
positions = np.array([0.5 + 0.001 * i**2 for i in range(30)])
|
|
39
|
-
positions +=
|
|
40
|
+
positions += rng.normal(0, 0.001, 30) # Small noise
|
|
40
41
|
|
|
41
42
|
outliers = detect_outliers_ransac(
|
|
42
43
|
positions, window_size=15, threshold=0.02, min_inliers=0.7
|
|
@@ -220,8 +221,9 @@ def test_bilateral_temporal_filter_preserves_edges() -> None:
|
|
|
220
221
|
def test_bilateral_temporal_filter_smooths_noise() -> None:
|
|
221
222
|
"""Test that bilateral filter smooths noise within smooth regions."""
|
|
222
223
|
# Noisy flat region
|
|
224
|
+
rng = np.random.default_rng(42)
|
|
223
225
|
positions = np.array([0.5] * 30)
|
|
224
|
-
positions +=
|
|
226
|
+
positions += rng.normal(0, 0.01, 30)
|
|
225
227
|
|
|
226
228
|
filtered = bilateral_temporal_filter(
|
|
227
229
|
positions, window_size=9, sigma_spatial=3.0, sigma_intensity=0.02
|
|
@@ -233,7 +235,8 @@ def test_bilateral_temporal_filter_smooths_noise() -> None:
|
|
|
233
235
|
|
|
234
236
|
def test_bilateral_temporal_filter_window_size() -> None:
|
|
235
237
|
"""Test that bilateral filter handles even window sizes."""
|
|
236
|
-
|
|
238
|
+
rng = np.random.default_rng(42)
|
|
239
|
+
positions = rng.random(50)
|
|
237
240
|
|
|
238
241
|
# Even window size should be adjusted to odd
|
|
239
242
|
filtered_even = bilateral_temporal_filter(
|
|
@@ -284,10 +287,11 @@ def test_smooth_landmarks_advanced_with_bilateral() -> None:
|
|
|
284
287
|
# Create landmark sequence with sharp transition (landing)
|
|
285
288
|
n_frames = 40
|
|
286
289
|
landmark_sequence = []
|
|
290
|
+
rng = np.random.default_rng(42)
|
|
287
291
|
|
|
288
292
|
for i in range(n_frames):
|
|
289
293
|
y = 0.8 if i < 20 else 0.5 # Sharp drop at frame 20
|
|
290
|
-
y +=
|
|
294
|
+
y += rng.normal(0, 0.005) # Add noise
|
|
291
295
|
|
|
292
296
|
landmark_sequence.append(
|
|
293
297
|
{
|
|
@@ -322,10 +326,11 @@ def test_smooth_landmarks_advanced_combined() -> None:
|
|
|
322
326
|
n_frames = 50
|
|
323
327
|
landmark_sequence = []
|
|
324
328
|
|
|
329
|
+
rng = np.random.default_rng(42)
|
|
325
330
|
for i in range(n_frames):
|
|
326
331
|
# Parabolic motion
|
|
327
332
|
y = 0.5 + 0.001 * (i - 25) ** 2
|
|
328
|
-
y +=
|
|
333
|
+
y += rng.normal(0, 0.005) # Noise
|
|
329
334
|
|
|
330
335
|
# Add tracking glitch
|
|
331
336
|
if i == 25:
|
|
@@ -15,10 +15,11 @@ def test_smooth_landmarks_polyorder2_vs_polyorder3() -> None:
|
|
|
15
15
|
n_frames = 30
|
|
16
16
|
landmark_sequence = []
|
|
17
17
|
|
|
18
|
+
rng = np.random.default_rng(42)
|
|
18
19
|
for i in range(n_frames):
|
|
19
20
|
# Smooth parabolic motion with noise
|
|
20
21
|
base_y = 0.5 + 0.01 * (i - 15) ** 2 / 225 # Parabola
|
|
21
|
-
noisy_y = base_y +
|
|
22
|
+
noisy_y = base_y + rng.normal(0, 0.01)
|
|
22
23
|
|
|
23
24
|
landmark_sequence.append(
|
|
24
25
|
{
|
|
@@ -49,9 +50,10 @@ def test_smooth_landmarks_polyorder2_vs_polyorder3() -> None:
|
|
|
49
50
|
def test_velocity_from_derivative_polyorder() -> None:
|
|
50
51
|
"""Test velocity computation with different polynomial orders."""
|
|
51
52
|
# Create synthetic position data with quadratic motion
|
|
53
|
+
rng = np.random.default_rng(42)
|
|
52
54
|
t = np.linspace(0, 2, 60)
|
|
53
55
|
positions = 0.5 + 0.1 * t**2 # Parabolic trajectory
|
|
54
|
-
positions +=
|
|
56
|
+
positions += rng.normal(0, 0.005, len(positions)) # Add noise
|
|
55
57
|
|
|
56
58
|
# Compute velocity with different polynomial orders
|
|
57
59
|
velocity_2 = compute_velocity_from_derivative(
|
|
@@ -79,9 +81,10 @@ def test_velocity_from_derivative_polyorder() -> None:
|
|
|
79
81
|
def test_acceleration_from_derivative_polyorder() -> None:
|
|
80
82
|
"""Test acceleration computation with different polynomial orders."""
|
|
81
83
|
# Create synthetic position data with cubic motion (non-zero third derivative)
|
|
84
|
+
rng = np.random.default_rng(42)
|
|
82
85
|
t = np.linspace(0, 2, 60)
|
|
83
86
|
positions = 0.5 + 0.05 * t**3 # Cubic trajectory
|
|
84
|
-
positions +=
|
|
87
|
+
positions += rng.normal(0, 0.003, len(positions)) # Add noise
|
|
85
88
|
|
|
86
89
|
# Compute acceleration with different polynomial orders
|
|
87
90
|
accel_2 = compute_acceleration_from_derivative(
|
|
@@ -111,7 +114,8 @@ def test_acceleration_from_derivative_polyorder() -> None:
|
|
|
111
114
|
|
|
112
115
|
def test_polyorder_validation() -> None:
|
|
113
116
|
"""Test that polyorder must be less than window_length."""
|
|
114
|
-
|
|
117
|
+
rng = np.random.default_rng(42)
|
|
118
|
+
positions = rng.random(50)
|
|
115
119
|
|
|
116
120
|
# polyorder=2 with window=5 should work (2 < 5)
|
|
117
121
|
velocity_valid = compute_velocity_from_derivative(
|
|
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
|
|
@@ -53,10 +53,10 @@ LABEL org.opencontainers.image.title="kinemotion" \
|
|
|
53
53
|
# - ffmpeg: Video codec support for OpenCV
|
|
54
54
|
# hadolint ignore=DL3008
|
|
55
55
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
56
|
+
ffmpeg \
|
|
56
57
|
libgl1 \
|
|
57
58
|
libglib2.0-0 \
|
|
58
59
|
libgomp1 \
|
|
59
|
-
ffmpeg \
|
|
60
60
|
&& apt-get clean \
|
|
61
61
|
&& rm -rf /var/lib/apt/lists/*
|
|
62
62
|
|
|
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
|