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.
Files changed (22) hide show
  1. {numpyimage-3.8.0 → numpyimage-3.8.2}/PKG-INFO +1 -1
  2. {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/vidio.py +58 -1
  3. {numpyimage-3.8.0 → numpyimage-3.8.2}/numpyimage.egg-info/PKG-INFO +1 -1
  4. {numpyimage-3.8.0 → numpyimage-3.8.2}/pyproject.toml +1 -1
  5. {numpyimage-3.8.0 → numpyimage-3.8.2}/LICENSE +0 -0
  6. {numpyimage-3.8.0 → numpyimage-3.8.2}/README.md +0 -0
  7. {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/__init__.py +0 -0
  8. {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/align.py +0 -0
  9. {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/graphics.py +0 -0
  10. {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/imageio.py +0 -0
  11. {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/nrrd_utils.py +0 -0
  12. {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/operations.py +0 -0
  13. {numpyimage-3.8.0 → numpyimage-3.8.2}/npimage/utils.py +0 -0
  14. {numpyimage-3.8.0 → numpyimage-3.8.2}/numpyimage.egg-info/SOURCES.txt +0 -0
  15. {numpyimage-3.8.0 → numpyimage-3.8.2}/numpyimage.egg-info/dependency_links.txt +0 -0
  16. {numpyimage-3.8.0 → numpyimage-3.8.2}/numpyimage.egg-info/requires.txt +0 -0
  17. {numpyimage-3.8.0 → numpyimage-3.8.2}/numpyimage.egg-info/top_level.txt +0 -0
  18. {numpyimage-3.8.0 → numpyimage-3.8.2}/setup.cfg +0 -0
  19. {numpyimage-3.8.0 → numpyimage-3.8.2}/tests/test_heic.py +0 -0
  20. {numpyimage-3.8.0 → numpyimage-3.8.2}/tests/test_pbm.py +0 -0
  21. {numpyimage-3.8.0 → numpyimage-3.8.2}/tests/test_video_streamer.py +0 -0
  22. {numpyimage-3.8.0 → numpyimage-3.8.2}/tests/test_video_writers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numpyimage
3
- Version: 3.8.0
3
+ Version: 3.8.2
4
4
  Summary: Load, save, & manipulate image files as numpy arrays
5
5
  Author-email: Jasper Phelps <jasper.s.phelps@gmail.com>
6
6
  License: MIT License
@@ -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',
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numpyimage
3
- Version: 3.8.0
3
+ Version: 3.8.2
4
4
  Summary: Load, save, & manipulate image files as numpy arrays
5
5
  Author-email: Jasper Phelps <jasper.s.phelps@gmail.com>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
4
4
 
5
5
  [project]
6
6
  name = 'numpyimage'
7
- version = '3.8.0'
7
+ version = '3.8.2'
8
8
  description = 'Load, save, & manipulate image files as numpy arrays'
9
9
  readme.file = 'README.md'
10
10
  readme.content-type = 'text/markdown'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes