class ThreadedEngine(object): def __init__(self, source, engine): self.source = source self.engine = engine self.thread_manager = ThreadManager(self) self.pred = None def __next__(self): return self.get_prediction() def _thread(self): predictions = self.inference_gen() for prediction in predictions: self.pred = prediction self.thread_manager.set() def inference_gen(self): while True: self.frame = next(self.source) yield self.engine.invoke(self.frame) def get_prediction(self): self.thread_manager.wait() return self.pred, self.frame def get_max_length(self): return self.engine.get_max_length() def label(self, pred): return self.engine.label(pred) def start(self): self.thread_manager.start()
class BaseCamera(object): ''' Base class for streaming video in a background thread. This class continuously pulls new images in a thread. When a separate thread calls the get() function to retrieve a frame, the ThreadManager blocks until a new frame comes in and then returns a frame to all listeners. This ensures that no duplicate frames are retreived. Attributes: source (str): The file path of the video source. For example, this could be "/dev/video0". Run v4l2-ctl --list-devices to find your device. resolution (tuple[int]): Stores the camera resolution. frame (array[int]): Stores the current frame. thread_manager: A separate class that handles incoming requests for frames. ''' def __init__(self, source, resolution): ''' Sets the video source and creates the thread manager. Args: source (str): The file path of the video stream. resolution (tuple[int]): The camera resolution. ''' self.source = source self.resolution = resolution self.frame = None # current frame is stored here by background thread self.thread_manager = ThreadManager(self) def __iter__(self): ''' Returns itself as an iterator. Since the class is structured around generators, there is no need for a separate iterator. ''' return self def __next__(self): ''' Returns the newest frame. This operator allows the class usage to be abstracted. The user can call next(source), and the source can be any video stream that implements this operator. Returns: The newest frame. ''' return self.get_frame() def _thread(self): ''' Continuously pulls new frames from the camera. An infinite generator is used to pull new frames. Once a new frame is pulled, the thread manager is set, notifying all listeners and handing them the new frame. If there have been no listeners for 10 seconds, then the thread stops. ''' frames_iterator = self.frames() for frame in frames_iterator: self.frame = frame # Send signal to listeners self.thread_manager.set() # if there haven't been any listeners asking for frames in # the last 10 seconds then stop the thread if self.thread_manager.time_lapsed() > 10: frames_iterator.close() print('Stopping camera thread due to inactivity.') break self.thread_manager.stop() def start(self): ''' Starts the streaming thread. ''' self.thread_manager.start() def get_frame(self): ''' Waits for a new frame and returns it. ''' self.thread_manager.wait() return self.frame def get_resolution(self): ''' Returns the camera resolution. ''' return self.resolution @abstractmethod def frames(self): ''' Generator that continuously yields frames. This method must be implemented by child classes. It should be an infinite while loop that yields new camera frames. Yields: The newest camera frame. ''' pass