Skip to content

Commit c71531e

Browse files
committed
latest version from python-examples-cv
1 parent 30b4ac9 commit c71531e

File tree

1 file changed

+251
-0
lines changed

1 file changed

+251
-0
lines changed

camera_stream.py

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
##########################################################################
2+
3+
# threaded frame capture from camera to avoid camera frame buffering delays
4+
# (always delivers the latest frame from the camera)
5+
6+
# Copyright (c) 2018-2021 Toby Breckon, Durham University, UK
7+
# Copyright (c) 2015-2016 Adrian Rosebrock, http://www.pyimagesearch.com
8+
# MIT License (MIT)
9+
10+
# based on code from this tutorial, with changes to make object method call
11+
# compatible with cv2.VideoCapture(src) as far as possible, optional OpenCV
12+
# Transparent API support (disabled by default) and improved thread management:
13+
# https://www.pyimagesearch.com/2015/12/21/increasing-webcam-fps-with-python-and-opencv/
14+
15+
##########################################################################
16+
17+
# suggested basic usage - as per example in canny.py found at:
18+
# https://github.com/tobybreckon/python-examples-cv/blob/master/canny.py
19+
20+
# try:
21+
# import camera_stream
22+
# cap = camera_stream.CameraVideoStream()
23+
# print("INFO: using CameraVideoStream() threaded capture")
24+
# except BaseException:
25+
# print("INFO: CameraVideoStream() module not found")
26+
# cap = cv2.VideoCapture()
27+
28+
# in the above example this makes use of the CameraVideoStream if it is
29+
# available (i.e. camera_stream.py is in the module search path) and
30+
# falls back to using cv2.VideoCapture otherwise
31+
32+
# OpenCV T-API usage - alternative usage to enable OpenCV Transparent API
33+
# h/w acceleration where available on all subsequent processing of image
34+
# ....
35+
# import camera_stream
36+
# cap = camera_stream.CameraVideoStream(use_tapi=True)
37+
# ....
38+
39+
##########################################################################
40+
41+
# import the necessary packages
42+
43+
from threading import Thread
44+
import cv2
45+
import sys
46+
import atexit
47+
48+
##########################################################################
49+
50+
# handle older versions of OpenCV (that had a different constuctor
51+
# prototype for cv2.VideoCapture() it appears) semi-gracefully
52+
53+
(majorCV, minorCV, _) = cv2.__version__.split(".")
54+
if ((majorCV <= '3') and (minorCV <= '4')):
55+
raise NameError('OpenCV version < 3.4,'
56+
+ ' not compatible with CameraVideoStream()')
57+
58+
##########################################################################
59+
60+
# set up global variables and atexit() function to facilitate safe thread exit
61+
# without a segfault from the VideoCapture object as experienced on some
62+
# platforms
63+
# (as __del__ and __exit__ are not called outside a 'with' construct)
64+
65+
exitingNow = False # global flag for program exit
66+
threadList = [] # list of current threads (i.e. multi-camera/thread safe)
67+
68+
###########################
69+
70+
71+
def closeDownAllThreadsCleanly():
72+
global exitingNow
73+
global threadList
74+
75+
# set exit flag to cause each thread to exit
76+
77+
exitingNow = True
78+
79+
# for each thread wait for it to exit
80+
81+
for thread in threadList:
82+
thread.join()
83+
84+
###########################
85+
86+
87+
atexit.register(closeDownAllThreadsCleanly)
88+
89+
##########################################################################
90+
91+
92+
class CameraVideoStream:
93+
def __init__(self, src=None, backend=None,
94+
name="CameraVideoStream", use_tapi=False):
95+
96+
# initialize the thread name
97+
self.name = name
98+
99+
# initialize the variables used to indicate if the thread should
100+
# be stopped or suspended
101+
self.stopped = False
102+
self.suspend = False
103+
104+
# set these to null values initially
105+
self.grabbed = 0
106+
self.frame = None
107+
108+
# set OpenCV Transparent API usage
109+
110+
self.tapi = use_tapi
111+
112+
# set some sensible backends for real-time video capture from
113+
# directly connected hardware on a per-OS basis,
114+
# that can we overidden via the open() method
115+
if sys.platform.startswith('linux'): # all Linux
116+
self.backend_default = cv2.CAP_V4L
117+
elif sys.platform.startswith('win'): # MS Windows
118+
self.backend_default = cv2.CAP_DSHOW
119+
elif sys.platform.startswith('darwin'): # macOS
120+
self.backend_default = cv2.CAP_QT
121+
else:
122+
self.backend_default = cv2.CAP_ANY # auto-detect via OpenCV
123+
124+
# if a source was specified at init, proceed to open device
125+
if not(src is None):
126+
self.open(src, backend)
127+
128+
def open(self, src=0, backend=None):
129+
130+
# determine backend to specified by user
131+
if (backend is None):
132+
backend = self.backend_default
133+
134+
# check if aleady opened via init method
135+
if (self.grabbed > 0):
136+
return True
137+
138+
# initialize the video camera stream and read the first frame
139+
# from the stream
140+
self.camera = cv2.VideoCapture(src, backend)
141+
(self.grabbed, self.frame) = self.camera.read()
142+
143+
# only start the thread if in-fact the camera read was successful
144+
if (self.grabbed):
145+
# create the thread to read frames from the video stream
146+
thread = Thread(target=self.update, name=self.name, args=())
147+
148+
# append thread to global array of threads
149+
threadList.append(thread)
150+
151+
# get thread id we will use to address thread on list
152+
self.threadID = len(threadList) - 1
153+
154+
# start thread and set it to run in background
155+
threadList[self.threadID].daemon = True
156+
threadList[self.threadID].start()
157+
158+
return (self.grabbed > 0)
159+
160+
def update(self):
161+
# keep looping infinitely until the thread is stopped
162+
while True:
163+
# if the thread indicator variable is set or exiting, stop the
164+
# thread
165+
if self.stopped or exitingNow:
166+
self.grabbed = 0 # set flag to ensure isOpen() returns False
167+
self.camera.release() # cleanly release camera hardware
168+
return
169+
170+
# otherwise, read the next frame from the stream
171+
# provided we are not suspended
172+
173+
if not(self.suspend):
174+
(self.grabbed, self.frame) = self.camera.read()
175+
176+
def grab(self):
177+
# return status of most recent grab by the thread
178+
return self.grabbed
179+
180+
def retrieve(self):
181+
# same as read() in the context of threaded capture
182+
return self.read()
183+
184+
def read(self):
185+
# return the frame most recently read
186+
if (self.tapi):
187+
# return OpenCV Transparent API UMat frame for H/W acceleration
188+
return (self.grabbed, cv2.UMat(self.frame))
189+
# return standard numpy frame
190+
return (self.grabbed, self.frame)
191+
192+
def isOpened(self):
193+
# indicate that the camera is open successfully
194+
return (self.grabbed > 0)
195+
196+
def release(self):
197+
# indicate that the thread should be stopped
198+
self.stopped = True
199+
200+
def set(self, property_name, property_value):
201+
# set a video capture property (behavior as per OpenCV manual for
202+
# VideoCapture)
203+
204+
# first suspend thread
205+
self.suspend = True
206+
207+
# set value - wrapping it in grabs() so it takes effect
208+
self.camera.grab()
209+
ret_val = self.camera.set(property_name, property_value)
210+
self.camera.grab()
211+
212+
# whilst we are still suspended flush the frame buffer held inside
213+
# the object by reading a new frame with new settings otherwise a race
214+
# condition will exist between the thread's next call to update() after
215+
# it un-suspends and the next call to read() by the object user
216+
(self.grabbed, self.frame) = self.camera.read()
217+
218+
# restart thread by unsuspending it
219+
self.suspend = False
220+
221+
return ret_val
222+
223+
def get(self, property_name):
224+
# get a video capture property (behavior as per OpenCV manual for
225+
# VideoCapture)
226+
return self.camera.get(property_name)
227+
228+
def getBackendName(self):
229+
# get a video capture backend (behavior as per OpenCV manual for
230+
# VideoCapture)
231+
return self.camera.getBackendName()
232+
233+
def getExceptionMode(self):
234+
# get a video capture exception mode (behavior as per OpenCV manual for
235+
# VideoCapture)
236+
return self.camera.getExceptionMode()
237+
238+
def setExceptionMode(self, enable):
239+
# get a video capture exception mode (behavior as per OpenCV manual for
240+
# VideoCapture)
241+
return self.camera.setExceptionMode(enable)
242+
243+
def __del__(self):
244+
self.stopped = True
245+
self.suspend = True
246+
247+
def __exit__(self, exec_type, exc_value, traceback):
248+
self.stopped = True
249+
self.suspend = True
250+
251+
##########################################################################

0 commit comments

Comments
 (0)