kinemotion 0.10.6__py3-none-any.whl → 0.67.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.

Files changed (48) hide show
  1. kinemotion/__init__.py +31 -6
  2. kinemotion/api.py +39 -598
  3. kinemotion/cli.py +2 -0
  4. kinemotion/cmj/__init__.py +5 -0
  5. kinemotion/cmj/analysis.py +621 -0
  6. kinemotion/cmj/api.py +563 -0
  7. kinemotion/cmj/cli.py +324 -0
  8. kinemotion/cmj/debug_overlay.py +457 -0
  9. kinemotion/cmj/joint_angles.py +307 -0
  10. kinemotion/cmj/kinematics.py +360 -0
  11. kinemotion/cmj/metrics_validator.py +767 -0
  12. kinemotion/cmj/validation_bounds.py +341 -0
  13. kinemotion/core/__init__.py +28 -0
  14. kinemotion/core/auto_tuning.py +71 -37
  15. kinemotion/core/cli_utils.py +60 -0
  16. kinemotion/core/debug_overlay_utils.py +385 -0
  17. kinemotion/core/determinism.py +83 -0
  18. kinemotion/core/experimental.py +103 -0
  19. kinemotion/core/filtering.py +9 -6
  20. kinemotion/core/formatting.py +75 -0
  21. kinemotion/core/metadata.py +231 -0
  22. kinemotion/core/model_downloader.py +172 -0
  23. kinemotion/core/pipeline_utils.py +433 -0
  24. kinemotion/core/pose.py +298 -141
  25. kinemotion/core/pose_landmarks.py +67 -0
  26. kinemotion/core/quality.py +393 -0
  27. kinemotion/core/smoothing.py +250 -154
  28. kinemotion/core/timing.py +247 -0
  29. kinemotion/core/types.py +42 -0
  30. kinemotion/core/validation.py +201 -0
  31. kinemotion/core/video_io.py +135 -50
  32. kinemotion/dropjump/__init__.py +1 -1
  33. kinemotion/dropjump/analysis.py +367 -182
  34. kinemotion/dropjump/api.py +665 -0
  35. kinemotion/dropjump/cli.py +156 -466
  36. kinemotion/dropjump/debug_overlay.py +136 -206
  37. kinemotion/dropjump/kinematics.py +232 -255
  38. kinemotion/dropjump/metrics_validator.py +240 -0
  39. kinemotion/dropjump/validation_bounds.py +157 -0
  40. kinemotion/models/__init__.py +0 -0
  41. kinemotion/models/pose_landmarker_lite.task +0 -0
  42. kinemotion-0.67.0.dist-info/METADATA +726 -0
  43. kinemotion-0.67.0.dist-info/RECORD +47 -0
  44. {kinemotion-0.10.6.dist-info → kinemotion-0.67.0.dist-info}/WHEEL +1 -1
  45. kinemotion-0.10.6.dist-info/METADATA +0 -561
  46. kinemotion-0.10.6.dist-info/RECORD +0 -20
  47. {kinemotion-0.10.6.dist-info → kinemotion-0.67.0.dist-info}/entry_points.txt +0 -0
  48. {kinemotion-0.10.6.dist-info → kinemotion-0.67.0.dist-info}/licenses/LICENSE +0 -0
kinemotion/cmj/cli.py ADDED
@@ -0,0 +1,324 @@
1
+ """Command-line interface for counter movement jump (CMJ) analysis."""
2
+
3
+ import json
4
+ import sys
5
+ from dataclasses import dataclass
6
+
7
+ import click
8
+
9
+ from ..core.auto_tuning import QualityPreset
10
+ from ..core.cli_utils import (
11
+ collect_video_files,
12
+ common_output_options,
13
+ generate_batch_output_paths,
14
+ )
15
+ from .api import AnalysisOverrides, process_cmj_video
16
+ from .kinematics import CMJMetrics
17
+
18
+
19
+ @dataclass
20
+ class AnalysisParameters:
21
+ """Expert parameters for CMJ analysis customization."""
22
+
23
+ smoothing_window: int | None = None
24
+ velocity_threshold: float | None = None
25
+ countermovement_threshold: float | None = None
26
+ min_contact_frames: int | None = None
27
+ visibility_threshold: float | None = None
28
+ detection_confidence: float | None = None
29
+ tracking_confidence: float | None = None
30
+
31
+
32
+ def _process_batch_videos(
33
+ video_files: list[str],
34
+ output_dir: str | None,
35
+ json_output_dir: str | None,
36
+ quality_preset: QualityPreset,
37
+ verbose: bool,
38
+ expert_params: AnalysisParameters,
39
+ workers: int,
40
+ ) -> None:
41
+ """Process multiple videos in batch mode."""
42
+ click.echo(
43
+ f"Batch mode: Processing {len(video_files)} video(s) with {workers} workers",
44
+ err=True,
45
+ )
46
+ click.echo("Note: Batch processing not yet fully implemented", err=True)
47
+ click.echo("Processing videos sequentially...", err=True)
48
+
49
+ for video in video_files:
50
+ try:
51
+ click.echo(f"\nProcessing: {video}", err=True)
52
+ out_path, json_path = generate_batch_output_paths(video, output_dir, json_output_dir)
53
+ _process_single(video, out_path, json_path, quality_preset, verbose, expert_params)
54
+ except Exception as e:
55
+ click.echo(f"Error processing {video}: {e}", err=True)
56
+ continue
57
+
58
+
59
+ @click.command(name="cmj-analyze")
60
+ @click.argument("video_path", nargs=-1, type=click.Path(exists=False), required=True)
61
+ @common_output_options
62
+ @click.option(
63
+ "--quality",
64
+ type=click.Choice(["fast", "balanced", "accurate"], case_sensitive=False),
65
+ default="balanced",
66
+ help=(
67
+ "Analysis quality preset: "
68
+ "fast (quick, less precise), "
69
+ "balanced (default, good for most cases), "
70
+ "accurate (research-grade, slower)"
71
+ ),
72
+ show_default=True,
73
+ )
74
+ @click.option(
75
+ "--verbose",
76
+ "-v",
77
+ is_flag=True,
78
+ help="Show auto-selected parameters and analysis details",
79
+ )
80
+ # Batch processing options
81
+ @click.option(
82
+ "--batch",
83
+ is_flag=True,
84
+ help="Enable batch processing mode for multiple videos",
85
+ )
86
+ @click.option(
87
+ "--workers",
88
+ type=int,
89
+ default=4,
90
+ help="Number of parallel workers for batch processing (default: 4)",
91
+ show_default=True,
92
+ )
93
+ @click.option(
94
+ "--output-dir",
95
+ type=click.Path(),
96
+ help="Directory for debug video outputs (batch mode only)",
97
+ )
98
+ @click.option(
99
+ "--json-output-dir",
100
+ type=click.Path(),
101
+ help="Directory for JSON metrics outputs (batch mode only)",
102
+ )
103
+ @click.option(
104
+ "--csv-summary",
105
+ type=click.Path(),
106
+ help="Path for CSV summary export (batch mode only)",
107
+ )
108
+ # Expert parameters (hidden in help, but always available for advanced users)
109
+ @click.option(
110
+ "--smoothing-window",
111
+ type=int,
112
+ default=None,
113
+ help="[EXPERT] Override auto-tuned smoothing window size",
114
+ )
115
+ @click.option(
116
+ "--velocity-threshold",
117
+ type=float,
118
+ default=None,
119
+ help="[EXPERT] Override auto-tuned velocity threshold for flight detection",
120
+ )
121
+ @click.option(
122
+ "--countermovement-threshold",
123
+ type=float,
124
+ default=None,
125
+ help="[EXPERT] Override auto-tuned countermovement threshold (negative value)",
126
+ )
127
+ @click.option(
128
+ "--min-contact-frames",
129
+ type=int,
130
+ default=None,
131
+ help="[EXPERT] Override auto-tuned minimum contact frames",
132
+ )
133
+ @click.option(
134
+ "--visibility-threshold",
135
+ type=float,
136
+ default=None,
137
+ help="[EXPERT] Override visibility threshold",
138
+ )
139
+ @click.option(
140
+ "--detection-confidence",
141
+ type=float,
142
+ default=None,
143
+ help="[EXPERT] Override pose detection confidence",
144
+ )
145
+ @click.option(
146
+ "--tracking-confidence",
147
+ type=float,
148
+ default=None,
149
+ help="[EXPERT] Override pose tracking confidence",
150
+ )
151
+ def cmj_analyze( # NOSONAR(S107) - Click CLI requires individual parameters
152
+ # for each option
153
+ video_path: tuple[str, ...],
154
+ output: str | None,
155
+ json_output: str | None,
156
+ quality: str,
157
+ verbose: bool,
158
+ batch: bool,
159
+ workers: int,
160
+ output_dir: str | None,
161
+ json_output_dir: str | None,
162
+ csv_summary: str | None,
163
+ smoothing_window: int | None,
164
+ velocity_threshold: float | None,
165
+ countermovement_threshold: float | None,
166
+ min_contact_frames: int | None,
167
+ visibility_threshold: float | None,
168
+ detection_confidence: float | None,
169
+ tracking_confidence: float | None,
170
+ ) -> None:
171
+ """
172
+ Analyze counter movement jump (CMJ) video(s) to estimate jump performance
173
+ metrics.
174
+
175
+ Uses intelligent auto-tuning to select optimal parameters based on video
176
+ characteristics. Parameters are automatically adjusted for frame rate,
177
+ tracking quality, and analysis preset.
178
+
179
+ VIDEO_PATH: Path(s) to video file(s). Supports glob patterns in batch mode.
180
+
181
+ Examples:
182
+
183
+ \b
184
+ # Basic analysis
185
+ kinemotion cmj-analyze video.mp4
186
+
187
+ \b
188
+ # With debug video output
189
+ kinemotion cmj-analyze video.mp4 --output debug.mp4
190
+
191
+ \b
192
+ # Batch mode with glob pattern
193
+ kinemotion cmj-analyze videos/*.mp4 --batch --workers 4
194
+
195
+ \b
196
+ # Batch with output directories
197
+ kinemotion cmj-analyze videos/*.mp4 --batch \
198
+ --json-output-dir results/ --csv-summary summary.csv
199
+ """
200
+ # Expand glob patterns and collect all video files
201
+ video_files = collect_video_files(video_path)
202
+
203
+ if not video_files:
204
+ click.echo("Error: No video files found", err=True)
205
+ sys.exit(1)
206
+
207
+ # Determine if batch mode should be used
208
+ use_batch = batch or len(video_files) > 1
209
+
210
+ quality_preset = QualityPreset(quality.lower())
211
+
212
+ # Group expert parameters
213
+ expert_params = AnalysisParameters(
214
+ smoothing_window=smoothing_window,
215
+ velocity_threshold=velocity_threshold,
216
+ countermovement_threshold=countermovement_threshold,
217
+ min_contact_frames=min_contact_frames,
218
+ visibility_threshold=visibility_threshold,
219
+ detection_confidence=detection_confidence,
220
+ tracking_confidence=tracking_confidence,
221
+ )
222
+
223
+ if use_batch:
224
+ _process_batch_videos(
225
+ video_files,
226
+ output_dir,
227
+ json_output_dir,
228
+ quality_preset,
229
+ verbose,
230
+ expert_params,
231
+ workers,
232
+ )
233
+ else:
234
+ # Single video mode
235
+ try:
236
+ _process_single(
237
+ video_files[0],
238
+ output,
239
+ json_output,
240
+ quality_preset,
241
+ verbose,
242
+ expert_params,
243
+ )
244
+ except Exception as e:
245
+ click.echo(f"Error: {e}", err=True)
246
+ sys.exit(1)
247
+
248
+
249
+ def _process_single(
250
+ video_path: str,
251
+ output: str | None,
252
+ json_output: str | None,
253
+ quality_preset: QualityPreset,
254
+ verbose: bool,
255
+ expert_params: AnalysisParameters,
256
+ ) -> None:
257
+ """Process a single CMJ video by calling the API."""
258
+ try:
259
+ # Create overrides from expert parameters
260
+ overrides = AnalysisOverrides(
261
+ smoothing_window=expert_params.smoothing_window,
262
+ velocity_threshold=expert_params.velocity_threshold,
263
+ min_contact_frames=expert_params.min_contact_frames,
264
+ visibility_threshold=expert_params.visibility_threshold,
265
+ )
266
+
267
+ # Call the API function (handles all processing logic)
268
+ metrics = process_cmj_video(
269
+ video_path=video_path,
270
+ quality=quality_preset.value,
271
+ output_video=output,
272
+ json_output=json_output,
273
+ overrides=overrides,
274
+ detection_confidence=expert_params.detection_confidence,
275
+ tracking_confidence=expert_params.tracking_confidence,
276
+ verbose=verbose,
277
+ )
278
+
279
+ # Print formatted summary to stdout
280
+ _output_results(metrics, json_output=None) # Don't write JSON (API already did)
281
+
282
+ except Exception as e:
283
+ click.echo(f"Error processing video: {e}", err=True)
284
+ if verbose:
285
+ import traceback
286
+
287
+ traceback.print_exc()
288
+ sys.exit(1)
289
+
290
+
291
+ def _output_results(metrics: CMJMetrics, json_output: str | None) -> None:
292
+ """Output analysis results."""
293
+ results = metrics.to_dict()
294
+
295
+ # Output JSON
296
+ if json_output:
297
+ with open(json_output, "w") as f:
298
+ json.dump(results, f, indent=2)
299
+ click.echo(f"Metrics saved to: {json_output}", err=True)
300
+ else:
301
+ # Output to stdout
302
+ print(json.dumps(results, indent=2))
303
+
304
+ # Print summary
305
+ click.echo("\n" + "=" * 60, err=True)
306
+ click.echo("CMJ ANALYSIS RESULTS", err=True)
307
+ click.echo("=" * 60, err=True)
308
+ click.echo(f"Jump height: {metrics.jump_height:.3f} m", err=True)
309
+ click.echo(f"Flight time: {metrics.flight_time * 1000:.1f} ms", err=True)
310
+ click.echo(f"Countermovement depth: {metrics.countermovement_depth:.3f} m", err=True)
311
+ click.echo(f"Eccentric duration: {metrics.eccentric_duration * 1000:.1f} ms", err=True)
312
+ click.echo(f"Concentric duration: {metrics.concentric_duration * 1000:.1f} ms", err=True)
313
+ click.echo(f"Total movement time: {metrics.total_movement_time * 1000:.1f} ms", err=True)
314
+ click.echo(
315
+ f"Peak eccentric velocity: {abs(metrics.peak_eccentric_velocity):.3f} m/s (downward)",
316
+ err=True,
317
+ )
318
+ click.echo(
319
+ f"Peak concentric velocity: {metrics.peak_concentric_velocity:.3f} m/s (upward)",
320
+ err=True,
321
+ )
322
+ if metrics.transition_time is not None:
323
+ click.echo(f"Transition time: {metrics.transition_time * 1000:.1f} ms", err=True)
324
+ click.echo("=" * 60, err=True)