Skip to content

Commit 22786cf

Browse files
author
Christoph Spörk
committed
Add support for skipping frames (in case videofile has malformed frames)
Add delegate call to get stream dimensions Replace long polling put queue with built-in blocking wait for put (with configurable timeout on instantiation)
1 parent 9f740a5 commit 22786cf

File tree

1 file changed

+59
-16
lines changed

1 file changed

+59
-16
lines changed

imutils/video/filevideostream.py

+59-16
Original file line numberDiff line numberDiff line change
@@ -6,72 +6,115 @@
66

77
# import the Queue class from Python 3
88
if sys.version_info >= (3, 0):
9-
from queue import Queue
9+
from queue import Queue, Full
1010

1111
# otherwise, import the Queue class for Python 2.7
1212
else:
1313
from Queue import Queue
1414

1515

16+
class EndOfStreamException(Exception):
17+
pass
18+
19+
1620
class FileVideoStream:
17-
def __init__(self, path, transform=None, queue_size=128):
21+
22+
def __init__(self, path, transform=None, queue_size=128, skip_frames=True):
1823
# initialize the file video stream along with the boolean
1924
# used to indicate if the thread should be stopped or not
25+
self.skip_frames = skip_frames
2026
self.stream = cv2.VideoCapture(path)
27+
self.total_frames = self.stream.get(cv2.CAP_PROP_FRAME_COUNT)
28+
self.skipped_frames = 0
2129
self.stopped = False
2230
self.transform = transform
2331

2432
# initialize the queue used to store frames read from
2533
# the video file
2634
self.Q = Queue(maxsize=queue_size)
27-
# intialize thread
35+
# initialize thread
2836
self.thread = Thread(target=self.update, args=())
2937
self.thread.daemon = True
3038

39+
def check_eos(self):
40+
if self.stream.get(cv2.CAP_PROP_POS_FRAMES) == self.total_frames:
41+
# If the number of captured frames is equal to the total number of frames,
42+
# we stop
43+
self.stop()
44+
raise EndOfStreamException("End of stream")
45+
3146
def start(self):
3247
# start a thread to read frames from the file video stream
3348
self.thread.start()
3449
return self
3550

51+
def get_frame(self):
52+
# grab the current frame
53+
flag, frame = self.stream.read()
54+
55+
while not flag and self.skip_frames:
56+
self.check_eos()
57+
if self.skipped_frames == 0:
58+
print(f"Skipping frame(s)")
59+
self.skipped_frames += 1
60+
flag, frame = self.stream.read()
61+
if self.skipped_frames > 0:
62+
print(f"Resuming video...")
63+
self.skipped_frames = 0
64+
65+
return flag, frame
66+
3667
def update(self):
3768
# keep looping infinitely
3869
while True:
3970
# if the thread indicator variable is set, stop the
4071
# thread
4172
if self.stopped:
4273
break
43-
44-
# otherwise, ensure the queue has room in it
45-
if not self.Q.full():
74+
try:
4675
# read the next frame from the file
47-
(grabbed, frame) = self.stream.read()
48-
76+
grabbed, frame = self.get_frame()
4977
# if the `grabbed` boolean is `False`, then we have
5078
# reached the end of the video file
5179
if not grabbed:
80+
print("Could not grab")
5281
self.stopped = True
53-
82+
raise EndOfStreamException()
83+
5484
# if there are transforms to be done, might as well
5585
# do them on producer thread before handing back to
56-
# consumer thread. ie. Usually the producer is so far
86+
# consumer thread. i.e. Usually the producer is so far
5787
# ahead of consumer that we have time to spare.
5888
#
5989
# Python is not parallel but the transform operations
60-
# are usually OpenCV native so release the GIL.
90+
# are typically OpenCV native so release the GIL.
6191
#
6292
# Really just trying to avoid spinning up additional
6393
# native threads and overheads of additional
6494
# producer/consumer queues since this one was generally
6595
# idle grabbing frames.
6696
if self.transform:
6797
frame = self.transform(frame)
98+
while True:
99+
try:
100+
# try to put it into the queue
101+
self.Q.put(frame, True, 0.5)
102+
break
103+
except Full:
104+
# after timeout seconds check if we are still not stopped
105+
if self.stopped:
106+
raise EndOfStreamException()
107+
except EndOfStreamException:
108+
# if we got stopped or reached eos
109+
self.stopped = True
110+
break
111+
self.stream.release()
68112

69-
# add the frame to the queue
70-
self.Q.put(frame)
71-
else:
72-
time.sleep(0.1) # Rest for 10ms, we have a full queue
113+
def dim(self):
114+
width = int(self.stream.get(cv2.CAP_PROP_FRAME_WIDTH))
115+
height = int(self.stream.get(cv2.CAP_PROP_FRAME_HEIGHT))
73116

74-
self.stream.release()
117+
return [width, height]
75118

76119
def read(self):
77120
# return next frame in the queue

0 commit comments

Comments
 (0)