-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvideo.py
161 lines (127 loc) · 4.07 KB
/
video.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import cv2
"""
Wrappers around the OpenCV VideoCapture and VideoWriter classes to give
them a more Pythonic interface
"""
class VideoCapture(cv2.VideoCapture):
"""
Extend cv2.VideoCapture so that it can be used as a context
manager and an iterator over the frames of the video.
Context manager
---------------
Ensures release() is called in case an error occurs
Iterator
--------
Returns frames from read() as long as the capture is opened and
the return value of read is Ture.
"""
def __init__(self, *args, **kwargs):
"""
Set up the VideoCapture. All parameters are forwarded to the
OpenCV VideoCapture object.
"""
super().__init__(*args, **kwargs)
self.stoppedIteration = False
## Context manager
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.release()
return False
## Random access
def __len__(self):
l = self.frame_count
if l < 0:
raise TypeError("VideoCapture source does not support random access")
return int(self.frame_count)
def __getitem__(self, i):
if 0 <= i < len(self):
self.set(cv2.CAP_PROP_POS_FRAMES, i)
elif -len(self) <= i < 0:
self.set(cv2.CAP_PROP_POS_FRAMES, len(self) + i)
else:
raise IndexError(f"index {i} out of range")
ret, frame = self.read()
if ret:
return frame
else:
raise RuntimeError(f"Couldn't read frame {i}")
## Iterable
def __iter__(self):
return self
def __next__(self):
if not self.isOpened():
self.stoppedIteration = True
raise StopIteration
ret, frame = self.read()
if ret and not self.stoppedIteration:
return frame
else:
# Iterators can't restart iteration after raising StopIteration
self.stoppedIteration = True
raise StopIteration
## Video properties
def __getattr__(self, name):
"""
Used to convert
VideoCapture.get(cv2.CAP_PROP_EXAMPLE_PROPERTY) to
VideoCapture.example_property.
"""
propertyName = f"CAP_PROP_{name.upper()}"
if propertyName in cv2.__dict__:
return self.get(cv2.__dict__[propertyName])
else:
return super().__getattr__(name)
def __setattr__(self, name, value):
propertyName = f"CAP_PROP_{name.upper()}"
if propertyName in cv2.__dict__:
return self.set(cv2.__dict__[propertyName], value)
else:
return super().__setattr__(name, value)
@property
def frame_width(self):
return int(self.get(cv2.CAP_PROP_FRAME_WIDTH))
@frame_width.setter
def set_frame_width(self, width):
return self.set(cv2.CAP_PROP_FRAME_WIDTH, width)
@property
def width(self):
return self.frame_width
@width.setter
def set_width(self, width):
self.frame_width = width
@property
def frame_height(self):
return int(self.get(cv2.CAP_PROP_FRAME_HEIGHT))
@frame_height.setter
def set_frame_height(self, height):
return self.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
@property
def height(self):
return self.frame_height
@height.setter
def set_height(self, height):
self.frame_height = height
@property
def shape(self):
return (self.height, self.width)
@shape.setter
def set_shape(self, shape):
self.height, self.width = shape
class VideoWriter(cv2.VideoWriter):
"""
Extend cv2.VideoWriter to provide a context manager.
"""
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.release()
return False
# Test: stream video from a webcam to window
if __name__ == "__main__":
with VideoCapture(0) as webcam:
for frame in webcam:
cv2.imshow("Frame", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()