コード例 #1
0
class IdsWrapper(object):
    def __init__(self, loggingQueue, moduleConfig):
        """ Create a new IdsWrapper instance. 
        IdsWrapper uses the Windows IDS DLL, wraps C calls in Python.
        Tested using the IDS XS 2.0 USB camera
        """

        self.config = moduleConfig
        self.isOpen = False
        self.width = 0
        self.height = 0
        self.bitspixels = 0
        self.py_width = 0
        self.py_height = 0
        self.py_bitspixel = 0
        self.cam = None
        self.pcImgMem = c_char_p()  #create placeholder for image memory
        self.pid = c_int()

        # Setup logger
        self.logger = ThreadsafeLogger(loggingQueue, __name__)

        # Load the correct dll
        try:
            if architecture()[0] == '64bit':
                self.logger.info('Using IDS camera with 64-bit architecture')
                self.uEyeDll = cdll.LoadLibrary(
                    "C:/Windows/System32/uEye_api_64.dll")
            else:
                self.logger.info('Using IDS camera with 32-bit architecture')
                self.uEyeDll = cdll.LoadLibrary(
                    "C:/Windows/System32/uEye_api.dll")
        except Exception as e:
            self.logger.error(
                'Failed to load IDS DLL: %s . Are you sure you are using an IDS camera?'
                % e)

    def isOpened(self):
        """ Return camera open status"""
        return self.isOpen

    def set(self, key, value):
        """ Set the py_width, py_height or py_bitspixel properties """
        if key == 'py_width':
            self.py_width = value
        elif key == 'py_height':
            self.py_height = value
        else:
            self.py_bitspixel = value

    def allocateImageMemory(self):
        """ Wrapped call to allocate image memory """
        ret = self.uEyeDll.is_AllocImageMem(self.cam, self.width,
                                            self.height, self.bitspixel,
                                            byref(self.pcImgMem),
                                            byref(self.pid))
        if ret == IS_SUCCESS:
            self.logger.info("Successfully allocated image memory")
        else:
            self.logger.error(
                'Memory allocation failed, no camera with value ' +
                str(self.cam.value) + ' | Error code: ' + str(ret))
            return

    def setImageMemory(self):
        """ Wrapped call to set image memory """
        ret = self.uEyeDll.is_SetImageMem(self.cam, self.pcImgMem, self.pid)
        if ret == IS_SUCCESS:
            self.logger.info("Successfully set image memory")
        else:
            self.logger.error("Failed to set image memory; error code: " +
                              str(ret))
            return

    def beginCapture(self):
        """ Wrapped call to begin capture """
        ret = self.uEyeDll.is_CaptureVideo(self.cam, c_long(IS_DONT_WAIT))
        if ret == IS_SUCCESS:
            self.logger.info("Successfully began video capture")
        else:
            self.logger.error("Failed to begin video capture; error code: " +
                              str(ret))
            return

    def initImageData(self):
        """ Initialize the ImageData numpy array """
        self.ImageData = np.ones((self.py_height, self.py_width),
                                 dtype=np.uint8)

    def setCTypes(self):
        """ Set C Types for width, height, and bitspixel properties"""
        self.width = c_int(self.py_width)
        self.height = c_int(self.py_height)
        self.bitspixel = c_int(self.py_bitspixel)

    def start(self):
        """ Start capturing frames on another thread as a daemon """
        self.updateThread = Thread(target=self.update)
        self.updateThread.setDaemon(True)
        self.updateThread.start()
        return self

    def initializeCamera(self):
        """ Wrapped call to initialize camera """
        ret = self.uEyeDll.is_InitCamera(byref(self.cam), self.hWnd)
        if ret == IS_SUCCESS:
            self.logger.info("Successfully initialized camera")
        else:
            self.logger.error("Failed to initialize camera; error code: " +
                              str(ret))
            return

    def enableAutoExit(self):
        """ Wrapped call to allow allocated memory to be dropped on exit. """
        ret = self.uEyeDll.is_EnableAutoExit(self.cam, c_uint(1))
        if ret == IS_SUCCESS:
            self.logger.info("Successfully enabled auto exit")
        else:
            self.logger.error("Failed to enable auto exit; error code: " +
                              str(ret))
            return

    def setDisplayMode(self):
        """ Wrapped call to set display mode to DIB """
        ret = self.uEyeDll.is_SetDisplayMode(self.cam, c_int(IS_SET_DM_DIB))
        if ret == IS_SUCCESS:
            self.logger.info("Successfully set camera to DIB mode")
        else:
            self.logger.error("Failed to set camera mode; error code: " +
                              str(ret))
            return

    def setColorMode(self):
        """ Wrapped call to set camera color capture mode """
        ret = self.uEyeDll.is_SetColorMode(self.cam, c_int(IS_CM_SENSOR_RAW8))
        if ret == IS_SUCCESS:
            self.logger.info("Successfully set color mode")
        else:
            self.logger.error("Failed to set color mode; error code: " +
                              str(ret))
            return

    def setCompressionFactor(self):
        """ Wrapped call to set image compression factor.
        Required for long USB lengths when bandwidth is constrained, lowers quality.
        """
        ret = self.uEyeDll.is_DeviceFeature(
            self.cam, IS_DEVICE_FEATURE_CMD_SET_JPEG_COMPRESSION,
            byref(c_int(self.config['CompressionFactor'])),
            c_uint(INT_BYTE_SIZE))
        if ret == IS_SUCCESS:
            self.logger.info("Successfully set compression factor to: " +
                             str(self.config['CompressionFactor']))
        else:
            self.logger.error(
                "Failed to set compression factor; error code: " + str(ret))
            return

    def setPixelClock(self):
        """ Wrapped call to set pixel clock.
        Required for long USB lengths when bandwidth is constrained
        Lowers frame rate and increases motion blur. 
        """
        ret = self.uEyeDll.is_PixelClock(
            self.cam, IS_PIXELCLOCK_CMD_SET,
            byref(c_uint(self.config['PixelClock'])), c_uint(INT_BYTE_SIZE))
        if ret == IS_SUCCESS:
            self.logger.info("Successfully set pixel clock to: " +
                             str(self.config['PixelClock']))
        else:
            self.logger.error("Failed to set pixel clock; error code: " +
                              str(ret))
            return

    def setTrigger(self):
        """ Wrapped call to set trigger type to software trigger. """
        ret = self.uEyeDll.is_SetExternalTrigger(
            self.cam, c_uint(IS_SET_TRIGGER_SOFTWARE))
        if ret == IS_SUCCESS:
            self.logger.info("Successfully set software trigger")
        else:
            self.logger.error("Failed to set software trigger; error code: " +
                              str(ret))
            return

    def setImageProfile(self):
        """ Wrapped call to set image format.
        Sets resolution of the capture to UXGA. More modes available in idsConsts.py.
        """
        ret = self.uEyeDll.is_ImageFormat(self.cam,
                                          c_uint(IMGFRMT_CMD_SET_FORMAT),
                                          byref(c_int(UXGA)),
                                          c_uint(INT_BYTE_SIZE))
        if ret == IS_SUCCESS:
            self.logger.info("Successfully set camera image profile")
        else:
            self.logger.error(
                "Failed to set camera image profile; error code: " + str(ret))
            return

    def open(self):
        """ Open connection to IDS camera, set various modes. """
        self.cam = c_uint32(0)
        self.hWnd = c_voidp()

        self.initializeCamera()
        self.enableAutoExit()
        self.setDisplayMode()
        self.setColorMode()
        self.setCompressionFactor()
        self.setPixelClock()
        self.setTrigger()
        self.setImageProfile()

        # Declare video open
        self.isOpen = True

        self.logger.info('Successfully opened camera')

    def update(self):
        """ Loop to update frames and copy to ImageData variable. """
        while True:
            if not self.isOpen:
                return
            self.uEyeDll.is_CopyImageMem(
                self.cam, self.pcImgMem, self.pid,
                self.ImageData.ctypes.data_as(c_char_p))

    def read(self):
        """ Read frame currently available in ImageData variable. """
        try:
            return True, self.ImageData
        except Exception as e:
            self.logger.error('Error getting image data: %r' % e)
            return False, None

    def exit(self):
        """ Close camera down, release memory. """
        self.uEyeDll.is_ExitCamera(self.cam)
        self.isOpen = False
        self.logger.info('Closing wrapper and camera')
        return
コード例 #2
0
class MQTTClientModule(ModuleProcess):
    """ Threaded MQTT client for processing and publishing outbound messages"""

    def __init__(self, baseConfig, pInBoundEventQueue, pOutBoundEventQueue, loggingQueue):

        super(MQTTClientModule, self).__init__()
        self.config = baseConfig
        self.alive = True
        self.inQueue = pInBoundEventQueue

        # Module config
        self.moduleConfig = configLoader.load(self.loggingQueue, __name__)

        # Constants
        self._keepAlive = self.moduleConfig['MqttKeepAlive']
        self._feedName = self.moduleConfig['MqttFeedName']
        self._username = self.moduleConfig['MqttUsername']
        self._key = self.moduleConfig['MqttKey']
        self._host = self.moduleConfig['MqttHost']
        self._port = self.moduleConfig['MqttPort']
        self._publishJson = self.moduleConfig['MqttPublishJson']

        # MQTT setup
        self._client = mqtt.Client()
        self._client.username_pw_set(self._username, self._key)
        self._client.on_connect    = self.on_connect
        self._client.on_disconnect = self.on_disconnect
        self._client.on_message    = self.on_message
        self.mqttConnected = False

        # Logging setup
        self.logger = ThreadsafeLogger(loggingQueue, "MQTT")

    def check_ss_version(self):
        #check for min version met
        self.logger.info('Module version %s' %(__version__))
        if LooseVersion(self.config['ss_version']) < LooseVersion(self.moduleConfig['MinSimpleSensorVersion']):
            self.logger.error('This module requires a min SimpleSensor %s version.  This instance is running version %s' %(self.moduleConfig['MinSimpleSensorVersion'],self.config['ss_version']))
            return False
        return True

    def on_connect(self, client, userdata, flags, rc):
        self.logger.debug('MQTT onConnect called')
        # Result code 0 is success
        if rc == 0:
            self.mqttConnected = True

            # Subscribe to feed here
        else:
            self.logger.error('MQTT failed to connect: %s'%rc)
            raise RuntimeError('MQTT failed to connect: %s'%rc)

    def on_disconnect(self, client, userdata, rc):
        self.logger.debug('MQTT onDisconnect called')
        self.mqttConnected = False
        if rc != 0:
            self.logger.debug('MQTT disconnected unexpectedly: %s'%rc)
            self.handle_reconnect(rc)

    def handle_reconnect(self, result_code):
        pass

    def on_message(self, client, userdata, msg):
        self.logger.debug('MQTT onMessage called for client: %s'%client)

    def connect(self):
        """ Connect to MQTT broker
        Skip calling connect if already connected.
        """
        if self.mqttConnected:
            return

        self._client.connect(self._host, port=self._port, keepalive=self._keepAlive)

    def disconnect(self):
        """ Check if connected"""
        if self.mqttConnected:
            self._client.disconnect()

    def subscribe(self, feed=False):
        """Subscribe to feed, defaults to feed specified in config"""
        if not feed: feed = _feedName
        self._client.subscribe('{0}/feeds/{1}'.format(self._username, feed))

    def publish(self, value, feed=False):
        """Publish a value to a feed"""
        if not feed: feed = _feedName
        self._client.publish('{0}/feeds/{1}'.format(self._username, feed), payload=value)

    def publish_face_values(self, message):
        """ Publish face detection values to individual MQTT feeds
        Parses _extendedData.predictions.faceAttributes property
        """
        try:
            for face in message.extended_data['predictions']:
                faceAttrs = face['faceAttributes']
                for key in faceAttrs:
                    if type(faceAttrs[key]) is dict:
                        val = self.flatten_dict(faceAttrs[key])
                        print('val: ', val)
                    else:
                        val = faceAttrs[key]
                    self.publish(val, key)
        except Exception as e:
            self.logger.error('Error publishing values: %s'%e)

    def flatten_dict(self, aDict):
        """ Get average of simple dictionary of numerical values """
        try:
            val = float(sum(aDict[key] for key in aDict)) / len(aDict)
        except Exception as e:
            self.logger.error('Error flattening dict, returning 0: %s'%e)
        return val or 0
  
    def publish_json_message(self, message):
        self.publish(message.stringify())

    def stringify_message(self, message):
        """ Dump into JSON string """
        return json.dumps(message.__dict__).encode('utf8')

    def process_queue(self):
        """ Process incoming messages. """

        while self.alive:
            # Pump the loop
            self._client.loop(timeout=1)
            if (self.inQueue.empty() == False):
                try:
                    message = self.inQueue.get(block=False,timeout=1)
                    if message is not None and self.mqttConnected:
                        if message.topic.upper() == "SHUTDOWN":
                            self.logger.debug("SHUTDOWN command handled")
                            self.shutdown()
                        else:
                            # Send message as string or split into channels
                            if self._publishJson:
                                self.publish_json_message(message)
                            elif self._publishFaceData:
                                self.publish_face_values(message)
                            else:
                                self.publish_values(message)

                except Exception as e:
                    self.logger.error("MQTT unable to read queue : %s " %e)
            else:
                time.sleep(.25)

    def shutdown(self):
        self.logger.info("Shutting down")
        self.alive = False
        time.sleep(1)
        self.exit = True

    def run(self):
       if not self.check_ss_version():
            #cant run with wrong version so we return early
            return False

        """ Thread start method"""
        self.logger.info("Running MQTT")

        self.connect()
        self.alive = True

        # Start queue loop
        self.process_queue()
コード例 #3
0
class WebsocketServerModule(ModuleProcess):
    def __init__(self, baseConfig, pInBoundQueue, pOutBoundQueue,
                 loggingQueue):

        # super(WebsocketServerModule, self).__init__()
        ModuleProcess.__init__(self, baseConfig, pInBoundQueue, pOutBoundQueue,
                               loggingQueue)
        self.alive = False
        self.config = baseConfig
        self.inQueue = pInBoundQueue  # inQueue are messages from the main process to websocket clients
        self.outQueue = pOutBoundQueue  # outQueue are messages from clients to main process
        self.websocketServer = None
        self.loggingQueue = loggingQueue
        self.threadProcessQueue = None

        # Configs
        self.moduleConfig = configLoader.load(self.loggingQueue, __name__)

        # Constants
        self._port = self.moduleConfig['WebsocketPort']
        self._host = self.moduleConfig['WebsocketHost']

        # logging setup
        self.logger = ThreadsafeLogger(loggingQueue, __name__)

    def run(self):
        if not self.check_ss_version():
            #cant run with wrong version so we return early
            return False
        """ Main thread entry point.

        Sets up websocket server and event callbacks.
        Starts thread to monitor inbound message queue.
        """

        self.logger.info("Starting websocket server")
        self.alive = True
        self.listen()

        self.websocketServer = WebsocketServer(self._port, host=self._host)
        self.websocketServer.set_fn_new_client(self.new_websocket_client)
        self.websocketServer.set_fn_message_received(
            self.websocket_message_received)
        self.websocketServer.run_forever()

    def check_ss_version(self):
        #check for min version met
        self.logger.info('Module version %s' % (__version__))
        if LooseVersion(self.config['ss_version']) < LooseVersion(
                self.moduleConfig['MinSimpleSensorVersion']):
            self.logger.error(
                'This module requires a min SimpleSensor %s version.  This instance is running version %s'
                % (self.moduleConfig['MinSimpleSensorVersion'],
                   self.config['ss_version']))
            return False
        return True

    def new_websocket_client(self, client, server):
        """ Client joined callback - called whenever a new client joins. """

        self.logger.debug("Client joined")

    def websocket_message_received(self, client, server, message):
        """ Message received callback - called whenever a new message is received. """

        self.logger.debug('Message received: %s' % message)
        message = json.loads(message)
        self.logger.info("message jsond: %s" % message)
        _msg = Message(topic=message['topic'], sender_id=message['sender_id'])
        if 'sender_type' in message:
            _msg.sender_type = message['sender_type']
        if 'recipients' in message:
            _msg.recipients = message['recipients']
        if 'extended_data' in message:
            _msg.extended_data = message['extended_data']

        self.put_message(_msg)

    def listen(self):
        self.threadProcessQueue = Thread(target=self.process_queue)
        self.threadProcessQueue.setDaemon(True)
        self.threadProcessQueue.start()

    def shutdown(self):
        """ Handle shutdown message. 
        Close and shutdown websocket server.
        Join queue processing thread.
        """

        self.logger.info("Shutting down websocket server")

        try:
            self.logger.info("Closing websocket")
            self.websocketServer.server_close()
        except Exception as e:
            self.logger.error("Websocket close error : %s " % e)

        try:
            self.logger.info("Shutdown websocket")
            self.websocketServer.shutdown()
        except Exception as e:
            self.logger.error("Websocket shutdown error : %s " % e)

        self.alive = False

        self.threadProcessQueue.join()

        time.sleep(1)
        self.exit = True

    def handle_message(self, message):
        """ Send message to listening clients. """
        self.websocketServer.send_message_to_all(json.dumps(message.__dict__))

    def process_queue(self):
        """ Monitor queue of messages from main process to this thread. """

        while self.alive:
            if (self.inQueue.empty() == False):
                try:
                    message = self.inQueue.get(block=False, timeout=1)
                    if message is not None:
                        if message.topic.upper() == "SHUTDOWN":
                            self.logger.debug("SHUTDOWN handled")
                            self.shutdown()
                        else:
                            self.handle_message(message)
                except Exception as e:
                    self.logger.error("Websocket unable to read queue : %s " %
                                      e)
            else:
                time.sleep(.25)
コード例 #4
0
class CollectionPoint(ModuleProcess):
    def __init__(self, baseConfig, pInBoundQueue, pOutBoundQueue,
                 loggingQueue):
        """ Initialize new CamCollectionPoint instance.
        Setup queues, variables, configs, predictionEngines, constants and loggers.
        """

        # ModuleProcess.__init__(self, baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue)
        super().__init__(baseConfig, pInBoundQueue, pOutBoundQueue,
                         loggingQueue)

        if not self.check_opencv_version("3.", cv2):
            print(
                "OpenCV version {0} is not supported. Use 3.x for best results."
                .format(self.get_opencv_version()))

        # Queues
        self.outQueue = pOutBoundQueue  #messages from this thread to the main process
        self.inQueue = pInBoundQueue
        self.loggingQueue = loggingQueue

        # Variables
        self.video = None
        self.needsReset = False
        self.needsResetMux = False
        self.alive = False

        # Configs
        self.moduleConfig = configLoader.load(
            self.loggingQueue, __name__)  #Get the config for this module
        self.config = baseConfig

        # Prediction engine
        self.imagePredictionEngine = AzureImagePredictor(
            moduleConfig=self.moduleConfig, loggingQueue=loggingQueue)

        # Constants
        self._useIdsCamera = self.moduleConfig['UseIdsCamera']
        self._minFaceWidth = self.moduleConfig['MinFaceWidth']
        self._minFaceHeight = self.moduleConfig['MinFaceHeight']
        self._minNearestNeighbors = self.moduleConfig['MinNearestNeighbors']
        self._maximumPeople = self.moduleConfig['MaximumPeople']
        self._facePixelBuffer = self.moduleConfig['FacePixelBuffer']
        self._collectionThreshold = self.moduleConfig['CollectionThreshold']
        self._showVideoStream = self.moduleConfig['ShowVideoStream']
        self._sendBlobs = self.moduleConfig['SendBlobs']
        self._blobWidth = self.moduleConfig['BlobWidth']
        self._blobHeight = self.moduleConfig['BlobHeight']
        self._captureWidth = self.moduleConfig['CaptureWidth']
        self._captureHeight = self.moduleConfig['CaptureHeight']
        self._bitsPerPixel = self.moduleConfig['BitsPerPixel']
        self._resetEventTimer = self.moduleConfig['ResetEventTimer']
        self._collectionPointType = self.moduleConfig['CollectionPointType']
        self._collectionPointId = self.moduleConfig['CollectionPointId']

        # Logger
        self.logger = ThreadsafeLogger(loggingQueue, __name__)

    def run(self):
        """ Main thread method, run when the thread's start() function is called.
        Controls flow of detected faces and the MultiTracker. 
        Determines when to send 'reset' events to clients and when to send 'found' events. 
        This function contains various comments along the way to help understand the flow.
        You can use this flow, extend it, or build your own.
        """
        if not self.check_ss_version():
            #cant run with wrong version so we return early
            return False

        self.alive = True

        # Monitor inbound queue on own thread
        self.listen()

        self.initialize_camera()

        # Load the OpenCV Haar classifier to detect faces
        curdir = os.path.dirname(__file__)
        cascadePath = os.path.join(curdir, 'classifiers', 'haarcascades',
                                   'haarcascade_frontalface_default.xml')
        faceCascade = cv2.CascadeClassifier(cascadePath)

        self.mmTracker = MultiTracker("KCF", self.moduleConfig,
                                      self.loggingQueue)

        # Setup timer for FPS calculations
        start = time.time()
        frameCounter = 1
        fps = 0

        # Start timer for collection events
        self.collectionStart = time.time()

        ok, frame = self.video.read()
        if not ok:
            self.logger.error('Cannot read video file')
            self.shutdown()

        while self.alive:
            ok, frame = self.video.read()
            if not ok:
                self.logger.error('Error while reading frame')
                break

            # Image alts
            if self._useIdsCamera:
                grayFrame = frame.copy()
                outputImage = cv2.cvtColor(frame, cv2.COLOR_GRAY2RGB)
            else:
                outputImage = frame.copy()
                grayFrame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)

            # Detect faces
            faces = faceCascade.detectMultiScale(
                grayFrame,
                scaleFactor=1.1,
                minNeighbors=self._minNearestNeighbors,
                minSize=(self._minFaceWidth, self._minFaceHeight))

            # If no faces in frame, clear tracker and start reset timer
            if len(faces
                   ) == 0 or self.mmTracker.length() > self._maximumPeople:
                self.mmTracker.clear()
                self.start_reset()

            # If there are trackers, update
            if self.mmTracker.length() > 0:
                ok, bboxes, failed = self.mmTracker.update(outputImage)
                if failed:
                    self.logger.error('Update trackers failed on: %s' %
                                      ''.join(str(s) for s in failed))

            for (x, y, w, h) in faces:
                # If faces are detected, engagement exists, do not reset
                self.needsReset = False

                # Optionally add buffer to face, can improve tracking/classification accuracy
                if self._facePixelBuffer > 0:
                    (x, y, w,
                     h) = self.apply_face_buffer(x, y, w, h,
                                                 self._facePixelBuffer,
                                                 outputImage.shape)

                # Get region of interest
                roi_gray = grayFrame[y:y + h, x:x + w]
                roi_color = outputImage[y:y + h, x:x + w]

                # If the tracker is valid and doesn't already exist, add it
                if self.valid_tracker(x, y, w, h):
                    self.logger.info('Adding tracker')
                    ok = self.mmTracker.add(bbox={
                        'x': x,
                        'y': y,
                        'w': w,
                        'h': h
                    },
                                            frame=outputImage)

                # Draw box around face
                if self._showVideoStream:
                    cv2.rectangle(outputImage, (x, y), (x + w, y + h),
                                  (0, 255, 0), 2)

            # If the time since last collection is more than the set threshold
            if not self.needsReset or (time.time() - self.collectionStart >
                                       self._collectionThreshold):
                # Check if the focal face has changed
                check, face = self.mmTracker.check_focus()
                if check:
                    predictions = self.get_predictions(grayFrame, face)
                    if predictions:
                        self.send_message(topic="update",
                                          data={
                                              'detectedTime':
                                              datetime.now().isoformat('T'),
                                              'predictions':
                                              predictions
                                          })

            frameCounter += 1
            elapsed = time.time() - start
            fps = frameCounter / max(abs(elapsed), 0.0001)
            if frameCounter > sys.maxsize:
                start = time.time()
                frameCounter = 1

            if self._showVideoStream:
                cv2.putText(outputImage, "%s FPS" % fps, (20, 20),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1,
                            cv2.LINE_AA)
                cv2.imshow("Faces found", outputImage)
                cv2.waitKey(1)

            if self._sendBlobs and frameCounter % 6 == 0:
                self.send_message(
                    topic="blob",
                    data={
                        'imageArr':
                        cv2.resize(outputImage,
                                   (self._blobWidth, self._blobHeight)),
                        'time':
                        datetime.now().isoformat('T')
                    })
                # self.putCPMessage(data = {
                #                     'imageArr': cv2.resize(outputImage, (self._blobWidth, self._blobHeight)) ,
                #                     'time': datetime.now().isoformat('T')
                #                     },
                #                   type="blob")

    def check_ss_version(self):
        #check for min version met
        self.logger.info('Module version %s' % (__version__))
        if LooseVersion(self.config['ss_version']) < LooseVersion(
                self.moduleConfig['MinSimpleSensorVersion']):
            self.logger.error(
                'This module requires a min SimpleSensor %s version.  This instance is running version %s'
                % (self.moduleConfig['MinSimpleSensorVersion'],
                   self.config['ss_version']))
            return False
        return True

    def get_predictions(self, grayFrame, face):
        """ Send face to predictionEngine as JPEG.
        Return predictions array or false if no face is found. 
        """

        faceArr = grayFrame[int(face[1]):int(face[1] + face[3]),
                            int(face[0]):int(face[0] + face[2])]
        img = Image.fromarray(faceArr)
        buff = io.BytesIO()
        img.save(buff, format="JPEG")

        predictions = self.imagePredictionEngine.get_prediction(
            buff.getvalue())
        if 'error' in predictions:
            return False
        return predictions

    def valid_tracker(self, x, y, w, h):
        """ Check if the coordinates are a newly detected face or already present in MultiTracker.
        Only accepts new tracker candidates every _collectionThreshold seconds.
        Return true if the object in those coordinates should be tracked.
        """
        if not self.needsReset or (time.time() - self.collectionStart >
                                   self._collectionThreshold):
            if (self.mmTracker.length() == 0
                    or not self.mmTracker.contains(bbox={
                        'x': x,
                        'y': y,
                        'w': w,
                        'h': h
                    })):
                return True
        return False

    def start_reset(self):
        """Start a timer from reset event.
        If timer completes and the reset event should still be sent, send it.
        """
        if self.needsResetMux:
            self.needsReset = True
            self.needsResetMux = False
            self.resetStart = time.time()

        if self.needsReset:
            if (time.time() - self.resetStart
                ) > 10:  # 10 seconds after last face detected
                self.send_message(data=None, type="reset")
                self.needsReset = False

    def apply_face_buffer(self, x, y, w, h, b, shape):
        x = x - b if x - b >= 0 else 0
        y = y - b if y - b >= 0 else 0
        w = w + b if w + b <= shape[1] else shape[1]
        h = h + b if h + b <= shape[0] else shape[0]
        return (x, y, w, h)

    def initialize_camera(self):
        # Using IDS camera
        if self._useIdsCamera:
            self.logger.info("Using IDS Camera")
            self.wrapper = IdsWrapper(self.loggingQueue, self.moduleConfig)
            if not (self.wrapper.isOpened()):
                self.wrapper.open()
            self.wrapper.set('py_width', self._captureWidth)
            self.wrapper.set('py_height', self._captureHeight)
            self.wrapper.set('py_bitspixel', self._bitsPerPixel)

            # Convert values to ctypes, prep memory locations
            self.wrapper.set_c_types()

            self.wrapper.allocate_image_memory()
            self.wrapper.set_image_memory()
            self.wrapper.begin_capture()
            self.wrapper.init_image_data()

            # Start video update thread
            self.video = self.wrapper.start()

        # Not using IDS camera
        else:
            # open first webcam available
            self.video = cv2.VideoCapture(0)
            if not (self.video.isOpened()):
                self.video.open()

            #set the resolution from config
            self.video.set(cv2.CAP_PROP_FRAME_WIDTH, self._captureWidth)
            self.video.set(cv2.CAP_PROP_FRAME_HEIGHT, self._captureHeight)

    def processQueue(self):
        self.logger.info(
            "Starting to watch collection point inbound message queue")
        while self.alive:
            if (self.inQueue.empty() == False):
                self.logger.info("Queue size is %s" % self.inQueue.qsize())
                try:
                    message = self.inQueue.get(block=False, timeout=1)
                    if message is not None:
                        if message == "SHUTDOWN":
                            self.logger.info("SHUTDOWN command handled on %s" %
                                             __name__)
                            self.shutdown()
                        else:
                            self.handleMessage(message)
                except Exception as e:
                    self.logger.error("Unable to read queue, error: %s " % e)
                    self.shutdown()
                self.logger.info("Queue size is %s after" %
                                 self.inQueue.qsize())
            else:
                time.sleep(.25)

    def handle_message(self, message):
        if message._topic == 'open-stream':
            self._sendBlobs = True
        elif message._topic == 'close-stream':
            self._sendBlobs = False

    def send_message(self, topic, data=None):
        message = self.build_message(topic, data)
        self.put_message(message)

    def build_message(self, topic, data):
        if topic == "reset":
            # Send reset message
            self.logger.info('Sending reset message')
            msg = Message(topic='reset',
                          sender_id=self._collectionPointId,
                          sender_type=self._collectionPointType,
                          extended_data=None,
                          recipients='communication_modules')
            return msg

        elif topic == "update":
            # Reset collection start and now needs needs reset
            collectionStart = time.time()
            self.needsResetMux = True

            self.logger.info('Sending found message')
            msg = Message(
                topic='face',
                sender_id=self._collectionPointId,
                sender_type=self._collectionPointType,
                extended_data=data['predictions'],
                recipients='communication_modules',
                timestamp=data['detectedTime'],
            )
            return msg

        elif topic == "blob":
            # Get numpy array as bytes
            img = Image.fromarray(data['imageArr'])
            buff = io.BytesIO()
            img.save(buff, format="JPEG")
            s = base64.b64encode(buff.getvalue()).decode("utf-8")

            eventExtraData = {}
            eventExtraData['imageData'] = s
            eventExtraData['dataType'] = 'image/jpeg'

            msg = Message(topic='blob',
                          sender_id=self._collectionPointId,
                          sender_type=self._collectionPointType,
                          extended_data=eventExtraData,
                          recipients='communication_modules')
            return msg

    # def putCPMessage(self, data, type):
    #     if type == "reset":
    #         # Send reset message
    #         self.logger.info('Sending reset message')
    #         msg = CollectionPointEvent(
    #             self._collectionPointId,
    #             self._collectionPointType,
    #             'Reset mBox',
    #             None)
    #         self.outQueue.put(msg)

    #     elif type == "update":
    #         # Reset collection start and now needs needs reset
    #         collectionStart = time.time()
    #         self.needsResetMux = True

    #         self.logger.info('Sending found message')
    #         msg = CollectionPointEvent(
    #             self._collectionPointId,
    #             self._collectionPointType,
    #             'Found face',
    #             data['predictions']
    #         )
    #         self.outQueue.put(msg)

    #     elif type == "blob":
    #         # Get numpy array as bytes
    #         img = Image.fromarray(data['imageArr'])
    #         buff = io.BytesIO()
    #         img.save(buff, format="JPEG")
    #         s = base64.b64encode(buff.getvalue()).decode("utf-8")

    #         eventExtraData = {}
    #         eventExtraData['imageData'] = s
    #         eventExtraData['dataType'] = 'image/jpeg'

    #         # Send found message
    #         # self.logger.info('Sending blob message')
    #         msg = CollectionPointEvent(
    #             self._collectionPointId,
    #             self._collectionPointType,
    #             'blob',
    #             eventExtraData,
    #             False
    #         )
    #         self.outQueue.put(msg)

    def shutdown(self):
        self.alive = False
        self.logger.info("Shutting down")
        if self._useIdsCamera and self.wrapper.is_open():
            self.wrapper.exit()
        cv2.destroyAllWindows()
        self.threadProcessQueue.join()
        time.sleep(1)
        self.exit = True

    #Custom methods for demo
    def get_opencv_version(self):
        import cv2 as lib
        return lib.__version__

    def check_opencv_version(self, major, lib=None):
        # if the supplied library is None, import OpenCV
        if lib is None:
            import cv2 as lib

        # return whether or not the current OpenCV version matches the
        # major version number
        return lib.__version__.startswith(major)