|
1 | 1 | import importlib
|
| 2 | +import sys |
| 3 | + |
2 | 4 | import math
|
3 | 5 | import warnings
|
4 | 6 | from io import StringIO
|
@@ -81,21 +83,30 @@ def extract_frames_as_images(video_document: Document, framenums: Iterable[int],
|
81 | 83 | :param as_PIL: return :py:class:`PIL.Image.Image` instead of :py:class:`~numpy.ndarray`
|
82 | 84 | :return: frames as a list of :py:class:`~numpy.ndarray` or :py:class:`~PIL.Image.Image`
|
83 | 85 | """
|
| 86 | + import cv2 |
84 | 87 | if as_PIL:
|
85 | 88 | from PIL import Image
|
86 | 89 | frames = []
|
87 | 90 | video = capture(video_document)
|
88 | 91 | cur_f = 0
|
89 | 92 | tot_fcount = video_document.get_property(FRAMECOUNT_DOCPROP_KEY)
|
| 93 | + # when the target frame is more than this frames away, fast-forward instead of reading frame by frame |
| 94 | + # this is sanity-checked with a small number of video samples |
| 95 | + # (frame-by-frame ndarrays are compared with fast-forwarded ndarrays) |
| 96 | + skip_threadhold = 1000 |
90 | 97 | framenumi = iter(framenums) # make sure that it's actually an iterator, in case a list is passed
|
91 | 98 | next_target_f = next(framenumi, None)
|
92 | 99 | from wurlitzer import pipes as cpipes
|
93 |
| - ffmpeg_outs = StringIO() |
94 | 100 | ffmpeg_errs = StringIO()
|
95 |
| - with cpipes(stderr=ffmpeg_errs, stdout=ffmpeg_outs): |
| 101 | + with cpipes(stderr=ffmpeg_errs, stdout=sys.stdout): |
96 | 102 | while True:
|
97 |
| - if next_target_f is None or cur_f > tot_fcount: |
| 103 | + if next_target_f is None or cur_f > tot_fcount or next_target_f > tot_fcount: |
98 | 104 | break
|
| 105 | + if next_target_f - cur_f > skip_threadhold: |
| 106 | + while next_target_f - cur_f > skip_threadhold: |
| 107 | + cur_f += skip_threadhold |
| 108 | + else: |
| 109 | + video.set(cv2.CAP_PROP_POS_FRAMES, cur_f) |
99 | 110 | ret, frame = video.read()
|
100 | 111 | if cur_f == next_target_f:
|
101 | 112 | if not ret:
|
|
0 commit comments