kinemotion 0.39.0__py3-none-any.whl → 0.39.1__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.
- kinemotion/core/debug_overlay_utils.py +91 -15
- {kinemotion-0.39.0.dist-info → kinemotion-0.39.1.dist-info}/METADATA +1 -1
- {kinemotion-0.39.0.dist-info → kinemotion-0.39.1.dist-info}/RECORD +6 -6
- {kinemotion-0.39.0.dist-info → kinemotion-0.39.1.dist-info}/WHEEL +0 -0
- {kinemotion-0.39.0.dist-info → kinemotion-0.39.1.dist-info}/entry_points.txt +0 -0
- {kinemotion-0.39.0.dist-info → kinemotion-0.39.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"""Shared debug overlay utilities for video rendering."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
3
8
|
import cv2
|
|
4
9
|
import numpy as np
|
|
5
10
|
|
|
@@ -11,7 +16,7 @@ def create_video_writer(
|
|
|
11
16
|
display_width: int,
|
|
12
17
|
display_height: int,
|
|
13
18
|
fps: float,
|
|
14
|
-
) -> tuple[cv2.VideoWriter, bool]:
|
|
19
|
+
) -> tuple[cv2.VideoWriter, bool, str]:
|
|
15
20
|
"""
|
|
16
21
|
Create a video writer with fallback codec support.
|
|
17
22
|
|
|
@@ -24,28 +29,43 @@ def create_video_writer(
|
|
|
24
29
|
fps: Frames per second
|
|
25
30
|
|
|
26
31
|
Returns:
|
|
27
|
-
Tuple of (video_writer, needs_resize)
|
|
32
|
+
Tuple of (video_writer, needs_resize, used_codec)
|
|
28
33
|
"""
|
|
29
34
|
needs_resize = (display_width != width) or (display_height != height)
|
|
30
35
|
|
|
31
|
-
# Try
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
# Try browser-compatible codecs first
|
|
37
|
+
# avc1/h264: H.264 (Most compatible)
|
|
38
|
+
# vp09: VP9 (Good compatibility)
|
|
39
|
+
# mp4v: MPEG-4 (Poor browser support, last resort)
|
|
40
|
+
codecs_to_try = ["avc1", "h264", "vp09", "mp4v"]
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
|
38
|
-
writer = cv2.VideoWriter(
|
|
39
|
-
output_path, fourcc, fps, (display_width, display_height)
|
|
40
|
-
)
|
|
42
|
+
writer = None
|
|
43
|
+
used_codec = "mp4v" # Default fallback
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
for codec in codecs_to_try:
|
|
46
|
+
try:
|
|
47
|
+
fourcc = cv2.VideoWriter_fourcc(*codec)
|
|
48
|
+
writer = cv2.VideoWriter(
|
|
49
|
+
output_path, fourcc, fps, (display_width, display_height)
|
|
50
|
+
)
|
|
51
|
+
if writer.isOpened():
|
|
52
|
+
used_codec = codec
|
|
53
|
+
if codec == "mp4v":
|
|
54
|
+
print(
|
|
55
|
+
f"Warning: Fallback to {codec} codec. "
|
|
56
|
+
"Video may not play in browsers."
|
|
57
|
+
)
|
|
58
|
+
break
|
|
59
|
+
except Exception:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
if writer is None or not writer.isOpened():
|
|
43
63
|
raise ValueError(
|
|
44
64
|
f"Failed to create video writer for {output_path} with dimensions "
|
|
45
65
|
f"{display_width}x{display_height}"
|
|
46
66
|
)
|
|
47
67
|
|
|
48
|
-
return writer, needs_resize
|
|
68
|
+
return writer, needs_resize, used_codec
|
|
49
69
|
|
|
50
70
|
|
|
51
71
|
def write_overlay_frame(
|
|
@@ -95,11 +115,12 @@ class BaseDebugOverlayRenderer:
|
|
|
95
115
|
display_height: Display height (considering SAR)
|
|
96
116
|
fps: Frames per second
|
|
97
117
|
"""
|
|
118
|
+
self.output_path = output_path
|
|
98
119
|
self.width = width
|
|
99
120
|
self.height = height
|
|
100
121
|
self.display_width = display_width
|
|
101
122
|
self.display_height = display_height
|
|
102
|
-
self.writer, self.needs_resize = create_video_writer(
|
|
123
|
+
self.writer, self.needs_resize, self.used_codec = create_video_writer(
|
|
103
124
|
output_path, width, height, display_width, display_height, fps
|
|
104
125
|
)
|
|
105
126
|
|
|
@@ -133,9 +154,64 @@ class BaseDebugOverlayRenderer:
|
|
|
133
154
|
write_overlay_frame(self.writer, frame, self.display_width, self.display_height)
|
|
134
155
|
|
|
135
156
|
def close(self) -> None:
|
|
136
|
-
"""Release video writer."""
|
|
157
|
+
"""Release video writer and re-encode if possible."""
|
|
137
158
|
self.writer.release()
|
|
138
159
|
|
|
160
|
+
# Post-process with ffmpeg ONLY if we fell back to the incompatible mp4v codec
|
|
161
|
+
if self.used_codec == "mp4v" and shutil.which("ffmpeg"):
|
|
162
|
+
temp_path = None
|
|
163
|
+
try:
|
|
164
|
+
temp_path = str(
|
|
165
|
+
Path(self.output_path).with_suffix(
|
|
166
|
+
".temp" + Path(self.output_path).suffix
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Convert to H.264 with yuv420p pixel format for browser compatibility
|
|
171
|
+
# -y: Overwrite output file
|
|
172
|
+
# -vcodec libx264: Use H.264 codec
|
|
173
|
+
# -pix_fmt yuv420p: Required for wide browser support (Chrome,
|
|
174
|
+
# Safari, Firefox)
|
|
175
|
+
# -preset fast: Reasonable speed/compression tradeoff
|
|
176
|
+
# -crf 23: Standard quality
|
|
177
|
+
# -an: Remove audio (debug video has no audio)
|
|
178
|
+
cmd = [
|
|
179
|
+
"ffmpeg",
|
|
180
|
+
"-y",
|
|
181
|
+
"-i",
|
|
182
|
+
self.output_path,
|
|
183
|
+
"-vcodec",
|
|
184
|
+
"libx264",
|
|
185
|
+
"-pix_fmt",
|
|
186
|
+
"yuv420p",
|
|
187
|
+
"-preset",
|
|
188
|
+
"fast",
|
|
189
|
+
"-crf",
|
|
190
|
+
"23",
|
|
191
|
+
"-an",
|
|
192
|
+
temp_path,
|
|
193
|
+
]
|
|
194
|
+
|
|
195
|
+
# Suppress output unless error
|
|
196
|
+
subprocess.run(
|
|
197
|
+
cmd,
|
|
198
|
+
check=True,
|
|
199
|
+
stdout=subprocess.DEVNULL,
|
|
200
|
+
stderr=subprocess.PIPE,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Overwrite original file
|
|
204
|
+
os.replace(temp_path, self.output_path)
|
|
205
|
+
|
|
206
|
+
except subprocess.CalledProcessError as e:
|
|
207
|
+
print(f"Warning: Failed to re-encode debug video with ffmpeg: {e}")
|
|
208
|
+
if temp_path and os.path.exists(temp_path):
|
|
209
|
+
os.remove(temp_path)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
print(f"Warning: Error during video post-processing: {e}")
|
|
212
|
+
if temp_path and os.path.exists(temp_path):
|
|
213
|
+
os.remove(temp_path)
|
|
214
|
+
|
|
139
215
|
def __enter__(self) -> "BaseDebugOverlayRenderer":
|
|
140
216
|
return self
|
|
141
217
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kinemotion
|
|
3
|
-
Version: 0.39.
|
|
3
|
+
Version: 0.39.1
|
|
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
|
|
@@ -12,7 +12,7 @@ kinemotion/cmj/validation_bounds.py,sha256=9ZTo68fl3ooyWjXXyTMRLpK9tFANa_rQf3oHh
|
|
|
12
12
|
kinemotion/core/__init__.py,sha256=HsqolRa60cW3vrG8F9Lvr9WvWcs5hCmsTzSgo7imi-4,1278
|
|
13
13
|
kinemotion/core/auto_tuning.py,sha256=wtCUMOhBChVJNXfEeku3GCMW4qED6MF-O_mv2sPTiVQ,11324
|
|
14
14
|
kinemotion/core/cli_utils.py,sha256=zbnifPhD-OYofJioeYfJtshuWcl8OAEWtqCGVF4ctAI,7966
|
|
15
|
-
kinemotion/core/debug_overlay_utils.py,sha256=
|
|
15
|
+
kinemotion/core/debug_overlay_utils.py,sha256=iouOW6Ss20dDH-IvDZuNnFtPIWzcav12xfUuDEulQtE,7312
|
|
16
16
|
kinemotion/core/determinism.py,sha256=NwVrHqJiVxxFHTBPVy8aDBJH2SLIcYIpdGFp7glblB8,2515
|
|
17
17
|
kinemotion/core/experimental.py,sha256=IK05AF4aZS15ke85hF3TWCqRIXU1AlD_XKzFz735Ua8,3640
|
|
18
18
|
kinemotion/core/filtering.py,sha256=GsC9BB71V07LJJHgS2lsaxUAtJsupcUiwtZFDgODh8c,11417
|
|
@@ -31,8 +31,8 @@ kinemotion/dropjump/kinematics.py,sha256=kH-XM66wlOCYMpjvyb6_Qh5ZebyOfFZ47rmhgE1
|
|
|
31
31
|
kinemotion/dropjump/metrics_validator.py,sha256=CrTlGup8q2kyPXtA6HNwm7_yq0AsBaDllG7RVZdXmYA,9342
|
|
32
32
|
kinemotion/dropjump/validation_bounds.py,sha256=5b4I3CKPybuvrbn-nP5yCcGF_sH4Vtyw3a5AWWvWnBk,4645
|
|
33
33
|
kinemotion/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
-
kinemotion-0.39.
|
|
35
|
-
kinemotion-0.39.
|
|
36
|
-
kinemotion-0.39.
|
|
37
|
-
kinemotion-0.39.
|
|
38
|
-
kinemotion-0.39.
|
|
34
|
+
kinemotion-0.39.1.dist-info/METADATA,sha256=ZvdKUkH6H9g5TiLNRA1NLPhtaKe09Y7zDjfFZg6KDwU,26020
|
|
35
|
+
kinemotion-0.39.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
36
|
+
kinemotion-0.39.1.dist-info/entry_points.txt,sha256=zaqnAnjLvcdrk1Qvj5nvXZCZ2gp0prS7it1zTJygcIY,50
|
|
37
|
+
kinemotion-0.39.1.dist-info/licenses/LICENSE,sha256=KZajvqsHw0NoOHOi2q0FZ4NBe9HdV6oey-IPYAtHXfg,1088
|
|
38
|
+
kinemotion-0.39.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|