kinemotion 0.11.2__tar.gz → 0.11.4__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.11.2 → kinemotion-0.11.4}/CHANGELOG.md +19 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/PKG-INFO +1 -1
- {kinemotion-0.11.2 → kinemotion-0.11.4}/pyproject.toml +1 -1
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/api.py +0 -4
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/cmj/cli.py +8 -79
- kinemotion-0.11.4/src/kinemotion/core/cli_utils.py +113 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/dropjump/cli.py +8 -103
- {kinemotion-0.11.2 → kinemotion-0.11.4}/uv.lock +1 -1
- {kinemotion-0.11.2 → kinemotion-0.11.4}/.dockerignore +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/.github/pull_request_template.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/.github/workflows/release.yml +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/.gitignore +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/.pre-commit-config.yaml +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/.tool-versions +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/CLAUDE.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/CODE_OF_CONDUCT.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/CONTRIBUTING.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/Dockerfile +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/GEMINI.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/LICENSE +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/README.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/SECURITY.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/BULK_PROCESSING.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/CAMERA_SETUP.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/CAMERA_SETUP_ES.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/CMJ_GUIDE.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/ERRORS_FINDINGS.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/FRAMERATE.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/IMU_METADATA_PRESERVATION.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/PARAMETERS.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/REAL_TIME_ANALYSIS.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/TRIPLE_EXTENSION.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/docs/VALIDATION_PLAN.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/examples/bulk/README.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/examples/bulk/bulk_processing.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/examples/bulk/simple_example.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/examples/programmatic_usage.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/samples/cmjs/README.md +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/__init__.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/cli.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/cmj/__init__.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/cmj/analysis.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/cmj/debug_overlay.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/cmj/joint_angles.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/cmj/kinematics.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/core/__init__.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/core/auto_tuning.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/core/filtering.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/core/pose.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/core/smoothing.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/core/video_io.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/dropjump/__init__.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/dropjump/analysis.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/dropjump/debug_overlay.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/dropjump/kinematics.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/src/kinemotion/py.typed +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/__init__.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/test_adaptive_threshold.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/test_api.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/test_aspect_ratio.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/test_cmj_analysis.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/test_cmj_kinematics.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/test_com_estimation.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/test_contact_detection.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/test_filtering.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/test_kinematics.py +0 -0
- {kinemotion-0.11.2 → kinemotion-0.11.4}/tests/test_polyorder.py +0 -0
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
<!-- version list -->
|
|
9
9
|
|
|
10
|
+
## v0.11.4 (2025-11-06)
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- **api**: Remove countermovement_threshold from CMJVideoConfig and bulk processing
|
|
15
|
+
([`66ac915`](https://github.com/feniix/kinemotion/commit/66ac915810853b6c7aeca79f07f6470ef5da4041))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## v0.11.3 (2025-11-06)
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
- Deduplicate CLI utilities across CMJ and drop jump modules
|
|
23
|
+
([`c314083`](https://github.com/feniix/kinemotion/commit/c314083dd6601071f75ded38864f7ba9a9daab3d))
|
|
24
|
+
|
|
25
|
+
- **cmj**: Remove unused countermovement_threshold parameter from process_cmj_video
|
|
26
|
+
([`a8d9425`](https://github.com/feniix/kinemotion/commit/a8d9425a509b44ccf5c9e983e2d8552e9b5f8839))
|
|
27
|
+
|
|
28
|
+
|
|
10
29
|
## v0.11.2 (2025-11-06)
|
|
11
30
|
|
|
12
31
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kinemotion
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.4
|
|
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
|
|
@@ -620,7 +620,6 @@ class CMJVideoConfig:
|
|
|
620
620
|
json_output: str | None = None
|
|
621
621
|
smoothing_window: int | None = None
|
|
622
622
|
velocity_threshold: float | None = None
|
|
623
|
-
countermovement_threshold: float | None = None
|
|
624
623
|
min_contact_frames: int | None = None
|
|
625
624
|
visibility_threshold: float | None = None
|
|
626
625
|
detection_confidence: float | None = None
|
|
@@ -689,7 +688,6 @@ def process_cmj_video(
|
|
|
689
688
|
json_output: str | None = None,
|
|
690
689
|
smoothing_window: int | None = None,
|
|
691
690
|
velocity_threshold: float | None = None,
|
|
692
|
-
countermovement_threshold: float | None = None,
|
|
693
691
|
min_contact_frames: int | None = None,
|
|
694
692
|
visibility_threshold: float | None = None,
|
|
695
693
|
detection_confidence: float | None = None,
|
|
@@ -710,7 +708,6 @@ def process_cmj_video(
|
|
|
710
708
|
json_output: Optional path for JSON metrics output
|
|
711
709
|
smoothing_window: Optional override for smoothing window
|
|
712
710
|
velocity_threshold: Optional override for velocity threshold
|
|
713
|
-
countermovement_threshold: Optional override for countermovement threshold
|
|
714
711
|
min_contact_frames: Optional override for minimum contact frames
|
|
715
712
|
visibility_threshold: Optional override for visibility threshold
|
|
716
713
|
detection_confidence: Optional override for pose detection confidence
|
|
@@ -929,7 +926,6 @@ def _process_cmj_video_wrapper(config: CMJVideoConfig) -> CMJVideoResult:
|
|
|
929
926
|
json_output=config.json_output,
|
|
930
927
|
smoothing_window=config.smoothing_window,
|
|
931
928
|
velocity_threshold=config.velocity_threshold,
|
|
932
|
-
countermovement_threshold=config.countermovement_threshold,
|
|
933
929
|
min_contact_frames=config.min_contact_frames,
|
|
934
930
|
visibility_threshold=config.visibility_threshold,
|
|
935
931
|
detection_confidence=config.detection_confidence,
|
|
@@ -18,8 +18,12 @@ from ..core.auto_tuning import (
|
|
|
18
18
|
analyze_video_sample,
|
|
19
19
|
auto_tune_parameters,
|
|
20
20
|
)
|
|
21
|
+
from ..core.cli_utils import (
|
|
22
|
+
determine_initial_confidence,
|
|
23
|
+
smooth_landmark_sequence,
|
|
24
|
+
track_all_frames,
|
|
25
|
+
)
|
|
21
26
|
from ..core.pose import PoseTracker
|
|
22
|
-
from ..core.smoothing import smooth_landmarks, smooth_landmarks_advanced
|
|
23
27
|
from ..core.video_io import VideoProcessor
|
|
24
28
|
from .analysis import detect_cmj_phases
|
|
25
29
|
from .debug_overlay import CMJDebugOverlayRenderer
|
|
@@ -295,53 +299,6 @@ def cmj_analyze( # NOSONAR(S107) - Click CLI requires individual parameters for
|
|
|
295
299
|
sys.exit(1)
|
|
296
300
|
|
|
297
301
|
|
|
298
|
-
def _determine_initial_confidence(
|
|
299
|
-
quality_preset: QualityPreset,
|
|
300
|
-
expert_params: AnalysisParameters,
|
|
301
|
-
) -> tuple[float, float]:
|
|
302
|
-
"""Determine initial detection and tracking confidence levels."""
|
|
303
|
-
initial_detection_conf = 0.5
|
|
304
|
-
initial_tracking_conf = 0.5
|
|
305
|
-
|
|
306
|
-
if quality_preset == QualityPreset.FAST:
|
|
307
|
-
initial_detection_conf = 0.3
|
|
308
|
-
initial_tracking_conf = 0.3
|
|
309
|
-
elif quality_preset == QualityPreset.ACCURATE:
|
|
310
|
-
initial_detection_conf = 0.6
|
|
311
|
-
initial_tracking_conf = 0.6
|
|
312
|
-
|
|
313
|
-
# Override with expert values if provided
|
|
314
|
-
if expert_params.detection_confidence is not None:
|
|
315
|
-
initial_detection_conf = expert_params.detection_confidence
|
|
316
|
-
if expert_params.tracking_confidence is not None:
|
|
317
|
-
initial_tracking_conf = expert_params.tracking_confidence
|
|
318
|
-
|
|
319
|
-
return initial_detection_conf, initial_tracking_conf
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
def _track_all_frames(video: VideoProcessor, tracker: PoseTracker) -> tuple[list, list]:
|
|
323
|
-
"""Track pose landmarks in all video frames."""
|
|
324
|
-
click.echo("Tracking pose landmarks...", err=True)
|
|
325
|
-
landmarks_sequence = []
|
|
326
|
-
frames = []
|
|
327
|
-
|
|
328
|
-
bar: Any
|
|
329
|
-
with click.progressbar(length=video.frame_count, label="Processing frames") as bar:
|
|
330
|
-
while True:
|
|
331
|
-
frame = video.read_frame()
|
|
332
|
-
if frame is None:
|
|
333
|
-
break
|
|
334
|
-
|
|
335
|
-
frames.append(frame)
|
|
336
|
-
landmarks = tracker.process_frame(frame)
|
|
337
|
-
landmarks_sequence.append(landmarks)
|
|
338
|
-
|
|
339
|
-
bar.update(1)
|
|
340
|
-
|
|
341
|
-
tracker.close()
|
|
342
|
-
return frames, landmarks_sequence
|
|
343
|
-
|
|
344
|
-
|
|
345
302
|
def _apply_expert_param_overrides(
|
|
346
303
|
params: AutoTunedParams, expert_params: AnalysisParameters
|
|
347
304
|
) -> AutoTunedParams:
|
|
@@ -386,34 +343,6 @@ def _print_auto_tuned_params(
|
|
|
386
343
|
click.echo("=" * 60 + "\n", err=True)
|
|
387
344
|
|
|
388
345
|
|
|
389
|
-
def _smooth_landmark_sequence(
|
|
390
|
-
landmarks_sequence: list, params: AutoTunedParams
|
|
391
|
-
) -> list:
|
|
392
|
-
"""Apply smoothing to landmark sequence."""
|
|
393
|
-
if params.outlier_rejection or params.bilateral_filter:
|
|
394
|
-
if params.outlier_rejection:
|
|
395
|
-
click.echo("Smoothing landmarks with outlier rejection...", err=True)
|
|
396
|
-
if params.bilateral_filter:
|
|
397
|
-
click.echo(
|
|
398
|
-
"Using bilateral temporal filter for edge-preserving smoothing...",
|
|
399
|
-
err=True,
|
|
400
|
-
)
|
|
401
|
-
return smooth_landmarks_advanced(
|
|
402
|
-
landmarks_sequence,
|
|
403
|
-
window_length=params.smoothing_window,
|
|
404
|
-
polyorder=params.polyorder,
|
|
405
|
-
use_outlier_rejection=params.outlier_rejection,
|
|
406
|
-
use_bilateral=params.bilateral_filter,
|
|
407
|
-
)
|
|
408
|
-
else:
|
|
409
|
-
click.echo("Smoothing landmarks...", err=True)
|
|
410
|
-
return smooth_landmarks(
|
|
411
|
-
landmarks_sequence,
|
|
412
|
-
window_length=params.smoothing_window,
|
|
413
|
-
polyorder=params.polyorder,
|
|
414
|
-
)
|
|
415
|
-
|
|
416
|
-
|
|
417
346
|
def _get_foot_position(frame_landmarks: dict | None, last_position: float) -> float:
|
|
418
347
|
"""Extract average foot position from frame landmarks."""
|
|
419
348
|
if not frame_landmarks:
|
|
@@ -470,7 +399,7 @@ def _process_single(
|
|
|
470
399
|
)
|
|
471
400
|
|
|
472
401
|
# Determine confidence levels
|
|
473
|
-
detection_conf, tracking_conf =
|
|
402
|
+
detection_conf, tracking_conf = determine_initial_confidence(
|
|
474
403
|
quality_preset, expert_params
|
|
475
404
|
)
|
|
476
405
|
|
|
@@ -479,7 +408,7 @@ def _process_single(
|
|
|
479
408
|
min_detection_confidence=detection_conf,
|
|
480
409
|
min_tracking_confidence=tracking_conf,
|
|
481
410
|
)
|
|
482
|
-
frames, landmarks_sequence =
|
|
411
|
+
frames, landmarks_sequence = track_all_frames(video, tracker)
|
|
483
412
|
|
|
484
413
|
if not landmarks_sequence:
|
|
485
414
|
click.echo("Error: No frames processed", err=True)
|
|
@@ -505,7 +434,7 @@ def _process_single(
|
|
|
505
434
|
)
|
|
506
435
|
|
|
507
436
|
# Apply smoothing
|
|
508
|
-
smoothed_landmarks =
|
|
437
|
+
smoothed_landmarks = smooth_landmark_sequence(landmarks_sequence, params)
|
|
509
438
|
|
|
510
439
|
# Extract foot positions
|
|
511
440
|
vertical_positions, tracking_method = _extract_positions_from_landmarks(
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Shared CLI utilities for drop jump and CMJ analysis."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Protocol
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from .auto_tuning import AutoTunedParams, QualityPreset
|
|
8
|
+
from .pose import PoseTracker
|
|
9
|
+
from .smoothing import smooth_landmarks, smooth_landmarks_advanced
|
|
10
|
+
from .video_io import VideoProcessor
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ExpertParameters(Protocol):
|
|
14
|
+
"""Protocol for expert parameter overrides."""
|
|
15
|
+
|
|
16
|
+
detection_confidence: float | None
|
|
17
|
+
tracking_confidence: float | None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def determine_initial_confidence(
|
|
21
|
+
quality_preset: QualityPreset,
|
|
22
|
+
expert_params: ExpertParameters,
|
|
23
|
+
) -> tuple[float, float]:
|
|
24
|
+
"""Determine initial detection and tracking confidence levels.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
quality_preset: Quality preset enum
|
|
28
|
+
expert_params: Expert parameter overrides
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Tuple of (detection_confidence, tracking_confidence)
|
|
32
|
+
"""
|
|
33
|
+
initial_detection_conf = 0.5
|
|
34
|
+
initial_tracking_conf = 0.5
|
|
35
|
+
|
|
36
|
+
if quality_preset == QualityPreset.FAST:
|
|
37
|
+
initial_detection_conf = 0.3
|
|
38
|
+
initial_tracking_conf = 0.3
|
|
39
|
+
elif quality_preset == QualityPreset.ACCURATE:
|
|
40
|
+
initial_detection_conf = 0.6
|
|
41
|
+
initial_tracking_conf = 0.6
|
|
42
|
+
|
|
43
|
+
# Override with expert values if provided
|
|
44
|
+
if expert_params.detection_confidence is not None:
|
|
45
|
+
initial_detection_conf = expert_params.detection_confidence
|
|
46
|
+
if expert_params.tracking_confidence is not None:
|
|
47
|
+
initial_tracking_conf = expert_params.tracking_confidence
|
|
48
|
+
|
|
49
|
+
return initial_detection_conf, initial_tracking_conf
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def track_all_frames(video: VideoProcessor, tracker: PoseTracker) -> tuple[list, list]:
|
|
53
|
+
"""Track pose landmarks in all video frames.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
video: Video processor
|
|
57
|
+
tracker: Pose tracker
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Tuple of (frames, landmarks_sequence)
|
|
61
|
+
"""
|
|
62
|
+
click.echo("Tracking pose landmarks...", err=True)
|
|
63
|
+
landmarks_sequence = []
|
|
64
|
+
frames = []
|
|
65
|
+
|
|
66
|
+
bar: Any
|
|
67
|
+
with click.progressbar(length=video.frame_count, label="Processing frames") as bar:
|
|
68
|
+
while True:
|
|
69
|
+
frame = video.read_frame()
|
|
70
|
+
if frame is None:
|
|
71
|
+
break
|
|
72
|
+
|
|
73
|
+
frames.append(frame)
|
|
74
|
+
landmarks = tracker.process_frame(frame)
|
|
75
|
+
landmarks_sequence.append(landmarks)
|
|
76
|
+
bar.update(1)
|
|
77
|
+
|
|
78
|
+
tracker.close()
|
|
79
|
+
return frames, landmarks_sequence
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def smooth_landmark_sequence(landmarks_sequence: list, params: AutoTunedParams) -> list:
|
|
83
|
+
"""Apply smoothing to landmark sequence.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
landmarks_sequence: Raw landmark sequence
|
|
87
|
+
params: Auto-tuned parameters
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Smoothed landmark sequence
|
|
91
|
+
"""
|
|
92
|
+
if params.outlier_rejection or params.bilateral_filter:
|
|
93
|
+
if params.outlier_rejection:
|
|
94
|
+
click.echo("Smoothing landmarks with outlier rejection...", err=True)
|
|
95
|
+
if params.bilateral_filter:
|
|
96
|
+
click.echo(
|
|
97
|
+
"Using bilateral temporal filter for edge-preserving smoothing...",
|
|
98
|
+
err=True,
|
|
99
|
+
)
|
|
100
|
+
return smooth_landmarks_advanced(
|
|
101
|
+
landmarks_sequence,
|
|
102
|
+
window_length=params.smoothing_window,
|
|
103
|
+
polyorder=params.polyorder,
|
|
104
|
+
use_outlier_rejection=params.outlier_rejection,
|
|
105
|
+
use_bilateral=params.bilateral_filter,
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
click.echo("Smoothing landmarks...", err=True)
|
|
109
|
+
return smooth_landmarks(
|
|
110
|
+
landmarks_sequence,
|
|
111
|
+
window_length=params.smoothing_window,
|
|
112
|
+
polyorder=params.polyorder,
|
|
113
|
+
)
|
|
@@ -21,8 +21,12 @@ from ..core.auto_tuning import (
|
|
|
21
21
|
analyze_video_sample,
|
|
22
22
|
auto_tune_parameters,
|
|
23
23
|
)
|
|
24
|
+
from ..core.cli_utils import (
|
|
25
|
+
determine_initial_confidence,
|
|
26
|
+
smooth_landmark_sequence,
|
|
27
|
+
track_all_frames,
|
|
28
|
+
)
|
|
24
29
|
from ..core.pose import PoseTracker
|
|
25
|
-
from ..core.smoothing import smooth_landmarks, smooth_landmarks_advanced
|
|
26
30
|
from ..core.video_io import VideoProcessor
|
|
27
31
|
from .analysis import (
|
|
28
32
|
ContactState,
|
|
@@ -256,69 +260,6 @@ def dropjump_analyze( # NOSONAR(S107) - Click CLI requires individual parameter
|
|
|
256
260
|
)
|
|
257
261
|
|
|
258
262
|
|
|
259
|
-
def _determine_initial_confidence(
|
|
260
|
-
quality_preset: QualityPreset,
|
|
261
|
-
expert_params: AnalysisParameters,
|
|
262
|
-
) -> tuple[float, float]:
|
|
263
|
-
"""Determine initial detection and tracking confidence levels.
|
|
264
|
-
|
|
265
|
-
Args:
|
|
266
|
-
quality_preset: Quality preset enum
|
|
267
|
-
expert_params: Expert parameter overrides
|
|
268
|
-
|
|
269
|
-
Returns:
|
|
270
|
-
Tuple of (detection_confidence, tracking_confidence)
|
|
271
|
-
"""
|
|
272
|
-
initial_detection_conf = 0.5
|
|
273
|
-
initial_tracking_conf = 0.5
|
|
274
|
-
|
|
275
|
-
if quality_preset == QualityPreset.FAST:
|
|
276
|
-
initial_detection_conf = 0.3
|
|
277
|
-
initial_tracking_conf = 0.3
|
|
278
|
-
elif quality_preset == QualityPreset.ACCURATE:
|
|
279
|
-
initial_detection_conf = 0.6
|
|
280
|
-
initial_tracking_conf = 0.6
|
|
281
|
-
|
|
282
|
-
# Override with expert values if provided
|
|
283
|
-
if expert_params.detection_confidence is not None:
|
|
284
|
-
initial_detection_conf = expert_params.detection_confidence
|
|
285
|
-
if expert_params.tracking_confidence is not None:
|
|
286
|
-
initial_tracking_conf = expert_params.tracking_confidence
|
|
287
|
-
|
|
288
|
-
return initial_detection_conf, initial_tracking_conf
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
def _track_all_frames(video: VideoProcessor, tracker: PoseTracker) -> tuple[list, list]:
|
|
292
|
-
"""Track pose landmarks in all video frames.
|
|
293
|
-
|
|
294
|
-
Args:
|
|
295
|
-
video: Video processor
|
|
296
|
-
tracker: Pose tracker
|
|
297
|
-
|
|
298
|
-
Returns:
|
|
299
|
-
Tuple of (frames, landmarks_sequence)
|
|
300
|
-
"""
|
|
301
|
-
click.echo("Tracking pose landmarks...", err=True)
|
|
302
|
-
landmarks_sequence = []
|
|
303
|
-
frames = []
|
|
304
|
-
|
|
305
|
-
bar: Any
|
|
306
|
-
with click.progressbar(length=video.frame_count, label="Processing frames") as bar:
|
|
307
|
-
while True:
|
|
308
|
-
frame = video.read_frame()
|
|
309
|
-
if frame is None:
|
|
310
|
-
break
|
|
311
|
-
|
|
312
|
-
frames.append(frame)
|
|
313
|
-
landmarks = tracker.process_frame(frame)
|
|
314
|
-
landmarks_sequence.append(landmarks)
|
|
315
|
-
|
|
316
|
-
bar.update(1)
|
|
317
|
-
|
|
318
|
-
tracker.close()
|
|
319
|
-
return frames, landmarks_sequence
|
|
320
|
-
|
|
321
|
-
|
|
322
263
|
def _apply_expert_param_overrides(
|
|
323
264
|
params: AutoTunedParams, expert_params: AnalysisParameters
|
|
324
265
|
) -> AutoTunedParams:
|
|
@@ -380,42 +321,6 @@ def _print_auto_tuned_params(
|
|
|
380
321
|
click.echo("=" * 60 + "\n", err=True)
|
|
381
322
|
|
|
382
323
|
|
|
383
|
-
def _smooth_landmark_sequence(
|
|
384
|
-
landmarks_sequence: list, params: AutoTunedParams
|
|
385
|
-
) -> list:
|
|
386
|
-
"""Apply smoothing to landmark sequence.
|
|
387
|
-
|
|
388
|
-
Args:
|
|
389
|
-
landmarks_sequence: Raw landmark sequence
|
|
390
|
-
params: Auto-tuned parameters
|
|
391
|
-
|
|
392
|
-
Returns:
|
|
393
|
-
Smoothed landmarks
|
|
394
|
-
"""
|
|
395
|
-
if params.outlier_rejection or params.bilateral_filter:
|
|
396
|
-
if params.outlier_rejection:
|
|
397
|
-
click.echo("Smoothing landmarks with outlier rejection...", err=True)
|
|
398
|
-
if params.bilateral_filter:
|
|
399
|
-
click.echo(
|
|
400
|
-
"Using bilateral temporal filter for edge-preserving smoothing...",
|
|
401
|
-
err=True,
|
|
402
|
-
)
|
|
403
|
-
return smooth_landmarks_advanced(
|
|
404
|
-
landmarks_sequence,
|
|
405
|
-
window_length=params.smoothing_window,
|
|
406
|
-
polyorder=params.polyorder,
|
|
407
|
-
use_outlier_rejection=params.outlier_rejection,
|
|
408
|
-
use_bilateral=params.bilateral_filter,
|
|
409
|
-
)
|
|
410
|
-
else:
|
|
411
|
-
click.echo("Smoothing landmarks...", err=True)
|
|
412
|
-
return smooth_landmarks(
|
|
413
|
-
landmarks_sequence,
|
|
414
|
-
window_length=params.smoothing_window,
|
|
415
|
-
polyorder=params.polyorder,
|
|
416
|
-
)
|
|
417
|
-
|
|
418
|
-
|
|
419
324
|
def _extract_positions_and_visibilities(
|
|
420
325
|
smoothed_landmarks: list,
|
|
421
326
|
) -> tuple[np.ndarray, np.ndarray]:
|
|
@@ -533,7 +438,7 @@ def _process_single(
|
|
|
533
438
|
)
|
|
534
439
|
|
|
535
440
|
# Determine confidence levels
|
|
536
|
-
detection_conf, tracking_conf =
|
|
441
|
+
detection_conf, tracking_conf = determine_initial_confidence(
|
|
537
442
|
quality_preset, expert_params
|
|
538
443
|
)
|
|
539
444
|
|
|
@@ -542,7 +447,7 @@ def _process_single(
|
|
|
542
447
|
min_detection_confidence=detection_conf,
|
|
543
448
|
min_tracking_confidence=tracking_conf,
|
|
544
449
|
)
|
|
545
|
-
frames, landmarks_sequence =
|
|
450
|
+
frames, landmarks_sequence = track_all_frames(video, tracker)
|
|
546
451
|
|
|
547
452
|
if not landmarks_sequence:
|
|
548
453
|
click.echo("Error: No frames processed", err=True)
|
|
@@ -560,7 +465,7 @@ def _process_single(
|
|
|
560
465
|
_print_auto_tuned_params(video, characteristics, quality_preset, params)
|
|
561
466
|
|
|
562
467
|
# Apply smoothing
|
|
563
|
-
smoothed_landmarks =
|
|
468
|
+
smoothed_landmarks = smooth_landmark_sequence(landmarks_sequence, params)
|
|
564
469
|
|
|
565
470
|
# Extract positions
|
|
566
471
|
vertical_positions, visibilities = _extract_positions_and_visibilities(
|
|
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
|