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.
- {numpyimage-3.4.2/numpyimage.egg-info → numpyimage-3.5.1}/PKG-INFO +2 -2
- {numpyimage-3.4.2 → numpyimage-3.5.1}/npimage/vidio.py +63 -44
- {numpyimage-3.4.2 → numpyimage-3.5.1/numpyimage.egg-info}/PKG-INFO +2 -2
- {numpyimage-3.4.2 → numpyimage-3.5.1}/pyproject.toml +3 -3
- {numpyimage-3.4.2 → numpyimage-3.5.1}/LICENSE +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/README.md +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/npimage/__init__.py +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/npimage/align.py +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/npimage/graphics.py +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/npimage/imageio.py +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/npimage/nrrd_utils.py +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/npimage/operations.py +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/npimage/utils.py +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/numpyimage.egg-info/SOURCES.txt +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/numpyimage.egg-info/dependency_links.txt +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/numpyimage.egg-info/requires.txt +1 -1
- {numpyimage-3.4.2 → numpyimage-3.5.1}/numpyimage.egg-info/top_level.txt +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/setup.cfg +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/tests/test_heic.py +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/tests/test_pbm.py +0 -0
- {numpyimage-3.4.2 → numpyimage-3.5.1}/tests/test_video_writers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: numpyimage
|
|
3
|
-
Version: 3.
|
|
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
|
|
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) ->
|
|
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
|
-
|
|
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
|
-
|
|
136
|
-
|
|
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,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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.
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|