コード例 #1
0
def process_frame_for_line(frame, image_format="PIL", process_image_width=320):
    cv2 = import_opencv()
    cv_frame = ImageFunctions.convert(frame, format="OpenCV")

    from imutils import resize

    resized_frame = resize(cv_frame, width=process_image_width)
    hsv_lower, hsv_upper = calculate_blue_limits()
    image_mask = color_mask(resized_frame, hsv_lower, hsv_upper)
    line_contour = find_largest_contour(image_mask)

    centroid = None
    scaled_image_centroid = None
    rectangle_dimensions = None
    angle = None
    if line_contour is not None:
        # find centroid of contour
        scaled_image_centroid = find_centroid(line_contour)
        centroid = center_reposition(scaled_image_centroid, resized_frame)
        bounding_rectangle = cv2.boundingRect(line_contour)
        rectangle_dimensions = bounding_rectangle[2:5]
        angle = get_object_target_lock_control_angle(centroid, resized_frame)

    robot_view_img = robot_view(resized_frame, image_mask, line_contour,
                                scaled_image_centroid)

    if image_format.lower() != "opencv":
        robot_view_img = ImageFunctions.convert(robot_view_img, format="PIL")

    return DotDict({
        "line_center": centroid,
        "robot_view": robot_view_img,
        "rectangle_dimensions": rectangle_dimensions,
        "angle": angle,
    })
コード例 #2
0
    def color_filter(self, frame, color: str = "red"):
        frame = ImageFunctions.convert(frame, format="OpenCV")
        mask = self.__get_color_mask(frame, color)
        filtered_image = cv2.bitwise_and(frame, frame, mask=mask)
        if self.format.lower() == "pil":
            filtered_image = ImageFunctions.convert(mask, format="PIL")

        return filtered_image
コード例 #3
0
    def __get_processed_current_frame(self):
        image = self.__frame_handler.frame

        if self.format.lower() == "opencv":
            image = ImageFunctions.convert(image, format="opencv")

        return image
コード例 #4
0
    def __call__(self, frame, color: Union[str, list] = "red"):
        def parse_colors(color_arg):
            colors = []
            if type(color_arg) == str:
                if color_arg not in VALID_COLORS:
                    raise ValueError(
                        f"Valid color values are {', '.join(VALID_COLORS[:-1])} or {VALID_COLORS[-1]}"
                    )
                colors = [color_arg]
            elif type(color_arg) in (list, tuple):
                if not set(color_arg).issubset(VALID_COLORS):
                    raise ValueError(
                        f"Valid color values are {', '.join(VALID_COLORS[:-1])} or {VALID_COLORS[-1]}"
                    )
                colors = color_arg
            if len(colors) > 3:
                raise ValueError("Cannot pass more than three colors.")

            return colors

        import_libs()
        frame = ImageFunctions.convert(frame, format="OpenCV")

        if self._frame_scaler is None:
            _, width = frame.shape[0:2]
            self._frame_scaler = width / self._image_processing_width

        for c in parse_colors(color):
            self.balls[c] = self.__find_most_likely_ball(
                ball=self.balls.get(c), frame=frame, color=c)

        robot_view = frame.copy()
        ball_data = DotDict({})
        for ball_color, ball_object in self.balls.items():
            ball_data[ball_color] = ball_object
            self.__draw_ball_contrail(robot_view, ball_object)
            if ball_object.found:
                self.__draw_ball_position(robot_view, ball_object)

        ball_data["robot_view"] = ImageFunctions.convert(
            robot_view, self.format)

        if self._print_fps:
            self._fps.update()

        return ball_data
コード例 #5
0
    def __prepare_face_data(self, frame, face, rectangle, center, features):
        face.original_detection_frame = frame

        if center is None:
            face.clear()
            face.robot_view = ImageFunctions.convert(frame,
                                                     format=self._format)
            return face

        # resize back to original frame resolution
        face.rectangle = tuple(
            (int(item * self._frame_scaler) for item in rectangle))
        face.center_default = tuple(
            (int(item * self._frame_scaler) for item in center))
        face.features = (features * self._frame_scaler).astype("int")

        face.robot_view = ImageFunctions.convert(self.__draw_on_frame(
            frame=frame.copy(), face=face),
                                                 format=self._format)
        return face
コード例 #6
0
    def process(self, frame):
        if isinstance(self.__format,
                      str) and self.__format.lower() == "opencv":
            frame = ImageFunctions.convert(frame, format="opencv")

        if self.__elapsed_frames % self.__frame_interval == 0:
            if self.callback_has_argument:
                self.__event_executor.submit(self.__generic_action_callback,
                                             frame)
            else:
                self.__event_executor.submit(self.__generic_action_callback)
        self.__elapsed_frames = (self.__elapsed_frames +
                                 1) % self.__frame_interval
コード例 #7
0
    def __call__(self, face):
        frame = ImageFunctions.convert(face.original_detection_frame.copy(),
                                       format="OpenCV")

        if not face.found:
            self.emotion.clear()
            self.emotion.robot_view = frame
            return self.emotion

        self.emotion = self.__get_emotion(frame=frame,
                                          face=face,
                                          emotion=self.emotion)

        return self.emotion
コード例 #8
0
ファイル: oled.py プロジェクト: pi-top/pi-top-Python-SDK
    def play_animated_image_file(self,
                                 file_path_or_url,
                                 background=False,
                                 loop=False):
        """Render an animated image to the screen from a file or URL.

        :param str file_path_or_url: A file path or URL to the image
        :param bool background: Set whether the image should be in a background thread
            or in the main thread.
        :param bool loop: Set whether the image animation should start again when it
            has finished
        """
        image = ImageFunctions.get_pil_image_from_path(file_path_or_url)
        self.play_animated_image(image, background, loop)
コード例 #9
0
ファイル: oled.py プロジェクト: pi-top/pi-top-Python-SDK
    def display_image(self, image, xy=None, invert=False):
        """Render a static image to the screen from a file or URL at a given
        position.

        The image should be provided as a PIL Image object.

        :param Image image: A PIL Image object to be rendered
        :param tuple xy: The position on the screen to render the image. If not
            provided or passed as `None` the image will be drawn in the top-left of
            the screen.
        :param bool invert: Set to True to flip the on/off state of each pixel in the image
        """
        self.__display(
            self.assistant.process_image(
                ImageFunctions.convert(image, format="PIL")),
            invert=invert,
        )
コード例 #10
0
    def __call__(self, frame):
        """Detect a face in an image frame.

        :param frame: Image frame in OpenCV or PIL format.
        :return: Face object containing data about the detected face.
        """
        self.__import_libs()
        frame = ImageFunctions.convert(frame, format="OpenCV")

        if self._frame_scaler is None:
            _, width = frame.shape[0:2]
            self._frame_scaler = (width / self._image_processing_width
                                  if self._image_processing_width is not None
                                  else 1)

        frame_to_process = self.__get_frame_to_process(frame=frame)

        if self._face_tracker is None:
            # if the face tracker has not been started, we first need to use face detection to find a face
            face_rectangle, face_center, face_features = self.__detect_largest_face(
                frame=frame_to_process)
            if face_center is not None and self._enable_tracking:
                # Face found, enable dlib correlation tracker for subsequent calls
                self.__start_tracker(frame=frame_to_process,
                                     rectangle=face_rectangle)
        else:
            # We are in face tracking mode, use dlib correlation tracker to track the face
            face_rectangle, face_center, face_features = self.__track_face(
                frame=frame_to_process)
            if face_center is None:
                self.__stop_tracker()
                # attempt to detect face since tracker has failed
                face_rectangle, face_center, face_features = self.__detect_largest_face(
                    frame=frame_to_process)

        self.face = self.__prepare_face_data(
            frame=frame,
            face=self.face,
            rectangle=face_rectangle,
            center=face_center,
            features=face_features,
        )
        if self._print_fps:
            self._fps.update()

        return self.face
コード例 #11
0
ファイル: oled.py プロジェクト: pi-top/pi-top-Python-SDK
    def display_image_file(self, file_path_or_url, xy=None, invert=False):
        """Render a static image to the screen from a file or URL at a given
        position.

        The display's positional properties (e.g. `top_left`, `top_right`) can be used to assist with
        specifying the `xy` position parameter.

        :param str file_path_or_url: A file path or URL to the image
        :param tuple xy: The position on the screen to render the image. If not
            provided or passed as `None` the image will be drawn in the top-left of
            the screen.
        :param bool invert: Set to True to flip the on/off state of each pixel in the image
        """
        self.display_image(
            ImageFunctions.get_pil_image_from_path(file_path_or_url),
            xy=xy,
            invert=invert,
        )
コード例 #12
0
    def __get_emotion(self, frame, face, emotion):
        """Emotion detection is carried out by taking the 68 face feature
        landmark positions found using the dlib landmark detector and putting
        them through a trained SVC model (in onnx format) that predicts the
        most likely emotion. The prediction outputs probabilities for each
        emotion type which are then put through a moving average filter to
        smooth the output. If being used for non-realtime applications (on
        static images) then the apply_mean_filter class attribute should be set
        to False.

        :param frame: Original camera frame used to detect the face (in OpenCV format), used for drawing robot_view.
        :param face: Face object obtained from FaceDetector
        :param emotion: Emotion object to store the resulting prediction data

        :return: Emotion object that was passed into this function.
        """
        def get_svc_feature_vector(features, face_angle):
            """The 68 face feature landmark positions need to be put into the
            same format as was used to train the SVC model. The basic process
            is as follows:

                1. Use the face angle to apply a rotation matrix to orient the face features so that the eyes lie on a
                horizontal line.

                2. Find the mean (x, y) coordinate of the resulting face features so we can transform the (x, y)
                face feature coordinates to be zero-centered.

                3. Find the interpupillary distance from the left and right eye center to normalize the (x, y)
                coordinates so that the resulting face features are independent of face scale.

                Note: this will not normalize the face features to lie between 0 and 1 as is typically done when
                pre-processing data to go into a machine learning algorithm. Previously, the diagonal of the face
                rectangle was used to accomplish this but because the face rectangle from face detection comes in
                discreet sizes (based on the pyramid layers), this had poor performance because a continuous scaling
                factor is desired. Interpupillary distance is a good metric since the variance across humans is quite
                small, and the data used for training contains a wide selection of face types to accommodate for this.
                Sklearn's StandardScaler() was added to the SVC pipeline to ensure the data lies between 0 and 1 before
                the classification is carried out - this does not completely remove the need for scaling here as was
                found during testing.

                4. After rotation, transformation and scaling, the resulting (x, y) coordinates are flatted into a numpy
                array in the form array([[x1, y1, x2, y2, ... x68, y68]]) with shape (1, 136). This is now in the format
                 to send to the onnx SVC model for emotion classification.

            :param features: 68 landmark face feature (x, y) coordinates in a 68x2 numpy array (found from FaceDetector)
            :param face_angle: face angle from Face object (found from FaceDetector).
            :return: 1x136 feature vector to put through the onnx SVC model.
            """
            rotation_matrix = np.array([
                [
                    np.cos(np.radians(face_angle)),
                    -np.sin(np.radians(face_angle))
                ],
                [
                    np.sin(np.radians(face_angle)),
                    np.cos(np.radians(face_angle))
                ],
            ])

            face_features_rotated = rotation_matrix.dot(features.T).T
            face_feature_mean = face_features_rotated.mean(axis=0)

            left_eye_center = np.mean(
                face_features_rotated[self.left_eye_start:self.left_eye_end],
                axis=0)
            right_eye_center = np.mean(
                face_features_rotated[self.right_eye_start:self.right_eye_end],
                axis=0)

            interpupillary_distance = np.linalg.norm(left_eye_center -
                                                     right_eye_center)

            feature_vector = []
            for landmark in face_features_rotated:
                relative_vector = (landmark -
                                   face_feature_mean) / interpupillary_distance
                feature_vector.append(relative_vector[0])
                feature_vector.append(relative_vector[1])

            return np.asarray([feature_vector])

        if len(face.features) != 68:
            raise ValueError(
                "This function is only compatible with dlib's 68 landmark feature predictor."
            )

        X = get_svc_feature_vector(face.features, face.angle)

        # Run feature vector through onnx model and convert results to a numpy array
        probabilities = np.asarray(
            list(
                self._emotion_model.run(
                    None, {self._onnx_input_node_name: X.astype(np.float32)
                           })[1][0].values()))

        if self._apply_mean_filter:
            self._probability_mean_array, probabilities = running_mean(
                self._probability_mean_array, probabilities)

        # Get the index of the most likely emotion type and associate to corresponding emotion string
        max_index = int(np.argmax(probabilities))
        emotion.type = self.emotion_types[max_index]
        emotion.confidence = round(probabilities[max_index], 2)

        emotion.robot_view = ImageFunctions.convert(
            self.__draw_on_frame(frame=frame.copy(),
                                 face=face,
                                 emotion=self.emotion),
            format=self._format,
        )

        return emotion
コード例 #13
0
 def format(self, format_value):
     ImageFunctions.image_format_check(format_value)
     self._format = format_value.lower()
コード例 #14
0
 def __init__(self, path: str):
     self.path = path
     self.data = ImageFunctions.get_pil_image_from_path(self.path)
     if self.data is None:
         raise AttributeError(f"Couldn't load image {path}")