numpyimage 3.8.0__tar.gz → 3.8.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.
- {numpyimage-3.8.0 → numpyimage-3.8.2}/PKG-INFO +1 -1
- {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/vidio.py +58 -1
- {numpyimage-3.8.0 → numpyimage-3.8.2}/numpyimage.egg-info/PKG-INFO +1 -1
- {numpyimage-3.8.0 → numpyimage-3.8.2}/pyproject.toml +1 -1
- {numpyimage-3.8.0 → numpyimage-3.8.2}/LICENSE +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/README.md +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/__init__.py +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/align.py +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/graphics.py +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/imageio.py +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/nrrd_utils.py +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/operations.py +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/utils.py +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/numpyimage.egg-info/SOURCES.txt +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/numpyimage.egg-info/dependency_links.txt +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/numpyimage.egg-info/requires.txt +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/numpyimage.egg-info/top_level.txt +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/setup.cfg +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/tests/test_heic.py +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/tests/test_pbm.py +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/tests/test_video_streamer.py +0 -0
- {numpyimage-3.8.0 → numpyimage-3.8.2}/tests/test_video_writers.py +0 -0
|
@@ -21,6 +21,8 @@ Class list:
|
|
|
21
21
|
from typing import Union, Tuple, Iterator, Literal
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
import subprocess
|
|
24
|
+
import shutil
|
|
25
|
+
import sys
|
|
24
26
|
import threading
|
|
25
27
|
import json
|
|
26
28
|
import math
|
|
@@ -70,6 +72,43 @@ def _import_av():
|
|
|
70
72
|
' run `pip install av` and try again')
|
|
71
73
|
|
|
72
74
|
|
|
75
|
+
def _check_ffmpeg_available(program: Literal['ffmpeg', 'ffprobe'] = 'ffmpeg'):
|
|
76
|
+
"""
|
|
77
|
+
Check that ffmpeg or ffprobe is on PATH, and raise a
|
|
78
|
+
FileNotFoundError with installation instructions if not.
|
|
79
|
+
"""
|
|
80
|
+
if shutil.which(program) is not None:
|
|
81
|
+
return
|
|
82
|
+
if sys.platform.startswith('linux'):
|
|
83
|
+
install_hint = (
|
|
84
|
+
'On Debian/Ubuntu: sudo apt install ffmpeg\n'
|
|
85
|
+
' On Fedora/RHEL: sudo dnf install ffmpeg\n'
|
|
86
|
+
' On Arch: sudo pacman -S ffmpeg'
|
|
87
|
+
)
|
|
88
|
+
elif sys.platform == 'darwin':
|
|
89
|
+
install_hint = (
|
|
90
|
+
'With Homebrew: brew install ffmpeg\n'
|
|
91
|
+
' With MacPorts: sudo port install ffmpeg'
|
|
92
|
+
)
|
|
93
|
+
elif sys.platform.startswith('win'):
|
|
94
|
+
install_hint = (
|
|
95
|
+
'With winget: winget install ffmpeg\n'
|
|
96
|
+
' With Chocolatey: choco install ffmpeg\n'
|
|
97
|
+
' Or download a static build from https://ffmpeg.org/download.html'
|
|
98
|
+
' and add it to your PATH.'
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
install_hint = (
|
|
102
|
+
'See https://ffmpeg.org/download.html for installation'
|
|
103
|
+
' instructions for your platform.'
|
|
104
|
+
)
|
|
105
|
+
raise FileNotFoundError(
|
|
106
|
+
f"`{program}` was not found on PATH. npimage.vidio needs the ffmpeg"
|
|
107
|
+
f" suite installed to read and write video files.\n\n"
|
|
108
|
+
f"Install ffmpeg:\n {install_hint}"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
73
112
|
def load_video(filename,
|
|
74
113
|
return_framerate=False,
|
|
75
114
|
progress_bar=True) -> Union[np.ndarray, Tuple[np.ndarray, float]]:
|
|
@@ -285,6 +324,7 @@ class VideoStreamer:
|
|
|
285
324
|
print('Falling back to ffprobe...')
|
|
286
325
|
frames_pts = []
|
|
287
326
|
# Fallback to ffprobe if PyAV didn't work
|
|
327
|
+
_check_ffmpeg_available('ffprobe')
|
|
288
328
|
cmd = ['ffprobe',
|
|
289
329
|
'-select_streams', 'v:0',
|
|
290
330
|
'-show_frames',
|
|
@@ -885,6 +925,8 @@ class FFmpegVideoWriter:
|
|
|
885
925
|
# Initialize process state
|
|
886
926
|
self._process = None
|
|
887
927
|
self._stdin = None
|
|
928
|
+
self._stderr_thread = None
|
|
929
|
+
self._stderr_buffer = b''
|
|
888
930
|
self._closed = False
|
|
889
931
|
self._width = None
|
|
890
932
|
self._height = None
|
|
@@ -945,6 +987,7 @@ class FFmpegVideoWriter:
|
|
|
945
987
|
command.append(str(self.filename))
|
|
946
988
|
|
|
947
989
|
# Start FFmpeg process
|
|
990
|
+
_check_ffmpeg_available('ffmpeg')
|
|
948
991
|
self._process = subprocess.Popen(
|
|
949
992
|
command,
|
|
950
993
|
stdin=subprocess.PIPE,
|
|
@@ -953,6 +996,15 @@ class FFmpegVideoWriter:
|
|
|
953
996
|
)
|
|
954
997
|
self._stdin = self._process.stdin
|
|
955
998
|
|
|
999
|
+
# Drain ffmpeg's stderr in a background thread so a verbose or
|
|
1000
|
+
# erroring ffmpeg can't fill the pipe buffer (~64 KB on Linux, smaller
|
|
1001
|
+
# on Windows) and deadlock our writes to stdin.
|
|
1002
|
+
def _drain_stderr():
|
|
1003
|
+
self._stderr_buffer = self._process.stderr.read()
|
|
1004
|
+
self._stderr_thread = threading.Thread(target=_drain_stderr,
|
|
1005
|
+
daemon=True)
|
|
1006
|
+
self._stderr_thread.start()
|
|
1007
|
+
|
|
956
1008
|
def write(self, frame):
|
|
957
1009
|
"""Write a frame to the video file"""
|
|
958
1010
|
if self._closed:
|
|
@@ -1013,8 +1065,10 @@ class FFmpegVideoWriter:
|
|
|
1013
1065
|
|
|
1014
1066
|
# Wait for FFmpeg to finish
|
|
1015
1067
|
if self._process:
|
|
1016
|
-
stderr_data = self._process.stderr.read()
|
|
1017
1068
|
return_code = self._process.wait()
|
|
1069
|
+
if self._stderr_thread is not None:
|
|
1070
|
+
self._stderr_thread.join()
|
|
1071
|
+
stderr_data = self._stderr_buffer
|
|
1018
1072
|
|
|
1019
1073
|
# Check for errors
|
|
1020
1074
|
if return_code != 0:
|
|
@@ -1024,6 +1078,7 @@ class FFmpegVideoWriter:
|
|
|
1024
1078
|
# Clean up process references
|
|
1025
1079
|
self._process = None
|
|
1026
1080
|
self._stdin = None
|
|
1081
|
+
self._stderr_thread = None
|
|
1027
1082
|
self._closed = True
|
|
1028
1083
|
|
|
1029
1084
|
def __enter__(self):
|
|
@@ -1176,6 +1231,8 @@ def _get_rotation_from_metadata(filename):
|
|
|
1176
1231
|
We could use PyAV to do this perhaps faster, but for an already fast operation
|
|
1177
1232
|
like this, we stick with ffprobe to avoid the memory leaks in PyAV.
|
|
1178
1233
|
"""
|
|
1234
|
+
if shutil.which('ffprobe') is None:
|
|
1235
|
+
return None
|
|
1179
1236
|
cmd = ['ffprobe',
|
|
1180
1237
|
'-v', 'quiet',
|
|
1181
1238
|
'-select_streams', 'v:0',
|
|
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
|