numpyimage 3.4.2__tar.gz → 3.5.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numpyimage
3
- Version: 3.4.2
3
+ Version: 3.5.1
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
@@ -36,6 +36,7 @@ Requires-Python: >=3.6
36
36
  Description-Content-Type: text/markdown
37
37
  License-File: LICENSE
38
38
  Requires-Dist: numpy
39
+ Requires-Dist: tqdm
39
40
  Requires-Dist: pillow
40
41
  Requires-Dist: pillow-heif
41
42
  Requires-Dist: tifffile
@@ -43,7 +44,6 @@ Requires-Dist: pynrrd
43
44
  Requires-Dist: matplotlib
44
45
  Requires-Dist: opencv-python-headless
45
46
  Provides-Extra: all
46
- Requires-Dist: tqdm; extra == "all"
47
47
  Requires-Dist: av; extra == "all"
48
48
  Dynamic: license-file
49
49
 
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- Functions for reading and writing videos.
3
+ Functions for reading and writing video files.
4
4
 
5
5
  Function list:
6
6
  - load_video(filename) -> np.ndarray
7
7
  - lazy_load_video(filename) -> Iterator[np.ndarray]
8
- - save_video(data, filename) -> Saves a (time, height, width[, channels])
9
- numpy array of pixel values as a video file.
8
+ - save_video(data, filename) -> None
9
+ Saves a numpy array of pixel values as a video file.
10
10
  Arguments for setting framerate, video bitrate, etc are provided.
11
11
 
12
12
  Class list:
@@ -25,6 +25,7 @@ import json
25
25
  from fractions import Fraction
26
26
 
27
27
  import numpy as np
28
+ from tqdm import tqdm
28
29
 
29
30
  from . import utils
30
31
 
@@ -44,6 +45,15 @@ codec_aliases = {
44
45
  supported_extensions = ['mp4', 'mkv', 'avi', 'mov', 'webm']
45
46
 
46
47
 
48
+ def _import_av():
49
+ try:
50
+ import av
51
+ return av
52
+ except ImportError:
53
+ raise ImportError('Missing optional dependency for video processing,'
54
+ ' run `pip install av` and try again')
55
+
56
+
47
57
  def load_video(filename,
48
58
  return_framerate=False,
49
59
  progress_bar=True) -> Union[np.ndarray, Tuple[np.ndarray, float]]:
@@ -69,13 +79,7 @@ def load_video(filename,
69
79
  framerate : float
70
80
  The frame rate of the video in frames per second
71
81
  """
72
- try:
73
- import av
74
- from tqdm import tqdm
75
- except ImportError:
76
- raise ImportError('Missing optional dependency for video processing,'
77
- ' run `pip install av tqdm`')
78
-
82
+ av = _import_av()
79
83
  with av.open(filename) as container:
80
84
  stream = container.streams.video[0]
81
85
  num_frames = stream.frames
@@ -132,15 +136,14 @@ def lazy_load_video(filename) -> Iterator[np.ndarray]:
132
136
  frame : np.ndarray
133
137
  Video frame as a numpy array, shape (height, width, colors).
134
138
  """
135
- try:
136
- import av
137
- except ImportError:
138
- raise ImportError('Missing optional dependency for video processing,'
139
- ' run `pip install av tqdm`')
139
+ av = _import_av()
140
+ rotation = _get_rotation_from_metadata(filename)
140
141
  with av.open(Path(filename).expanduser()) as container:
141
142
  stream = container.streams.video[0]
142
143
  for frame in container.decode(stream):
143
144
  img = frame.to_ndarray(format='rgb24')
145
+ if rotation not in [None, '0', 0]:
146
+ img = np.rot90(img, k=-int(rotation) // 90)
144
147
  yield img
145
148
 
146
149
 
@@ -149,19 +152,33 @@ class VideoSeekError(RuntimeError):
149
152
 
150
153
 
151
154
  class VideoStreamer:
152
- def __init__(self, filename, verbose: bool = False):
153
- try:
154
- import av
155
- self.av = av
156
- except ImportError:
157
- raise ImportError('Missing optional dependency for video processing,'
158
- ' run `pip install av tqdm`')
155
+ def __init__(self,
156
+ filename: str,
157
+ verbose: bool = False,
158
+ cache_index: Literal['auto', True, False] = 'auto'):
159
+ """
160
+ Parameters
161
+ ----------
162
+ filename : str
163
+ Path to the video file.
164
+
165
+ verbose : bool, default False
166
+ If True, print progress messages.
167
+
168
+ cache_index : 'auto' (default), True, or False
169
+ **Definitely set this to True in code where speed matters!**
170
+ Whether to cache the frame timestamp index to a .index file
171
+ next to the video file for faster loading next time.
172
+ If 'auto', the index is cached only if building it takes
173
+ more than 0.5 seconds, which only happens for ~1+ GB videos.
174
+ """
175
+ self.av = _import_av()
159
176
  self.verbose = verbose
160
177
  self.filename = Path(filename).expanduser()
161
178
  if not self.filename.exists():
162
179
  raise FileNotFoundError(f'File {filename} not found')
163
180
 
164
- self.container = av.open(str(self.filename))
181
+ self.container = self.av.open(str(self.filename))
165
182
  self.stream = self.container.streams.video[0]
166
183
  self.time_base = self.stream.time_base
167
184
  self._frame_iterator = self.container.decode(self.stream)
@@ -175,12 +192,17 @@ class VideoStreamer:
175
192
  self._lock = threading.Lock()
176
193
 
177
194
  self.index_filename = self.filename.with_suffix(self.filename.suffix + '.index')
178
- self._load_index()
195
+ self._build_index(cache_index=cache_index)
179
196
 
180
- def _build_index(self):
197
+ def _build_index(self, cache_index='auto'):
198
+ if self.index_filename.exists():
199
+ return self._load_index()
181
200
  if self.verbose:
182
201
  print('Building frame timestamp index for fast random frame access...')
183
202
 
203
+ import time
204
+ start_time = time.time()
205
+
184
206
  frames_pts = []
185
207
  # Try getting frame PTS values fast using PyAV
186
208
  try:
@@ -255,15 +277,16 @@ class VideoStreamer:
255
277
  index['frames_pts'] = frames_pts
256
278
  else:
257
279
  index['pts0'] = self.pts0
258
- with open(self.index_filename, 'w') as f:
259
- json.dump(index, f)
260
- if self.verbose:
261
- print(f'Cached index at "{self.index_filename}"')
280
+
281
+ if cache_index is True or (cache_index == 'auto'
282
+ and time.time() - start_time > 0.5):
283
+ with open(self.index_filename, 'w') as f:
284
+ json.dump(index, f)
285
+ if self.verbose:
286
+ print(f'Cached index at "{self.index_filename}"')
262
287
 
263
288
  def _load_index(self):
264
- if not self.index_filename.exists():
265
- self._build_index()
266
- elif self.verbose:
289
+ if self.verbose:
267
290
  print(f'Loading frame timestamp index from "{self.index_filename}"')
268
291
 
269
292
  with open(self.index_filename, 'r') as f:
@@ -517,6 +540,12 @@ class VideoStreamer:
517
540
  except Exception:
518
541
  pass
519
542
 
543
+ def __enter__(self):
544
+ return self
545
+
546
+ def __exit__(self, exc_type, exc_val, exc_tb):
547
+ self.close()
548
+
520
549
 
521
550
  class AVVideoWriter:
522
551
  """
@@ -550,12 +579,7 @@ class AVVideoWriter:
550
579
  def __init__(self, filename, framerate=30, crf=23, compression_speed='medium',
551
580
  codec: Literal['libx264', 'libx265'] = 'libx264',
552
581
  overwrite=False):
553
- try:
554
- import av
555
- except ImportError:
556
- raise ImportError('Missing optional dependency for video processing,'
557
- ' run `pip install av tqdm`')
558
- self.av = av
582
+ self.av = _import_av()
559
583
  filename = Path(filename).expanduser()
560
584
  if filename.exists() and not overwrite:
561
585
  raise FileExistsError(f'File {filename} already exists. '
@@ -565,7 +589,7 @@ class AVVideoWriter:
565
589
  self.crf = crf
566
590
  self.compression_speed = compression_speed
567
591
  self.codec = codec_aliases[codec.lower()]
568
- self.container = av.open(filename, mode='w')
592
+ self.container = self.av.open(filename, mode='w')
569
593
  self.stream = self.container.add_stream(self.codec, rate=self._framerate)
570
594
  self.stream.pix_fmt = 'yuv420p'
571
595
  self.stream.options = {'crf': str(crf), 'preset': compression_speed}
@@ -849,11 +873,6 @@ def save_video(data, filename, time_axis=0, color_axis=None, overwrite=False,
849
873
  The video codec to use for encoding. Can be any of a number of aliases for
850
874
  these two codecs, including avc1/h264 vs hevc/hvc1/hev1/h265.
851
875
  """
852
- try:
853
- from tqdm import tqdm
854
- except ImportError:
855
- raise ImportError('Missing optional dependency for video processing,'
856
- ' run `pip install av tqdm`')
857
876
 
858
877
  filename = str(filename)
859
878
  if filename.split('.')[-1].lower() not in supported_extensions:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numpyimage
3
- Version: 3.4.2
3
+ Version: 3.5.1
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
@@ -36,6 +36,7 @@ Requires-Python: >=3.6
36
36
  Description-Content-Type: text/markdown
37
37
  License-File: LICENSE
38
38
  Requires-Dist: numpy
39
+ Requires-Dist: tqdm
39
40
  Requires-Dist: pillow
40
41
  Requires-Dist: pillow-heif
41
42
  Requires-Dist: tifffile
@@ -43,7 +44,6 @@ Requires-Dist: pynrrd
43
44
  Requires-Dist: matplotlib
44
45
  Requires-Dist: opencv-python-headless
45
46
  Provides-Extra: all
46
- Requires-Dist: tqdm; extra == "all"
47
47
  Requires-Dist: av; extra == "all"
48
48
  Dynamic: license-file
49
49
 
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
4
4
 
5
5
  [project]
6
6
  name = 'numpyimage'
7
- version = '3.4.2'
7
+ version = '3.5.1'
8
8
  description = 'Load, save, & manipulate image files as numpy arrays'
9
9
  readme.file = 'README.md'
10
10
  readme.content-type = 'text/markdown'
@@ -22,6 +22,7 @@ classifiers = [
22
22
  urls = {Repository = 'https://github.com/jasper-tms/npimage'}
23
23
  dependencies = [
24
24
  'numpy',
25
+ 'tqdm',
25
26
  'pillow',
26
27
  'pillow-heif',
27
28
  'tifffile',
@@ -32,8 +33,7 @@ dependencies = [
32
33
 
33
34
  [project.optional-dependencies]
34
35
  all = [
35
- "tqdm",
36
- "av"
36
+ 'av'
37
37
  ]
38
38
 
39
39
  [tool.setuptools.packages.find]
File without changes
File without changes
File without changes
File without changes
@@ -1,4 +1,5 @@
1
1
  numpy
2
+ tqdm
2
3
  pillow
3
4
  pillow-heif
4
5
  tifffile
@@ -7,5 +8,4 @@ matplotlib
7
8
  opencv-python-headless
8
9
 
9
10
  [all]
10
- tqdm
11
11
  av
File without changes
File without changes