def __init__(self): # * Grab application context, logger and check if we have a valid input source self.context = Context.getInstance() self.logger = logging.getLogger(self.__class__.__name__) self.isOkay = False # conservative assumption: not okay till we're ready if self.context.options.input_source is None: raise Exception("No valid input source, nothing to do!") # * Open input source if self.context.isImage: self.camera = cv2.imread(self.context.options.input_source) if self.camera is None: raise Exception("Error opening input image file") elif self.context.isRemote: from net import ImageClient # to prevent circular dependency self.camera = ImageClient(protocol=self.context.remoteEndpoint['protocol'], host=self.context.remoteEndpoint['host'], port=self.context.remoteEndpoint['port']) # TODO Allow custom read_call param via command-line arg --rpc_read_call (or remoteEndpoint['path']?) # TODO Add ability to read remote URLs by (use a common isStatic flag for local and remote images) else: self.camera = cv2.VideoCapture(self.context.options.input_source) if self.camera is None or not self.camera.isOpened(): raise Exception("Error opening camera / input video file") # * Acquire logger and initialize other members self.logger = logging.getLogger(self.__class__.__name__) self.frameCount = 0 self.frameTimeStart = self.context.timeNow # synced with context self.timeDelta = 0.0 # * Set camera frame size (if this is a live camera; TODO enable frame resizing for videos and static images as well?) if not (self.context.isImage or self.context.isVideo or self.context.isRemote): #_, self.image = self.camera.read() # pre-grab # NOTE: If camera frame size is not one supported by the hardware, grabbed images are scaled to desired size, discarding aspect-ratio try: if self.context.options.camera_width != 'auto': self.camera.set(cv.CV_CAP_PROP_FRAME_WIDTH, int(self.context.options.camera_width)) if self.context.options.camera_height != 'auto': self.camera.set(cv.CV_CAP_PROP_FRAME_HEIGHT, int(self.context.options.camera_height)) except ValueError: self.logger.warning("Ignoring invalid camera frame size: {}x{}".format(self.context.options.camera_width, self.context.options.camera_height)) # * Check if this is a static image or camera/video/remote endpoint if self.context.isImage: # ** Supplied camera object should be an image, copy it self.image = self.camera else: if self.context.isVideo: # ** Read video properties, set video-specific variables self.videoNumFrames = int(self.camera.get(cv.CV_CAP_PROP_FRAME_COUNT)) if self.context.options.video_fps == 'auto': self.videoFPS = self.camera.get(cv.CV_CAP_PROP_FPS) else: try: self.videoFPS = float(self.context.options.video_fps) except ValueError: self.logger.warning("Invalid video FPS \"{}\"; switching to auto".format(self.self.context.options.video_fps)) self.videoFPS = self.camera.get(cv.CV_CAP_PROP_FPS) self.videoDuration = self.videoNumFrames / self.videoFPS self.logger.info("Video [init]: {:.3f} secs., {} frames at {:.2f} fps{}".format(self.videoDuration, self.videoNumFrames, self.videoFPS, (" (sync target)" if self.context.options.sync_video else ""))) if self.context.options.sync_video: self.videoFramesRepeated = 0 self.videoFramesSkipped = 0 # ** Grab test image and read common camera/video properties self.readFrame() # post-grab (to apply any camera prop changes made) if not self.context.isRemote: self.logger.info("Frame size: {}x{}".format(int(self.camera.get(cv.CV_CAP_PROP_FRAME_WIDTH)), int(self.camera.get(cv.CV_CAP_PROP_FRAME_HEIGHT)))) if self.context.isVideo: self.resetVideo() # * Read image properties (common to static image/camera/video) if self.image is not None: self.imageSize = (self.image.shape[1], self.image.shape[0]) self.logger.info("Image size: {}x{}".format(self.imageSize[0], self.imageSize[1])) self.isOkay = True # all good, so far else: self.logger.error("Empty image!")
def __init__(self): # * Grab application context, logger and check if we have a valid input source self.context = Context.getInstance() self.logger = logging.getLogger(self.__class__.__name__) self.isOkay = False # conservative assumption: not okay till we're ready if self.context.options.input_source is None: raise Exception("No valid input source, nothing to do!") # * Open input source if self.context.isImage: self.camera = cv2.imread(self.context.options.input_source) if self.camera is None: raise Exception("Error opening input image file") elif self.context.isRemote: from net import ImageClient # to prevent circular dependency self.camera = ImageClient( protocol=self.context.remoteEndpoint['protocol'], host=self.context.remoteEndpoint['host'], port=self.context.remoteEndpoint['port']) # TODO Allow custom read_call param via command-line arg --rpc_read_call (or remoteEndpoint['path']?) # TODO Add ability to read remote URLs by (use a common isStatic flag for local and remote images) else: self.camera = cv2.VideoCapture(self.context.options.input_source) if self.camera is None or not self.camera.isOpened(): raise Exception("Error opening camera / input video file") # * Acquire logger and initialize other members self.logger = logging.getLogger(self.__class__.__name__) self.frameCount = 0 self.frameTimeStart = self.context.timeNow # synced with context self.timeDelta = 0.0 # * Set camera frame size (if this is a live camera; TODO enable frame resizing for videos and static images as well?) if not (self.context.isImage or self.context.isVideo or self.context.isRemote): #_, self.image = self.camera.read() # pre-grab # NOTE: If camera frame size is not one supported by the hardware, grabbed images are scaled to desired size, discarding aspect-ratio try: if self.context.options.camera_width != 'auto': self.camera.set(cv.CV_CAP_PROP_FRAME_WIDTH, int(self.context.options.camera_width)) if self.context.options.camera_height != 'auto': self.camera.set(cv.CV_CAP_PROP_FRAME_HEIGHT, int(self.context.options.camera_height)) except ValueError: self.logger.warning( "Ignoring invalid camera frame size: {}x{}".format( self.context.options.camera_width, self.context.options.camera_height)) # * Check if this is a static image or camera/video/remote endpoint if self.context.isImage: # ** Supplied camera object should be an image, copy it self.image = self.camera else: if self.context.isVideo: # ** Read video properties, set video-specific variables self.videoNumFrames = int( self.camera.get(cv.CV_CAP_PROP_FRAME_COUNT)) if self.context.options.video_fps == 'auto': self.videoFPS = self.camera.get(cv.CV_CAP_PROP_FPS) else: try: self.videoFPS = float(self.context.options.video_fps) except ValueError: self.logger.warning( "Invalid video FPS \"{}\"; switching to auto". format(self.self.context.options.video_fps)) self.videoFPS = self.camera.get(cv.CV_CAP_PROP_FPS) self.videoDuration = self.videoNumFrames / self.videoFPS self.logger.info( "Video [init]: {:.3f} secs., {} frames at {:.2f} fps{}". format(self.videoDuration, self.videoNumFrames, self.videoFPS, (" (sync target)" if self.context.options.sync_video else ""))) if self.context.options.sync_video: self.videoFramesRepeated = 0 self.videoFramesSkipped = 0 # ** Grab test image and read common camera/video properties self.readFrame( ) # post-grab (to apply any camera prop changes made) if not self.context.isRemote: self.logger.info("Frame size: {}x{}".format( int(self.camera.get(cv.CV_CAP_PROP_FRAME_WIDTH)), int(self.camera.get(cv.CV_CAP_PROP_FRAME_HEIGHT)))) if self.context.isVideo: self.resetVideo() # * Read image properties (common to static image/camera/video) if self.image is not None: self.imageSize = (self.image.shape[1], self.image.shape[0]) self.logger.info("Image size: {}x{}".format( self.imageSize[0], self.imageSize[1])) self.isOkay = True # all good, so far else: self.logger.error("Empty image!")
class InputDevice(object): """Abstracts away the handling of static image, recorded video files and live camera as input.""" def __init__(self): # * Grab application context, logger and check if we have a valid input source self.context = Context.getInstance() self.logger = logging.getLogger(self.__class__.__name__) self.isOkay = False # conservative assumption: not okay till we're ready if self.context.options.input_source is None: raise Exception("No valid input source, nothing to do!") # * Open input source if self.context.isImage: self.camera = cv2.imread(self.context.options.input_source) if self.camera is None: raise Exception("Error opening input image file") elif self.context.isRemote: from net import ImageClient # to prevent circular dependency self.camera = ImageClient(protocol=self.context.remoteEndpoint['protocol'], host=self.context.remoteEndpoint['host'], port=self.context.remoteEndpoint['port']) # TODO Allow custom read_call param via command-line arg --rpc_read_call (or remoteEndpoint['path']?) # TODO Add ability to read remote URLs by (use a common isStatic flag for local and remote images) else: self.camera = cv2.VideoCapture(self.context.options.input_source) if self.camera is None or not self.camera.isOpened(): raise Exception("Error opening camera / input video file") # * Acquire logger and initialize other members self.logger = logging.getLogger(self.__class__.__name__) self.frameCount = 0 self.frameTimeStart = self.context.timeNow # synced with context self.timeDelta = 0.0 # * Set camera frame size (if this is a live camera; TODO enable frame resizing for videos and static images as well?) if not (self.context.isImage or self.context.isVideo or self.context.isRemote): #_, self.image = self.camera.read() # pre-grab # NOTE: If camera frame size is not one supported by the hardware, grabbed images are scaled to desired size, discarding aspect-ratio try: if self.context.options.camera_width != 'auto': self.camera.set(cv.CV_CAP_PROP_FRAME_WIDTH, int(self.context.options.camera_width)) if self.context.options.camera_height != 'auto': self.camera.set(cv.CV_CAP_PROP_FRAME_HEIGHT, int(self.context.options.camera_height)) except ValueError: self.logger.warning("Ignoring invalid camera frame size: {}x{}".format(self.context.options.camera_width, self.context.options.camera_height)) # * Check if this is a static image or camera/video/remote endpoint if self.context.isImage: # ** Supplied camera object should be an image, copy it self.image = self.camera else: if self.context.isVideo: # ** Read video properties, set video-specific variables self.videoNumFrames = int(self.camera.get(cv.CV_CAP_PROP_FRAME_COUNT)) if self.context.options.video_fps == 'auto': self.videoFPS = self.camera.get(cv.CV_CAP_PROP_FPS) else: try: self.videoFPS = float(self.context.options.video_fps) except ValueError: self.logger.warning("Invalid video FPS \"{}\"; switching to auto".format(self.self.context.options.video_fps)) self.videoFPS = self.camera.get(cv.CV_CAP_PROP_FPS) self.videoDuration = self.videoNumFrames / self.videoFPS self.logger.info("Video [init]: {:.3f} secs., {} frames at {:.2f} fps{}".format(self.videoDuration, self.videoNumFrames, self.videoFPS, (" (sync target)" if self.context.options.sync_video else ""))) if self.context.options.sync_video: self.videoFramesRepeated = 0 self.videoFramesSkipped = 0 # ** Grab test image and read common camera/video properties self.readFrame() # post-grab (to apply any camera prop changes made) if not self.context.isRemote: self.logger.info("Frame size: {}x{}".format(int(self.camera.get(cv.CV_CAP_PROP_FRAME_WIDTH)), int(self.camera.get(cv.CV_CAP_PROP_FRAME_HEIGHT)))) if self.context.isVideo: self.resetVideo() # * Read image properties (common to static image/camera/video) if self.image is not None: self.imageSize = (self.image.shape[1], self.image.shape[0]) self.logger.info("Image size: {}x{}".format(self.imageSize[0], self.imageSize[1])) self.isOkay = True # all good, so far else: self.logger.error("Empty image!") def readFrame(self): """Read a frame from camera/video. Not meant to be called directly.""" if self.context.isVideo and self.context.options.loop_video and self.frameCount >= self.videoNumFrames: framesDelivered = self.frameCount + self.videoFramesRepeated - self.videoFramesSkipped if self.context.options.sync_video else self.frameCount self.logger.info("Video [loop]: {:.3f} secs., {} frames at {:.2f} fps{}".format( self.timeDelta, framesDelivered, (framesDelivered / self.timeDelta) if self.timeDelta > 0.0 else 0.0, (" ({} repeated, {} skipped)".format(self.videoFramesRepeated, self.videoFramesSkipped) if self.context.options.sync_video else ""))) self.resetVideo() if self.context.options.sync_video: self.videoFramesRepeated = 0 self.videoFramesSkipped = 0 self.isOkay, self.image = self.camera.read() self.frameCount += 1 def read(self): """Read a frame from image/camera/video, handling video sync and loop.""" self.timeDelta = self.context.timeNow - self.frameTimeStart # synced with context if not self.context.isImage: if self.context.isVideo and self.context.options.sync_video: targetFrameCount = self.videoFPS * self.timeDelta diffFrameCount = targetFrameCount - self.frameCount #self.logger.debug("[syncVideo] timeDelta: {:06.3f}, frame: {:03d}, target: {:06.2f}, diff: {:+04.2f}".format(self.timeDelta, self.frameCount, targetFrameCount, diffFrameCount)) if diffFrameCount <= 0: self.videoFramesRepeated += 1 else: self.videoFramesSkipped += min(int(diffFrameCount), self.videoNumFrames - self.frameCount) while self.isOkay and self.frameCount < targetFrameCount: self.readFrame() if self.frameCount == 1: # a video reset occurred break else: self.readFrame() return self.isOkay def resetVideo(self): self.camera.set(cv.CV_CAP_PROP_POS_FRAMES, 0) self.frameCount = 0 self.frameTimeStart = self.context.timeNow # synced with context self.timeDelta = 0.0 # NOTE Depending on the actual keyframes in the video, a reset may not work correctly (some frames towards the end may be skipped everytime after the first reset) def close(self): if not self.context.isImage: # not valid for static images self.camera.release()
class InputDevice(object): """Abstracts away the handling of static image, recorded video files and live camera as input.""" def __init__(self): # * Grab application context, logger and check if we have a valid input source self.context = Context.getInstance() self.logger = logging.getLogger(self.__class__.__name__) self.isOkay = False # conservative assumption: not okay till we're ready if self.context.options.input_source is None: raise Exception("No valid input source, nothing to do!") # * Open input source if self.context.isImage: self.camera = cv2.imread(self.context.options.input_source) if self.camera is None: raise Exception("Error opening input image file") elif self.context.isRemote: from net import ImageClient # to prevent circular dependency self.camera = ImageClient( protocol=self.context.remoteEndpoint['protocol'], host=self.context.remoteEndpoint['host'], port=self.context.remoteEndpoint['port']) # TODO Allow custom read_call param via command-line arg --rpc_read_call (or remoteEndpoint['path']?) # TODO Add ability to read remote URLs by (use a common isStatic flag for local and remote images) else: self.camera = cv2.VideoCapture(self.context.options.input_source) if self.camera is None or not self.camera.isOpened(): raise Exception("Error opening camera / input video file") # * Acquire logger and initialize other members self.logger = logging.getLogger(self.__class__.__name__) self.frameCount = 0 self.frameTimeStart = self.context.timeNow # synced with context self.timeDelta = 0.0 # * Set camera frame size (if this is a live camera; TODO enable frame resizing for videos and static images as well?) if not (self.context.isImage or self.context.isVideo or self.context.isRemote): #_, self.image = self.camera.read() # pre-grab # NOTE: If camera frame size is not one supported by the hardware, grabbed images are scaled to desired size, discarding aspect-ratio try: if self.context.options.camera_width != 'auto': self.camera.set(cv.CV_CAP_PROP_FRAME_WIDTH, int(self.context.options.camera_width)) if self.context.options.camera_height != 'auto': self.camera.set(cv.CV_CAP_PROP_FRAME_HEIGHT, int(self.context.options.camera_height)) except ValueError: self.logger.warning( "Ignoring invalid camera frame size: {}x{}".format( self.context.options.camera_width, self.context.options.camera_height)) # * Check if this is a static image or camera/video/remote endpoint if self.context.isImage: # ** Supplied camera object should be an image, copy it self.image = self.camera else: if self.context.isVideo: # ** Read video properties, set video-specific variables self.videoNumFrames = int( self.camera.get(cv.CV_CAP_PROP_FRAME_COUNT)) if self.context.options.video_fps == 'auto': self.videoFPS = self.camera.get(cv.CV_CAP_PROP_FPS) else: try: self.videoFPS = float(self.context.options.video_fps) except ValueError: self.logger.warning( "Invalid video FPS \"{}\"; switching to auto". format(self.self.context.options.video_fps)) self.videoFPS = self.camera.get(cv.CV_CAP_PROP_FPS) self.videoDuration = self.videoNumFrames / self.videoFPS self.logger.info( "Video [init]: {:.3f} secs., {} frames at {:.2f} fps{}". format(self.videoDuration, self.videoNumFrames, self.videoFPS, (" (sync target)" if self.context.options.sync_video else ""))) if self.context.options.sync_video: self.videoFramesRepeated = 0 self.videoFramesSkipped = 0 # ** Grab test image and read common camera/video properties self.readFrame( ) # post-grab (to apply any camera prop changes made) if not self.context.isRemote: self.logger.info("Frame size: {}x{}".format( int(self.camera.get(cv.CV_CAP_PROP_FRAME_WIDTH)), int(self.camera.get(cv.CV_CAP_PROP_FRAME_HEIGHT)))) if self.context.isVideo: self.resetVideo() # * Read image properties (common to static image/camera/video) if self.image is not None: self.imageSize = (self.image.shape[1], self.image.shape[0]) self.logger.info("Image size: {}x{}".format( self.imageSize[0], self.imageSize[1])) self.isOkay = True # all good, so far else: self.logger.error("Empty image!") def readFrame(self): """Read a frame from camera/video. Not meant to be called directly.""" if self.context.isVideo and self.context.options.loop_video and self.frameCount >= self.videoNumFrames: framesDelivered = self.frameCount + self.videoFramesRepeated - self.videoFramesSkipped if self.context.options.sync_video else self.frameCount self.logger.info( "Video [loop]: {:.3f} secs., {} frames at {:.2f} fps{}".format( self.timeDelta, framesDelivered, (framesDelivered / self.timeDelta) if self.timeDelta > 0.0 else 0.0, (" ({} repeated, {} skipped)".format( self.videoFramesRepeated, self.videoFramesSkipped) if self.context.options.sync_video else ""))) self.resetVideo() if self.context.options.sync_video: self.videoFramesRepeated = 0 self.videoFramesSkipped = 0 self.isOkay, self.image = self.camera.read() self.frameCount += 1 def read(self): """Read a frame from image/camera/video, handling video sync and loop.""" self.timeDelta = self.context.timeNow - self.frameTimeStart # synced with context if not self.context.isImage: if self.context.isVideo and self.context.options.sync_video: targetFrameCount = self.videoFPS * self.timeDelta diffFrameCount = targetFrameCount - self.frameCount #self.logger.debug("[syncVideo] timeDelta: {:06.3f}, frame: {:03d}, target: {:06.2f}, diff: {:+04.2f}".format(self.timeDelta, self.frameCount, targetFrameCount, diffFrameCount)) if diffFrameCount <= 0: self.videoFramesRepeated += 1 else: self.videoFramesSkipped += min( int(diffFrameCount), self.videoNumFrames - self.frameCount) while self.isOkay and self.frameCount < targetFrameCount: self.readFrame() if self.frameCount == 1: # a video reset occurred break else: self.readFrame() return self.isOkay def resetVideo(self): self.camera.set(cv.CV_CAP_PROP_POS_FRAMES, 0) self.frameCount = 0 self.frameTimeStart = self.context.timeNow # synced with context self.timeDelta = 0.0 # NOTE Depending on the actual keyframes in the video, a reset may not work correctly (some frames towards the end may be skipped everytime after the first reset) def close(self): if not self.context.isImage: # not valid for static images self.camera.release()