77from queue import Queue
88from threading import Thread , Event
99from pcv .interact import DoNothing , waitKey
10+ from source import VideoSource
1011
1112
1213class BlockingVideoWriter (cv2 .VideoWriter ):
@@ -137,7 +138,7 @@ def suggested_codec(cls, filename, exclude=[]):
137138
138139 @classmethod
139140 def from_camera (cls , filename , camera , fourcc = None , isColor = True ,
140- apiPreference = None , fps = - 3 , ** kwargs ):
141+ apiPreference = None , fps = - 3 , frameSize = None , ** kwargs ):
141142 ''' Returns a VideoWriter based on the properties of the input camera.
142143
143144 'filename' is the name of the file to save to.
@@ -150,12 +151,16 @@ def from_camera(cls, filename, camera, fourcc=None, isColor=True,
150151 If no processing is occurring, 'camera' is suggested, otherwise
151152 it is generally best to measure the frame output.
152153 Defaults to -3, to measure over 3 frames.
154+ 'frameSize' is an integer tuple of (width, height)/(cols, rows).
155+ If left as None, uses `camera.get` to retrieve width and height.
153156 'kwrags' are any additional keyword arguments for initialisation.
154157
155158 '''
156159 if fourcc is None :
157160 fourcc = cls .suggested_codec (filename )
158- frameSize = tuple (int (camera .get (dim )) for dim in ('width' ,'height' ))
161+ if frameSize is None :
162+ frameSize = tuple (int (camera .get (dim ))
163+ for dim in ('width' , 'height' ))
159164
160165 if fps == 'camera' :
161166 fps = camera .get ('fps' )
@@ -302,21 +307,37 @@ def __init__(msg='User quit manually', *args, **kwargs):
302307 super ().__init__ (msg , * args , ** kwargs )
303308
304309
305- class ContextualVideoCapture (cv2 .VideoCapture ):
306- ''' A cv2.VideoCapture with a context manager for releasing. '''
310+ class OpenCVSource (cv2 .VideoCapture ):
311+ ''' A class to provide opencv's VideoCapture as a VideoSource backend. '''
312+ # more properties + descriptions can be found in the docs:
313+ # https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html#gaeb8dd9c89c10a5c63c139bf7c4f5704d
307314 properties = {
308315 'fps' : cv2 .CAP_PROP_FPS ,
309316 'mode' : cv2 .CAP_PROP_MODE ,
310317 'width' : cv2 .CAP_PROP_FRAME_WIDTH ,
311318 'height' : cv2 .CAP_PROP_FRAME_HEIGHT ,
312319 'backend' : cv2 .CAP_PROP_BACKEND ,
313320 }
314- # more properties + descriptions can be found in the docs:
315- # https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html#gaeb8dd9c89c10a5c63c139bf7c4f5704d
316321
322+ def get (self , property ):
323+ try :
324+ return super ().get (self .properties .get (property , property ))
325+ except TypeError : # property must be an unknown string
326+ return super ().get (eval ('cv2.CAP_PROP_' + property .upper ()))
327+
328+ def set (self , property , value ):
329+ try :
330+ return super ().set (self .properties .get (property , property ), value )
331+ except TypeError : # 'property' must be an unknown string
332+ return super ().set (eval ('cv2.CAP_PROP_' + property .upper ))
333+
334+
335+ class ContextualVideoCapture (VideoSource ):
336+ ''' A video-capturing class with a context manager for releasing. '''
337+
317338 def __init__ (self , id , * args , display = 'frame' , delay = None , quit = ord ('q' ),
318339 play_pause = ord (' ' ), pause_effects = {}, play_commands = {},
319- destroy = - 1 , ** kwargs ):
340+ destroy = - 1 , source = OpenCVSource , ** kwargs ):
320341 ''' A pausable, quitable, iterable video-capture object
321342 with context management.
322343
@@ -353,9 +374,12 @@ def __init__(self, id, *args, display='frame', delay=None, quit=ord('q'),
353374 to destroy all active opencv windows, a string of a specific window
354375 name, or a list of window names to close. If left as -1, destroys
355376 the window specified in 'display'.
377+ 'source' is a class which acts like a video source, by implementing at
378+ minimum the methods 'get', 'set', 'read', 'grab', 'retrieve',
379+ 'open', 'isOpened', and 'release'.
356380
357381 '''
358- super ().__init__ (id , * args , ** kwargs )
382+ super ().__init__ (source , id , * args , ** kwargs )
359383 self ._id = id
360384 self .display = display
361385 self ._delay = delay
@@ -460,7 +484,7 @@ def headless_stream(self):
460484 if not read_success : break # camera disconnected
461485
462486 def record_stream (self , filename , show = True , mouse_handler = DoNothing (),
463- writer = VideoWriter ):
487+ writer = VideoWriter , ** kwargs ):
464488 ''' Capture and record stream, with optional display.
465489
466490 'filename' is the file to save to.
@@ -474,7 +498,7 @@ def record_stream(self, filename, show=True, mouse_handler=DoNothing(),
474498 to better ensure a consistent output framerate.
475499
476500 '''
477- with writer .from_camera (filename , self ) as writer , mouse_handler :
501+ with writer .from_camera (filename , self , ** kwargs ) as writer , mouse_handler :
478502 for read_success , frame in self :
479503 if read_success :
480504 if show :
@@ -483,20 +507,6 @@ def record_stream(self, filename, show=True, mouse_handler=DoNothing(),
483507 else :
484508 break # camera disconnected
485509
486- def get (self , property ):
487- ''' Return the value of 'property' if it exists, else 0.0. '''
488- try :
489- return super ().get (self .properties .get (property , property ))
490- except TypeError : # property must be an unknown string
491- return super ().get (eval ('cv2.CAP_PROP_' + property .upper ()))
492-
493- def set (self , property , value ):
494- ''' Attempts to set 'property' to 'value', returning success. '''
495- try :
496- return super ().set (self .properties .get (property , property ), value )
497- except TypeError : # 'property' must be an unknown string
498- return super ().set (eval ('cv2.CAP_PROP_' + property .upper ))
499-
500510 def read (self , image = None ):
501511 if image is not None :
502512 status , image = super ().read (image )
@@ -537,7 +547,7 @@ def measure_framerate(self, frames):
537547 return count / (perf_counter () - start )
538548
539549 def __repr__ (self ):
540- return f"{ self .__class__ .__name__ } (camera_id={ repr ( self ._id ) } )"
550+ return f"{ self .__class__ .__name__ } (camera_id={ self ._id !r } )"
541551
542552
543553class Camera (SlowCamera ):
@@ -687,7 +697,7 @@ def read(self, image=None):
687697class VideoReader (LockedCamera ):
688698 ''' A class for reading video files. '''
689699 properties = {
690- ** ContextualVideoCapture .properties ,
700+ ** OpenCVSource .properties ,
691701 'frame' : cv2 .CAP_PROP_POS_FRAMES ,
692702 'codec' : cv2 .CAP_PROP_FOURCC ,
693703 'timestamp' : cv2 .CAP_PROP_POS_MSEC ,
0 commit comments