Example #1
0
class CustomModelProcessor(BaseProcessor):
    def __init__(self):
        super().__init__()
        self.recognizer = Recognizer()
        self.detector = MaskDetector()
        self.face_detector = FaceDetector()
        self.dlibProcessor = DlibProcessor()

    def get_face_locations(self, image):
        return self.face_detector.detect_faces(image)

    def process_detected_faces(self, image, faces):
        if faces is None or len(faces) == 0:
            return

        detected_faces, locations = [], []
        for x, y in faces:
            detected_faces.append(x)
            locations.append(y)

        mask_per_person = self.detector.predict_mask(detected_faces)
        if len(mask_per_person) < len(faces):
            return
        all_without_mask = self.is_all_without_masks(mask_per_person)
        if all_without_mask:
            self.dlibProcessor.process_single_image(image)
        else:
            self.process_with_masks(faces, image, locations, mask_per_person)

    def process_with_masks(self, faces, image, locations, mask_per_person):
        for index in range(0, len(faces)):
            location = locations[index]
            (mask, withoutMask) = mask_per_person[index]

            # determine the class label and color we'll use to draw
            # the bounding box and text
            in_mask = mask > withoutMask
            label = "Mask" if in_mask else "No Mask"
            if label == "Mask":
                color = (0, 255, 0)
            else:
                color = (0, 0, 255)

            label = "{}: {:.2f}%".format(label, max(mask, withoutMask) * 100)
            image = self.draw_face_rectangle(image, location, label, color)

    def is_all_without_masks(self, mask_per_persons):
        if mask_per_persons is None or len(mask_per_persons) == 0:
            return True

        for index in range(0, len(mask_per_persons)):
            (mask, without_mask) = mask_per_persons[index]
            if mask > without_mask:
                return False
        return True
class FaceDetectionDoorbellRinger(FileSystemEventHandler):

    def __init__(self, vision_api_client, doorbell, min_confidence,
        timeout_secs, verbose=False):
        """This is a `watchdog` event handler which monitors a directory for
        newly created image files, checking them for the presence of faces, and
        ringing a doorbell if it believes strongly enough that someone is
        waiting.

        All Vision API face detection annotations will have an associated
        confidence value, which must exceed the specified minimum confidence
        threshold (`min_confidence`) in order to trigger a doorbell ring.

        We also do not want to repeatedly ring for the same group of visitors,
        so we do not even bother checking for faces if we are within the
        specified `timeout_secs`.

        The `Observer` object for this event handler is created inside of here
        for simplicity's sake.

        Only the `on_create()` method will be overridden here, as we do not
        care about other file-system events.

        Args:
            vision_api_client: `visionapi.VisionAPIClient` object
            doorbell: `doorbell.SlackDoorbell` object
            min_confidence: minimum confidence threshold for face detection
            timout_secs: number of seconds to wait before ringing again
            verbose: (optional) if True, print info message for every event
        Returns:
            `FaceDetectionDoorbellRinger` event handler
        """
        self._detector = FaceDetector(vision_api_client)
        self._doorbell = doorbell
        self._min_confidence = min_confidence
        self._timeout_secs = timeout_secs
        self._time_of_prev_ring = 0
        self._logger = Logger(LogLevel.ANY if verbose else LogLevel.INFO)

    def _file_is_allowed(self, event):
        return not event.is_directory and \
            event.src_path.split('.')[-1].lower() in ALLOWED_EXTENSIONS

    def _already_rang_doorbell(self):
        return int(time.time()) - self._time_of_prev_ring < self._timeout_secs

    def _get_confidence(self, image_path):
        try:
            faces = self._detector.detect_faces(image_path)
            confidence = sum(faces) / len(faces)
        except VisionAPIError as err:
            self._logger.warning(err, 'Treating this as 0% confidence')
            confidence = 0. 
        return confidence

    def _ring_doorbell(self, confidence):
        try:
            self._doorbell.ring(confidence=confidence)
            self._time_of_prev_ring = int(time.time())
        except SlackDoorbellError as err:
            # TODO: Not all failed rings should be critical
            self._logger.critical(err)
            self._observer.stop()

    def on_created(self, event):
        """Overrides the `watchdog.events.FileSystemEventHandler.on_created()`.
        
        When a new file is created in our watch directory:
            1. Check if this was an image file
            2. Check if we have already rang the doorbell recently
            3. Check if faces are present in the image
            4. Check if average confidence of faces exceeds threshold
            5. Ring doorbell
            6. Update the timeout clock

        Args:
            event: `watchdog.events.FileSystemEvent` object
        Returns:
            None
        """
        self._logger.debug('Event triggered')

        if self._file_is_allowed(event) and not self._already_rang_doorbell():
            self._logger.debug('Checking for faces')
            confidence = self._get_confidence(event.src_path)
            self._logger.debug('Confidence of faces: %f' % confidence)

            if confidence >= self._min_confidence:
                self._logger.info('Ringing doorbell')
                self._ring_doorbell(confidence)
            else:
                self._logger.debug('Did not ring doorbell')
        else:
            self._logger.debug('Ignored event')

    def run(self, motion_output_dir, sleep_secs, recursive=False):
        """Create and run a `watchdog.observers.Observer` over the `motion`
        process's output directory, triggering potential doorbell rings (see
        `on_created()` meothod of this class).

        Args:
            motion_output_dir: path to `motion` daemon image output directory
            sleep_secs: number of seconds to sleep between polling
            recursive: (optional) recursively watch directory
        Returns:
            None
        """
        observer = Observer()
        observer.schedule(self, motion_output_dir, recursive=recursive)
        observer.start()

        self._logger.info('Monitoring %s for images %srecursively' % (
            motion_output_dir, '' if recursive else 'non-'))

        try:
            while True:
                time.sleep(sleep_secs)
        except KeyboardInterrupt:
            observer.stop()
        observer.join()