kinemotion 0.44.0__py3-none-any.whl → 0.45.0__py3-none-any.whl

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/cmj/cli.py CHANGED
@@ -1,17 +1,19 @@
1
1
  """Command-line interface for counter movement jump (CMJ) analysis."""
2
2
 
3
- import glob
4
3
  import json
5
4
  import sys
6
5
  from dataclasses import dataclass
7
- from pathlib import Path
8
6
  from typing import Any
9
7
 
10
8
  import click
11
9
 
12
10
  from ..api import process_cmj_video
13
11
  from ..core.auto_tuning import QualityPreset
14
- from ..core.cli_utils import common_output_options
12
+ from ..core.cli_utils import (
13
+ collect_video_files,
14
+ common_output_options,
15
+ generate_batch_output_paths,
16
+ )
15
17
 
16
18
 
17
19
  @dataclass
@@ -27,33 +29,6 @@ class AnalysisParameters:
27
29
  tracking_confidence: float | None = None
28
30
 
29
31
 
30
- def _collect_video_files(video_path: tuple[str, ...]) -> list[str]:
31
- """Expand glob patterns and collect all video files."""
32
- video_files: list[str] = []
33
- for pattern in video_path:
34
- expanded = glob.glob(pattern)
35
- if expanded:
36
- video_files.extend(expanded)
37
- elif Path(pattern).exists():
38
- video_files.append(pattern)
39
- else:
40
- click.echo(f"Warning: No files found for pattern: {pattern}", err=True)
41
- return video_files
42
-
43
-
44
- def _generate_output_paths(
45
- video: str, output_dir: str | None, json_output_dir: str | None
46
- ) -> tuple[str | None, str | None]:
47
- """Generate output paths for debug video and JSON."""
48
- out_path = None
49
- json_path = None
50
- if output_dir:
51
- out_path = str(Path(output_dir) / f"{Path(video).stem}_debug.mp4")
52
- if json_output_dir:
53
- json_path = str(Path(json_output_dir) / f"{Path(video).stem}.json")
54
- return out_path, json_path
55
-
56
-
57
32
  def _process_batch_videos(
58
33
  video_files: list[str],
59
34
  output_dir: str | None,
@@ -74,7 +49,7 @@ def _process_batch_videos(
74
49
  for video in video_files:
75
50
  try:
76
51
  click.echo(f"\nProcessing: {video}", err=True)
77
- out_path, json_path = _generate_output_paths(
52
+ out_path, json_path = generate_batch_output_paths(
78
53
  video, output_dir, json_output_dir
79
54
  )
80
55
  _process_single(
@@ -209,25 +184,25 @@ def cmj_analyze( # NOSONAR(S107) - Click CLI requires individual parameters
209
184
 
210
185
  Examples:
211
186
 
212
- \\b
187
+ \b
213
188
  # Basic analysis
214
189
  kinemotion cmj-analyze video.mp4
215
190
 
216
- \\b
191
+ \b
217
192
  # With debug video output
218
193
  kinemotion cmj-analyze video.mp4 --output debug.mp4
219
194
 
220
- \\b
195
+ \b
221
196
  # Batch mode with glob pattern
222
197
  kinemotion cmj-analyze videos/*.mp4 --batch --workers 4
223
198
 
224
- \\b
199
+ \b
225
200
  # Batch with output directories
226
- kinemotion cmj-analyze videos/*.mp4 --batch \\
201
+ kinemotion cmj-analyze videos/*.mp4 --batch \
227
202
  --json-output-dir results/ --csv-summary summary.csv
228
203
  """
229
204
  # Expand glob patterns and collect all video files
230
- video_files = _collect_video_files(video_path)
205
+ video_files = collect_video_files(video_path)
231
206
 
232
207
  if not video_files:
233
208
  click.echo("Error: No video files found", err=True)
@@ -1,227 +1,11 @@
1
1
  """Shared CLI utilities for drop jump and CMJ analysis."""
2
2
 
3
+ import glob
3
4
  from collections.abc import Callable
4
- from typing import Any, Protocol
5
+ from pathlib import Path
5
6
 
6
7
  import click
7
8
 
8
- from .auto_tuning import AnalysisParameters, QualityPreset, VideoCharacteristics
9
- from .experimental import unused
10
- from .pose import PoseTracker
11
- from .smoothing import smooth_landmarks, smooth_landmarks_advanced
12
- from .video_io import VideoProcessor
13
-
14
-
15
- class ExpertParameters(Protocol):
16
- """Protocol for expert parameter overrides."""
17
-
18
- detection_confidence: float | None
19
- tracking_confidence: float | None
20
- smoothing_window: int | None
21
- velocity_threshold: float | None
22
- min_contact_frames: int | None
23
- visibility_threshold: float | None
24
-
25
-
26
- @unused(
27
- reason="Not called by analysis pipeline - remnant from CLI refactoring",
28
- remove_in="1.0.0",
29
- since="0.34.0",
30
- )
31
- def determine_initial_confidence(
32
- quality_preset: QualityPreset,
33
- expert_params: ExpertParameters,
34
- ) -> tuple[float, float]:
35
- """Determine initial detection and tracking confidence levels.
36
-
37
- Args:
38
- quality_preset: Quality preset enum
39
- expert_params: Expert parameter overrides
40
-
41
- Returns:
42
- Tuple of (detection_confidence, tracking_confidence)
43
- """
44
- initial_detection_conf = 0.5
45
- initial_tracking_conf = 0.5
46
-
47
- if quality_preset == QualityPreset.FAST:
48
- initial_detection_conf = 0.3
49
- initial_tracking_conf = 0.3
50
- elif quality_preset == QualityPreset.ACCURATE:
51
- initial_detection_conf = 0.6
52
- initial_tracking_conf = 0.6
53
-
54
- # Override with expert values if provided
55
- if expert_params.detection_confidence is not None:
56
- initial_detection_conf = expert_params.detection_confidence
57
- if expert_params.tracking_confidence is not None:
58
- initial_tracking_conf = expert_params.tracking_confidence
59
-
60
- return initial_detection_conf, initial_tracking_conf
61
-
62
-
63
- @unused(
64
- reason="Not called by analysis pipeline - remnant from CLI refactoring",
65
- remove_in="1.0.0",
66
- since="0.34.0",
67
- )
68
- def track_all_frames(video: VideoProcessor, tracker: PoseTracker) -> tuple[list, list]:
69
- """Track pose landmarks in all video frames.
70
-
71
- Args:
72
- video: Video processor
73
- tracker: Pose tracker
74
-
75
- Returns:
76
- Tuple of (frames, landmarks_sequence)
77
- """
78
- click.echo("Tracking pose landmarks...", err=True)
79
- landmarks_sequence = []
80
- frames = []
81
-
82
- bar: Any
83
- with click.progressbar(length=video.frame_count, label="Processing frames") as bar:
84
- while True:
85
- frame = video.read_frame()
86
- if frame is None:
87
- break
88
-
89
- frames.append(frame)
90
- landmarks = tracker.process_frame(frame)
91
- landmarks_sequence.append(landmarks)
92
- bar.update(1)
93
-
94
- tracker.close()
95
- return frames, landmarks_sequence
96
-
97
-
98
- @unused(
99
- reason="Not called by analysis pipeline - remnant from CLI refactoring",
100
- remove_in="1.0.0",
101
- since="0.34.0",
102
- )
103
- def apply_expert_param_overrides(
104
- params: AnalysisParameters, expert_params: ExpertParameters
105
- ) -> AnalysisParameters:
106
- """Apply expert parameter overrides to auto-tuned parameters.
107
-
108
- Args:
109
- params: Auto-tuned parameters
110
- expert_params: Expert overrides
111
-
112
- Returns:
113
- Modified params object (mutated in place)
114
- """
115
- if expert_params.smoothing_window is not None:
116
- params.smoothing_window = expert_params.smoothing_window
117
- if expert_params.velocity_threshold is not None:
118
- params.velocity_threshold = expert_params.velocity_threshold
119
- if expert_params.min_contact_frames is not None:
120
- params.min_contact_frames = expert_params.min_contact_frames
121
- if expert_params.visibility_threshold is not None:
122
- params.visibility_threshold = expert_params.visibility_threshold
123
- return params
124
-
125
-
126
- @unused(
127
- reason="Not called by analysis pipeline - remnant from CLI refactoring",
128
- remove_in="1.0.0",
129
- since="0.34.0",
130
- )
131
- def print_auto_tuned_params(
132
- video: VideoProcessor,
133
- quality_preset: QualityPreset,
134
- params: AnalysisParameters,
135
- characteristics: VideoCharacteristics | None = None,
136
- extra_params: dict[str, Any] | None = None,
137
- ) -> None:
138
- """Print auto-tuned parameters in verbose mode.
139
-
140
- Args:
141
- video: Video processor
142
- quality_preset: Quality preset
143
- params: Auto-tuned parameters
144
- characteristics: Optional video characteristics (for tracking quality
145
- display)
146
- extra_params: Optional extra parameters to display (e.g.,
147
- countermovement_threshold)
148
- """
149
- click.echo("\n" + "=" * 60, err=True)
150
- click.echo("AUTO-TUNED PARAMETERS", err=True)
151
- click.echo("=" * 60, err=True)
152
- click.echo(f"Video FPS: {video.fps:.2f}", err=True)
153
-
154
- if characteristics:
155
- click.echo(
156
- f"Tracking quality: {characteristics.tracking_quality} "
157
- f"(avg visibility: {characteristics.avg_visibility:.2f})",
158
- err=True,
159
- )
160
-
161
- click.echo(f"Quality preset: {quality_preset.value}", err=True)
162
- click.echo("\nSelected parameters:", err=True)
163
- click.echo(f" smoothing_window: {params.smoothing_window}", err=True)
164
- click.echo(f" polyorder: {params.polyorder}", err=True)
165
- click.echo(f" velocity_threshold: {params.velocity_threshold:.4f}", err=True)
166
-
167
- # Print extra parameters if provided
168
- if extra_params:
169
- for key, value in extra_params.items():
170
- if isinstance(value, float):
171
- click.echo(f" {key}: {value:.4f}", err=True)
172
- else:
173
- click.echo(f" {key}: {value}", err=True)
174
-
175
- click.echo(f" min_contact_frames: {params.min_contact_frames}", err=True)
176
- click.echo(f" visibility_threshold: {params.visibility_threshold}", err=True)
177
- click.echo(f" detection_confidence: {params.detection_confidence}", err=True)
178
- click.echo(f" tracking_confidence: {params.tracking_confidence}", err=True)
179
- click.echo(f" outlier_rejection: {params.outlier_rejection}", err=True)
180
- click.echo(f" bilateral_filter: {params.bilateral_filter}", err=True)
181
- click.echo(f" use_curvature: {params.use_curvature}", err=True)
182
- click.echo("=" * 60 + "\n", err=True)
183
-
184
-
185
- @unused(
186
- reason="Not called by analysis pipeline - remnant from CLI refactoring",
187
- remove_in="1.0.0",
188
- since="0.34.0",
189
- )
190
- def smooth_landmark_sequence(
191
- landmarks_sequence: list, params: AnalysisParameters
192
- ) -> list:
193
- """Apply smoothing to landmark sequence.
194
-
195
- Args:
196
- landmarks_sequence: Raw landmark sequence
197
- params: Auto-tuned parameters
198
-
199
- Returns:
200
- Smoothed landmark sequence
201
- """
202
- if params.outlier_rejection or params.bilateral_filter:
203
- if params.outlier_rejection:
204
- click.echo("Smoothing landmarks with outlier rejection...", err=True)
205
- if params.bilateral_filter:
206
- click.echo(
207
- "Using bilateral temporal filter for edge-preserving smoothing...",
208
- err=True,
209
- )
210
- return smooth_landmarks_advanced(
211
- landmarks_sequence,
212
- window_length=params.smoothing_window,
213
- polyorder=params.polyorder,
214
- use_outlier_rejection=params.outlier_rejection,
215
- use_bilateral=params.bilateral_filter,
216
- )
217
- else:
218
- click.echo("Smoothing landmarks...", err=True)
219
- return smooth_landmarks(
220
- landmarks_sequence,
221
- window_length=params.smoothing_window,
222
- polyorder=params.polyorder,
223
- )
224
-
225
9
 
226
10
  def common_output_options(func: Callable) -> Callable: # type: ignore[type-arg]
227
11
  """Add common output options to CLI command."""
@@ -238,3 +22,39 @@ def common_output_options(func: Callable) -> Callable: # type: ignore[type-arg]
238
22
  help="Path for JSON metrics output (default: stdout)",
239
23
  )(func)
240
24
  return func
25
+
26
+
27
+ def collect_video_files(video_path: tuple[str, ...]) -> list[str]:
28
+ """Expand glob patterns and collect all video files."""
29
+ video_files: list[str] = []
30
+ for pattern in video_path:
31
+ expanded = glob.glob(pattern)
32
+ if expanded:
33
+ video_files.extend(expanded)
34
+ elif Path(pattern).exists():
35
+ video_files.append(pattern)
36
+ else:
37
+ click.echo(f"Warning: No files found for pattern: {pattern}", err=True)
38
+ return video_files
39
+
40
+
41
+ def generate_batch_output_paths(
42
+ video_path: str, output_dir: str | None, json_output_dir: str | None
43
+ ) -> tuple[str | None, str | None]:
44
+ """Generate output paths for debug video and JSON in batch mode.
45
+
46
+ Args:
47
+ video_path: Path to source video
48
+ output_dir: Directory for debug video output (optional)
49
+ json_output_dir: Directory for JSON metrics output (optional)
50
+
51
+ Returns:
52
+ Tuple of (debug_video_path, json_output_path)
53
+ """
54
+ out_path = None
55
+ json_path = None
56
+ if output_dir:
57
+ out_path = str(Path(output_dir) / f"{Path(video_path).stem}_debug.mp4")
58
+ if json_output_dir:
59
+ json_path = str(Path(json_output_dir) / f"{Path(video_path).stem}.json")
60
+ return out_path, json_path