# Copyright (c) 2018, The SenseAct Authors. # All rights reserved. # # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. import logging import cv2 as cv import numpy as np from senseact.communicator import Communicator class CameraCommunicator(Communicator): """ Camera Communicator for interfacing with most common webcams supported by OpenCV. """ def __init__(self, res=(0, 0), device_id=0): """Inits the camera communicator with desired resolution and device_id. Args: res: a 2D tuple representing desired frame resolution. A default resolution is used if res contains zero values. device_id: either the device_id as integer or as string to the path of the device """ self._device_id = device_id # query camera default resolution or test custom resolution if needed and release it so that # the camera can be opened in the child process temp_cap = cv.VideoCapture(device_id) if not temp_cap.isOpened(): raise IOError("Unable to open camera on device id {}".format(self._device_id)) if res[0] == 0 or res[1] == 0: # get the default resolution, and extract depth from OpenCV typecode such as CV_8UC1 res = (temp_cap.get(cv.CAP_PROP_FRAME_WIDTH), temp_cap.get(cv.CAP_PROP_FRAME_HEIGHT), 1 + (int(temp_cap.get(cv.CAP_PROP_FORMAT)) >> 3)) else: temp_cap.set(cv.CAP_PROP_FRAME_WIDTH, res[0]) temp_cap.set(cv.CAP_PROP_FRAME_HEIGHT, res[1]) real_res = (temp_cap.get(cv.CAP_PROP_FRAME_WIDTH), temp_cap.get(cv.CAP_PROP_FRAME_HEIGHT), 1 + (int(temp_cap.get(cv.CAP_PROP_FORMAT)) >> 3)) if real_res[0] != res[0] or real_res[1] != res[1]: logging.warning("Custom resolution ({}, {}) not supported, reset to default.".format(res[0], res[1])) res = real_res temp_cap.release() self._res = res self._cap = None sensor_args = {'array_len': int(np.product(self._res)), 'array_type': 'd', 'np_array_type': 'd', } super(CameraCommunicator, self).__init__(use_sensor=True, use_actuator=False, sensor_args=sensor_args, actuator_args={}) def run(self): """Opening the video IO in the child process and invoke parent 'run' """ self._cap = cv.VideoCapture(self._device_id) if not self._cap.isOpened(): raise IOError("Unable to open camera on device id {}".format(self._device_id)) self._cap.set(cv.CAP_PROP_FRAME_WIDTH, self._res[0]) self._cap.set(cv.CAP_PROP_FRAME_HEIGHT, self._res[1]) # main process loop super(CameraCommunicator, self).run() # try to close the IO when the process end self._cap.release() def _sensor_handler(self): """Block and read the next available frame.""" # reading the original frame in (height, width, depth) dimension retval, frame = self._cap.read() if retval: # flatten and write to buffer self.sensor_buffer.write(frame.flatten()) def _actuator_handler(self): """There's no actuator available for cameras.""" raise RuntimeError("Camera Communicator does not have an actuator handler.")