numpyimage 3.4.2__tar.gz → 3.5.0__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.0
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
 
@@ -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,11 +136,7 @@ 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
140
  with av.open(Path(filename).expanduser()) as container:
141
141
  stream = container.streams.video[0]
142
142
  for frame in container.decode(stream):
@@ -149,19 +149,33 @@ class VideoSeekError(RuntimeError):
149
149
 
150
150
 
151
151
  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`')
152
+ def __init__(self,
153
+ filename: str,
154
+ verbose: bool = False,
155
+ cache_index: Literal['auto', True, False] = 'auto'):
156
+ """
157
+ Parameters
158
+ ----------
159
+ filename : str
160
+ Path to the video file.
161
+
162
+ verbose : bool, default False
163
+ If True, print progress messages.
164
+
165
+ cache_index : 'auto' (default), True, or False
166
+ **Definitely set this to True in code where speed matters!**
167
+ Whether to cache the frame timestamp index to a .index file
168
+ next to the video file for faster loading next time.
169
+ If 'auto', the index is cached only if building it takes
170
+ more than 0.5 seconds, which only happens for ~1+ GB videos.
171
+ """
172
+ self.av = _import_av()
159
173
  self.verbose = verbose
160
174
  self.filename = Path(filename).expanduser()
161
175
  if not self.filename.exists():
162
176
  raise FileNotFoundError(f'File {filename} not found')
163
177
 
164
- self.container = av.open(str(self.filename))
178
+ self.container = self.av.open(str(self.filename))
165
179
  self.stream = self.container.streams.video[0]
166
180
  self.time_base = self.stream.time_base
167
181
  self._frame_iterator = self.container.decode(self.stream)
@@ -175,12 +189,17 @@ class VideoStreamer:
175
189
  self._lock = threading.Lock()
176
190
 
177
191
  self.index_filename = self.filename.with_suffix(self.filename.suffix + '.index')
178
- self._load_index()
192
+ self._build_index(cache_index=cache_index)
179
193
 
180
- def _build_index(self):
194
+ def _build_index(self, cache_index='auto'):
195
+ if self.index_filename.exists():
196
+ return self._load_index()
181
197
  if self.verbose:
182
198
  print('Building frame timestamp index for fast random frame access...')
183
199
 
200
+ import time
201
+ start_time = time.time()
202
+
184
203
  frames_pts = []
185
204
  # Try getting frame PTS values fast using PyAV
186
205
  try:
@@ -255,15 +274,16 @@ class VideoStreamer:
255
274
  index['frames_pts'] = frames_pts
256
275
  else:
257
276
  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}"')
277
+
278
+ if cache_index is True or (cache_index == 'auto'
279
+ and time.time() - start_time > 0.5):
280
+ with open(self.index_filename, 'w') as f:
281
+ json.dump(index, f)
282
+ if self.verbose:
283
+ print(f'Cached index at "{self.index_filename}"')
262
284
 
263
285
  def _load_index(self):
264
- if not self.index_filename.exists():
265
- self._build_index()
266
- elif self.verbose:
286
+ if self.verbose:
267
287
  print(f'Loading frame timestamp index from "{self.index_filename}"')
268
288
 
269
289
  with open(self.index_filename, 'r') as f:
@@ -517,6 +537,12 @@ class VideoStreamer:
517
537
  except Exception:
518
538
  pass
519
539
 
540
+ def __enter__(self):
541
+ return self
542
+
543
+ def __exit__(self, exc_type, exc_val, exc_tb):
544
+ self.close()
545
+
520
546
 
521
547
  class AVVideoWriter:
522
548
  """
@@ -550,12 +576,7 @@ class AVVideoWriter:
550
576
  def __init__(self, filename, framerate=30, crf=23, compression_speed='medium',
551
577
  codec: Literal['libx264', 'libx265'] = 'libx264',
552
578
  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
579
+ self.av = _import_av()
559
580
  filename = Path(filename).expanduser()
560
581
  if filename.exists() and not overwrite:
561
582
  raise FileExistsError(f'File {filename} already exists. '
@@ -565,7 +586,7 @@ class AVVideoWriter:
565
586
  self.crf = crf
566
587
  self.compression_speed = compression_speed
567
588
  self.codec = codec_aliases[codec.lower()]
568
- self.container = av.open(filename, mode='w')
589
+ self.container = self.av.open(filename, mode='w')
569
590
  self.stream = self.container.add_stream(self.codec, rate=self._framerate)
570
591
  self.stream.pix_fmt = 'yuv420p'
571
592
  self.stream.options = {'crf': str(crf), 'preset': compression_speed}
@@ -849,11 +870,6 @@ def save_video(data, filename, time_axis=0, color_axis=None, overwrite=False,
849
870
  The video codec to use for encoding. Can be any of a number of aliases for
850
871
  these two codecs, including avc1/h264 vs hevc/hvc1/hev1/h265.
851
872
  """
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
873
 
858
874
  filename = str(filename)
859
875
  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.0
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.0'
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