numpyimage 3.6.0__tar.gz → 3.6.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.6.0/numpyimage.egg-info → numpyimage-3.6.2}/PKG-INFO +1 -1
  2. {numpyimage-3.6.0 → numpyimage-3.6.2}/npimage/imageio.py +7 -1
  3. {numpyimage-3.6.0 → numpyimage-3.6.2}/npimage/vidio.py +38 -15
  4. {numpyimage-3.6.0 → numpyimage-3.6.2/numpyimage.egg-info}/PKG-INFO +1 -1
  5. {numpyimage-3.6.0 → numpyimage-3.6.2}/pyproject.toml +3 -3
  6. {numpyimage-3.6.0 → numpyimage-3.6.2}/LICENSE +0 -0
  7. {numpyimage-3.6.0 → numpyimage-3.6.2}/README.md +0 -0
  8. {numpyimage-3.6.0 → numpyimage-3.6.2}/npimage/__init__.py +0 -0
  9. {numpyimage-3.6.0 → numpyimage-3.6.2}/npimage/align.py +0 -0
  10. {numpyimage-3.6.0 → numpyimage-3.6.2}/npimage/graphics.py +0 -0
  11. {numpyimage-3.6.0 → numpyimage-3.6.2}/npimage/nrrd_utils.py +0 -0
  12. {numpyimage-3.6.0 → numpyimage-3.6.2}/npimage/operations.py +0 -0
  13. {numpyimage-3.6.0 → numpyimage-3.6.2}/npimage/utils.py +0 -0
  14. {numpyimage-3.6.0 → numpyimage-3.6.2}/numpyimage.egg-info/SOURCES.txt +0 -0
  15. {numpyimage-3.6.0 → numpyimage-3.6.2}/numpyimage.egg-info/dependency_links.txt +0 -0
  16. {numpyimage-3.6.0 → numpyimage-3.6.2}/numpyimage.egg-info/requires.txt +0 -0
  17. {numpyimage-3.6.0 → numpyimage-3.6.2}/numpyimage.egg-info/top_level.txt +0 -0
  18. {numpyimage-3.6.0 → numpyimage-3.6.2}/setup.cfg +0 -0
  19. {numpyimage-3.6.0 → numpyimage-3.6.2}/tests/test_heic.py +0 -0
  20. {numpyimage-3.6.0 → numpyimage-3.6.2}/tests/test_pbm.py +0 -0
  21. {numpyimage-3.6.0 → numpyimage-3.6.2}/tests/test_video_streamer.py +0 -0
  22. {numpyimage-3.6.0 → numpyimage-3.6.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.6.0
3
+ Version: 3.6.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
@@ -152,7 +152,8 @@ def save(data,
152
152
  units=None,
153
153
  compress=None,
154
154
  compression_level=3,
155
- metadata=None) -> None:
155
+ metadata=None,
156
+ **kwargs) -> None:
156
157
  """
157
158
  Save a numpy array to file with a file type specified by the
158
159
  filename extension.
@@ -173,7 +174,12 @@ def save(data,
173
174
  raise FileExistsError(f'File {filename} already exists. '
174
175
  'Set overwrite=True to overwrite.')
175
176
  extension = filename.split('.')[-1].lower()
177
+ if extension in vidio.supported_extensions:
178
+ return vidio.save_video(data, filename, dim_order=dim_order,
179
+ overwrite=overwrite, **kwargs)
176
180
  assert extension in supported_extensions, f'Filetype {extension} not supported'
181
+ if kwargs:
182
+ raise TypeError(f'save() got unexpected keyword arguments: {list(kwargs.keys())}')
177
183
 
178
184
  if compress is not None and extension not in ['nrrd', 'ng']:
179
185
  print('WARNING: compress argument is ignored because not saving as '
@@ -395,6 +395,8 @@ class VideoStreamer:
395
395
  return float(1.0 / self._framerate)
396
396
 
397
397
  def frame_number_to_pts(self, frame_number: int) -> int:
398
+ if hasattr(frame_number, '__iter__'):
399
+ return [self.frame_number_to_pts(n) for n in frame_number]
398
400
  frame_number = self._normalize_frame_number(frame_number)
399
401
  if self._framerate == 'variable':
400
402
  return self.frames_pts[frame_number]
@@ -402,9 +404,13 @@ class VideoStreamer:
402
404
  return int(frame_number) * self.pts_delta + self.pts0
403
405
 
404
406
  def frame_number_to_time(self, frame_number: int) -> float:
407
+ if hasattr(frame_number, '__iter__'):
408
+ return [self.frame_number_to_time(n) for n in frame_number]
405
409
  return float(self.frame_number_to_pts(frame_number) * self.time_base)
406
410
 
407
411
  def pts_to_frame_number(self, pts: int) -> int:
412
+ if hasattr(pts, '__iter__'):
413
+ return [self.pts_to_frame_number(p) for p in pts]
408
414
  if self._framerate == 'variable':
409
415
  if pts not in self.frames_pts:
410
416
  raise ValueError(f'PTS {pts} not in video index.')
@@ -776,14 +782,37 @@ class FFmpegVideoWriter:
776
782
  self._closed = False
777
783
  self._width = None
778
784
  self._height = None
779
- self._pixel_format = None
785
+ self._pixel_format_in = None
786
+ self._pixel_format_out = 'yuv420p'
780
787
 
781
788
  @property
782
789
  def framerate(self):
783
790
  return float(self._framerate)
784
791
 
785
- def _initialize_process(self, width, height, pixel_format):
792
+ def _initialize_process(self, width, height, pixel_format_in):
786
793
  """Initialize the FFmpeg subprocess for video encoding"""
794
+ self._width = width
795
+ self._height = height
796
+ self._pixel_format_in = pixel_format_in
797
+
798
+ # Check if width or height needs to be padded to an even value
799
+ if (self._pixel_format_out == 'yuv420p'
800
+ and (width % 2 != 0 or height % 2 != 0)):
801
+ pad = [[0, 0], [0, 0], [0, 0]] # [h, w, c]
802
+ if height % 2 != 0:
803
+ print('INFO: Height must be even for yuv420p pixel format but image'
804
+ f' has height {height}, so the bottom row will be duplicated.')
805
+ pad[0][1] = 1
806
+ height = height + 1
807
+ if width % 2 != 0:
808
+ print('INFO: Width must be even for yuv420p pixel format but image'
809
+ f' has width {width}, so the right column will be duplicated.')
810
+ pad[1][1] = 1
811
+ width = width + 1
812
+ self._pad = pad
813
+ else:
814
+ self._pad = 0
815
+
787
816
  command = [
788
817
  'ffmpeg',
789
818
  '-hide_banner',
@@ -793,12 +822,12 @@ class FFmpegVideoWriter:
793
822
  '-f', 'rawvideo',
794
823
  '-vcodec', 'rawvideo',
795
824
  '-s', f'{width}x{height}',
796
- '-pix_fmt', pixel_format,
825
+ '-pix_fmt', pixel_format_in, # Input pixel format comes before -i
797
826
  '-r', str(self._framerate),
798
827
  '-i', '-', # Read from stdin
799
828
  '-an', # No audio
800
829
  '-c:v', self.codec,
801
- '-pix_fmt', 'yuv420p', # Output pixel format
830
+ '-pix_fmt', self._pixel_format_out, # Output pixel format after -i
802
831
  '-crf', str(self.crf),
803
832
  ]
804
833
  if self.codec in ('libvpx', 'libvpx-vp9'):
@@ -816,9 +845,7 @@ class FFmpegVideoWriter:
816
845
  stderr=subprocess.PIPE # Capture errors
817
846
  )
818
847
  self._stdin = self._process.stdin
819
- self._width = width
820
- self._height = height
821
- self._pixel_format = pixel_format
848
+
822
849
 
823
850
  def write(self, frame):
824
851
  """Write a frame to the video file"""
@@ -861,6 +888,9 @@ class FFmpegVideoWriter:
861
888
  raise ValueError(f'Cannot write image of size (w={width}, h={height}) to video'
862
889
  f' already containing images of size {(self._width, self._height)}')
863
890
 
891
+ if self._pad:
892
+ frame = np.pad(frame, self._pad, mode='edge')
893
+
864
894
  # Convert frame to bytes and write to FFmpeg
865
895
  frame_bytes = frame.tobytes()
866
896
  self._stdin.write(frame_bytes)
@@ -993,16 +1023,9 @@ def save_video(data, filename, time_axis=0, color_axis=None, overwrite=False,
993
1023
  height, width = data.shape[1:]
994
1024
 
995
1025
  extension = filename.suffix.lower().lstrip('.')
996
- if extension == 'mp4':
997
- pad = [[0, 0], [0, 0], [0, 0]]
998
- if height % 2 != 0:
999
- pad[1][1] = 1
1000
- if width % 2 != 0:
1001
- pad[2][1] = 1
1002
- if pad != [[0, 0], [0, 0], [0, 0]]:
1003
- data = np.pad(data, pad, mode='edge')
1004
1026
 
1005
1027
  if extension == 'gif':
1028
+ # We make gifs with PIL instead of using FFmpeg
1006
1029
  from PIL import Image
1007
1030
  pil_images = [Image.fromarray(frame) for frame in data]
1008
1031
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numpyimage
3
- Version: 3.6.0
3
+ Version: 3.6.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,12 +4,12 @@ build-backend = 'setuptools.build_meta'
4
4
 
5
5
  [project]
6
6
  name = 'numpyimage'
7
- version = '3.6.0'
7
+ version = '3.6.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'
11
- requires-python = ">=3.6"
12
- license = {file = "LICENSE"}
11
+ requires-python = '>=3.6'
12
+ license = {file = 'LICENSE'}
13
13
  keywords = ['images', 'pixel arrays', 'image formats', 'convert', 'graphics', 'draw simple shapes']
14
14
  authors = [{name = 'Jasper Phelps', email = 'jasper.s.phelps@gmail.com'}]
15
15
  classifiers = [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes