class TreasurePickupSuccessDetector(Detector):
    TREASURE_COLOR_RANGE = ColorRange(Color.from_hsv(40, 20, 75), Color.from_hsv(70, 255, 255))
    DETECTION_CAMERA_ANGLE = Vector2(0, -80)
    RELATIVE_TREASURE_SIZE = Vector2(0.15, 0.117)
    DETECTION_RECT_CENTERED_SCALE = 1.25
    DETECTION_RECT_SCALE = Vector2(1, 1.25)

    def __init__(self):
        self._treasure_contour_filter = ContourFilter(min_size_scale = Vector2(0.5, 0.5), max_size_scale = Vector2(2, 2))

    def detect(self, image, gripper):
        self._treasure_contour_filter.target_size = image.size * self.RELATIVE_TREASURE_SIZE
        detection_zone_mask = self._create_detection_zone_mask(image, gripper)
        detection_zone_image = self._apply_mask(image, detection_zone_mask)
        treasure_mask = self._create_mask(detection_zone_image, self.TREASURE_COLOR_RANGE, opening_kernel_size = 10, closure_kernel_size = 20)
        treasure_contours = self._find_contours(treasure_mask, detection_zone_image)
        return self._detect_treasures_from_contours(treasure_contours) 

    def _create_detection_zone_mask(self, image, gripper):
        mask = Image.from_attributes(image.width, image.height, ColorMode.BLACK_AND_WHITE)
        detection_rect = Rect.from_center(gripper.location, image.size * self.RELATIVE_TREASURE_SIZE)
        detection_rect = detection_rect.scale_centered(self.DETECTION_RECT_CENTERED_SCALE).scale(self.DETECTION_RECT_SCALE)
        OpenCV.fill_rectangle(mask, detection_rect, Color.WHITE)
        self._log_detection_step("Treasure Detection Zone Mask", mask)
        return mask

    def _detect_treasures_from_contours(self, contours):
        filtered_contours = self._treasure_contour_filter.filter_contours(contours)
        if len(filtered_contours) == 0:
            raise DetectionError("Treasure pickup success detection failed. Detected contours: {0}.".format(len(filtered_contours)))
        return [self._create_treasure(filtered_contours[0])]

    def _create_treasure(self, contour):
        treasure_location = OpenCV.get_contour_center(contour)
        return Treasure(treasure_location)
class TreasurePickupDetector(Detector):
    TREASURE_COLOR_RANGE = ColorRange(Color.from_hsv(40, 100, 75), Color.from_hsv(70, 255, 255))
    DETECTION_CAMERA_ANGLE = Vector2(0, -80)
    RELATIVE_TREASURE_SIZE = Vector2(0.15, 0.03)
    CAMERA_PHYSICAL_SCALE = 32

    def __init__(self):
        self._treasure_contour_filter = ContourFilter(min_size_scale = Vector2(0.75, 0.5), max_size_scale = Vector2(1.25, 5))

    def detect(self, image, gripper):
        self._treasure_contour_filter.target_size = image.size * self.RELATIVE_TREASURE_SIZE
        detection_zone_mask = self._create_detection_zone_mask(image, gripper)
        detection_zone_image = self._apply_mask(image, detection_zone_mask)
        treasure_mask = self._create_mask(detection_zone_image, self.TREASURE_COLOR_RANGE, opening_kernel_size = 10, closure_kernel_size = 10)
        treasure_contours = self._find_contours(treasure_mask, detection_zone_image)
        return self._detect_treasures_from_contours(treasure_contours) 

    def _create_detection_zone_mask(self, image, gripper):
        mask = Image.from_attributes(image.width, image.height, ColorMode.BLACK_AND_WHITE)
        detection_rect = Rect.from_size(image.size).clone()
        detection_rect.bottom = gripper.location.y
        OpenCV.fill_rectangle(mask, detection_rect, Color.WHITE)
        self._log_detection_step("Treasure Detection Zone Mask", mask)
        return mask

    def _detect_treasures_from_contours(self, contours):
        filtered_contours = self._treasure_contour_filter.filter_contours(contours)
        treasures = []
        for contour in filtered_contours:
           treasures.append(self._create_treasure(contour))
        return treasures

    def _create_treasure(self, contour):
        treasure_location = OpenCV.get_contour_center(contour)
        return Treasure(treasure_location)
Beispiel #3
0
class GripperDetector(Detector):
    GRIPPER_COLOR_RANGE = ColorRange(Color.from_hsv(82, 60, 100), Color.from_hsv(168, 255, 255))
    GRIPPER_DETECTION_CAMERA_ANGLE = Vector2(0, -90)
    RELATIVE_GRIPPER_SIZE = Vector2(0.1, 0.075)
    DETECTION_RECT_SCALE = Vector2(1, 0.8)

    def __init__(self):
        self._gripper_contour_filter = ContourFilter(min_size_scale = 0.75, max_size_scale = 1.25)

    def detect(self, image):
        self._gripper_contour_filter.target_size = image.size * self.RELATIVE_GRIPPER_SIZE
        detection_zone_mask = self._create_detection_zone_mask(image)
        detection_zone_image = self._apply_mask(image, detection_zone_mask)
        gripper_mask = self._create_mask(detection_zone_image, self.GRIPPER_COLOR_RANGE, opening_kernel_size = 5, closure_kernel_size = 10)
        contours = self._find_contours(gripper_mask, image)
        return self._detect_gripper_from_contours(contours)

    def _create_detection_zone_mask(self, image):
        mask = Image.from_attributes(image.width, image.height, ColorMode.BLACK_AND_WHITE)
        detection_rect = Rect.from_size(image.size.clone())
        detection_rect = detection_rect.scale(self.DETECTION_RECT_SCALE)
        OpenCV.fill_rectangle(mask, detection_rect, Color.WHITE)
        self._log_detection_step("Gripper Detection Zone Mask", mask)
        return mask

    def _detect_gripper_from_contours(self, contours):
        filtered_contours = self._gripper_contour_filter.filter_contours(contours, use_rotated_rect = True)
        if len(filtered_contours) != 1:
            raise DetectionError("The gripper recognition pattern could not be detected.")
        return self._create_gripper(filtered_contours[0])

    def _create_gripper(self, contour):
        gripper_location = OpenCV.get_contour_center(contour)
        return Gripper(gripper_location)
Beispiel #4
0
class IslandDetector(Detector):

    MAX_SEGMENT_COUNT = 12
    PLAYFIELD_RECT_MASK_SCALE = Vector2(1, 1.1)
    
    def __init__(self, coordinate_factory):
        self._island_factory = IslandFactory(coordinate_factory)
        self._simple_shape_finder = SimpleShapeFinder()
        self._contour_filter = ContourFilter(min_size_scale = 0.75, max_size_scale = 1.5, min_aspect_ratio = 0.8, max_aspect_ratio = 1.2, min_area_scale = 0.5, max_area_scale = 2)

    def detect(self, image, playfield, color_ranges, island_color, robot):
        Logger.get_instance().log(self, "Island Detection", details = "Detection started. Color: {0}.".format(island_color))
        self._contour_filter.target_size = Coordinate.translate_physical_to_game(Island.AVERAGE_PHYSICAL_SIZE_CM, playfield)
        self._contour_filter.target_area = self._contour_filter.target_size.area
        return self._perform_detection(image.clone(), playfield, color_ranges, island_color, robot)

    def _perform_detection(self, image, playfield, color_ranges, island_color, robot):
        island_zone = playfield.rect.clone().scale_centered(self.PLAYFIELD_RECT_MASK_SCALE)
        island_zone_mask = self._create_island_zone_mask(image, island_zone, robot)
        island_zone_image = self._apply_mask(image, island_zone_mask)
        island_mask = self._create_mask(island_zone_image, color_ranges, closure_kernel_size = 7)
        contours = self._find_contours(island_mask, island_zone_image)
        return self._detect_islands_from_contours(contours, island_color)

    def _create_island_zone_mask(self, image, island_zone, robot):
        mask = Image.from_attributes(image.width, image.height, ColorMode.BLACK_AND_WHITE)
        OpenCV.fill_rectangle(mask, island_zone, Color.WHITE)
        self._mask_robot(mask, robot)
        self._log_detection_step("Island Zone Mask", mask)
        return mask

    def _mask_robot(self, mask, robot):
        if robot is not None:
            OpenCV.fill_rotated_rectangle(mask, robot.get_rotated_rect(CoordinateSystem.CAMERA), Color.BLACK)

    def _detect_islands_from_contours(self, contours, island_color):
        islands = []
        filtered_contours = self._contour_filter.filter_contours(contours)
        for contour in filtered_contours:
            islands.append(self._create_island_from_contour(contour, island_color))
        Logger.get_instance().log(self, "Island Detection", details = "Detected islands: {0}.".format(len(islands)))
        return islands

    def _create_island_from_contour(self, contour, island_color):
        simplified_path, enclosing_circle = self._simple_shape_finder.find_shape(contour, self.MAX_SEGMENT_COUNT)
        return self._island_factory.create_island(IslandType.from_segment_count(simplified_path.segment_count), island_color, simplified_path, enclosing_circle)
class ChargingStationDetector(Detector):

    RECOGNITION_PATTERN_COLOR_RANGES = [ColorRange(Color.from_hsv(0, 100, 100), Color.from_hsv(20, 255, 255)), ColorRange(Color.from_hsv(340, 100, 100), Color.from_hsv(360, 255, 255))]

    def __init__(self, coordinate_factory):
        self._coordinate_factory = coordinate_factory
        self._contour_filter = ContourFilter(min_size_scale = 0.75, max_size_scale = 1.5, min_aspect_ratio = 0.75, max_aspect_ratio = 1.25)

    def detect(self, image, playfield):
        self._contour_filter.target_size = Coordinate.translate_physical_to_game(ChargingStation.RECOGNITION_PATTERN_PHYSICAL_SIZE_CM, playfield)
        return self._perform_detection(image, playfield)

    def _perform_detection(self, image, playfield):
        detection_zone = self._define_detection_zone(playfield)
        detection_zone_mask = self._create_detection_zone_mask(image, detection_zone)
        detection_zone_image = self._apply_mask(image, detection_zone_mask)
        recognition_pattern_mask = self._create_mask(detection_zone_image, self.RECOGNITION_PATTERN_COLOR_RANGES, opening_kernel_size = 3)
        contours = self._find_contours(recognition_pattern_mask, detection_zone_image)
        return self._detect_charging_station_from_contours(contours, playfield)

    def _define_detection_zone(self, playfield):
        charging_station_size = Coordinate.translate_physical_to_game(ChargingStation.PHYSICAL_SIZE_CM, playfield)
        chargin_station_rect = Rect(Vector2(playfield.rect.right - charging_station_size.width, 0), Vector2(charging_station_size.width, playfield.rect.top))
        return chargin_station_rect

    def _create_detection_zone_mask(self, image, zone):
        mask = Image.from_attributes(image.width, image.height, ColorMode.BLACK_AND_WHITE)
        OpenCV.fill_rectangle(mask, zone, Color.WHITE)
        self._log_detection_step("Charging Station Detection Zone Mask", mask)
        return mask

    def _detect_charging_station_from_contours(self, contours, playfield):
        filtered_contours = self._contour_filter.filter_contours(contours)
        if len(filtered_contours) != 1:
            raise DetectionError("The recognition pattern could not be detected.")
        return self._create_charging_station(filtered_contours[0], playfield)

    def _create_charging_station(self, contour, playfield):
        center = OpenCV.get_contour_center(contour)
        center.y += Coordinate.translate_physical_to_game(ChargingStation.RECOGNITION_PATTERN_PHYSICAL_SIZE_CM, playfield).height
        location_coordinate = self._coordinate_factory.create(center, CoordinateSystem.CAMERA, altitude_cm = ChargingStation.RECOGNITION_PATTERN_HEIGHT_CM)
        return ChargingStation(location_coordinate)
class EndZoneTreasureDetector(Detector):
    TREASURE_COLOR_RANGE = ColorRange(Color.from_hsv(40, 150, 85), Color.from_hsv(70, 255, 255))
    DETECTION_CAMERA_MOVEMENT_SEQUENCE = [Vector2(-35, -35), Vector2(-29, -35), Vector2(-23, -35), Vector2(-17, -35), Vector2(-11, -35)]
    RELATIVE_TREASURE_SIZE = Vector2(0.017, 0.0125)
    RELATIVE_LOCATION_ADJUSTEMENT = 0.03

    def __init__(self):
        self._treasure_contour_filter = ContourFilter(min_size_scale = 0.5, max_size_scale = 1.5)

    def detect(self, image, playfield):
        detection_zone_mask = self._create_detection_zone_mask(image, playfield)
        detection_zone_image = self._apply_mask(image, detection_zone_mask)
        treasure_mask = self._create_mask(detection_zone_image, self.TREASURE_COLOR_RANGE, opening_kernel_size = 3)
        contours = self._find_contours(treasure_mask, detection_zone_image)
        return self._detect_treasures_from_contours(contours, playfield, image)

    def _create_detection_zone_mask(self, image, playfield):
        mask = Image.from_attributes(image.width, image.height, ColorMode.BLACK_AND_WHITE)
        detection_rect = Rect.from_points([Vector2(playfield.delimiting_segment.point1.x, 0), Vector2(playfield.delimiting_segment.point2.x, playfield.lowest_delimiting_point.y)])
        OpenCV.fill_rectangle(mask, detection_rect, Color.WHITE)
        mask = OpenCV.combine_masks([mask.invert(), playfield.surface_mask]).invert()
        self._log_detection_step("Treasure Detection Zone Mask", mask)
        return mask

    def _detect_treasures_from_contours(self, contours, playfield, image):
        treasures = []
        self._treasure_contour_filter.target_size = image.size * self.RELATIVE_TREASURE_SIZE
        filtered_contours = self._treasure_contour_filter.filter_contours(contours)
        for contour in filtered_contours:
            treasures.append(self._create_treasure(contour, playfield))
        Logger.get_instance().log(self, "Treasure Detection", details = "Detected treasures: {0}.".format(len(treasures)))
        return treasures

    def _create_treasure(self, contour, playfield):
        treasure_location = OpenCV.get_contour_bounding_rect(contour).center
        panel_relative_location = (treasure_location.x - playfield.delimiting_segment.point1.x) / playfield.delimiting_segment.width + self.RELATIVE_LOCATION_ADJUSTEMENT
        return Treasure(treasure_location, panel_relative_location)
Beispiel #7
0
class PlayfieldDetector(Detector):
    LEFT_MASK_AREA_WIDTH = 5
    RELATIVE_PLAYFIELD_AREA_SIZE = Vector2(0.95, 0.65)

    def __init__(self):
        self._rect_finder = RectFinder()
        self._image = None
        self._contour_filter = ContourFilter(min_size_scale = 0.7, max_size_scale = 1.25, min_aspect_ratio = 0.75, max_aspect_ratio = 2.25)

    def detect(self, image):
        self._image = image
        self._contour_filter.target_size = image.size * self.RELATIVE_PLAYFIELD_AREA_SIZE
        return self._perform_detection(image)

    def _perform_detection(self, image):
        playfield_mask = self._create_playfield_mask(image)
        playfield_contour = self._find_playfield_contour(playfield_mask, image)
        playfield_rect, detected_corners = self._find_rect_corners(playfield_contour)
        self._adjust_playfield_width(image, playfield_rect)
        return Playfield(playfield_rect)

    def _create_playfield_mask(self, image):
        mask = self._create_mask(image, ColorRange(Color.from_hsv(0, 0, 0), Color.from_hsv(360, 255, 75)), closure_kernel_size = 14)
        mask.invert()
        self._draw_left_exclusion_area(mask, image)
        self._draw_right_exclusion_area(mask, image)
        return mask

    def _draw_left_exclusion_area(self, mask, image):
        OpenCV.fill_rectangle(mask, Rect.from_size(Vector2(self.LEFT_MASK_AREA_WIDTH, image.height)), Color.BLACK)

    def _draw_right_exclusion_area(self, mask, image):
        charging_station_area_width = ChargingStation.PHYSICAL_SIZE_CM.width / Playfield.PHYSICAL_SIZE_CM.width * image.width
        OpenCV.fill_rectangle(mask, Rect(Vector2(image.width - charging_station_area_width, 0), Vector2(charging_station_area_width, image.height)), Color.BLACK)

    def _find_playfield_contour(self, playfield_mask, image):
        contours = self._find_contours(playfield_mask, image)
        playfield_contours = self._contour_filter.filter_contours(contours)
        self._log_contour_detection_step("Playfield Contours", playfield_contours, self._image)
        if len(playfield_contours) != 1:
            raise DetectionError("Playfield detection failed. Detected contours: {0}.".format(len(playfield_contours)))
        return playfield_contours[0]

    def _find_rect_corners(self, playfield_contour):
        table_rect, detected_corners =  self._rect_finder.find_rectangle(playfield_contour)
        self._log_rect_corners_detection_step(table_rect, detected_corners)
        return table_rect, detected_corners

    def _adjust_playfield_width(self, image, playfield_rect):
        playfield_rect.left = 0
        playfield_rect.width = playfield_rect.height * Playfield.PHYSICAL_SIZE_CM.aspect_ratio

    # Logging

    def _log_rect_corners_detection_step(self, table_rect, detected_corners):
        if Logger.get_instance().save_blob_data:
            self._image.save_state()
            OpenCV.draw_rectangle(self._image, table_rect, Color.YELLOW, thickness = 3)
            for corner in detected_corners:
                OpenCV.draw_circle(self._image, Vector2(corner[0], corner[1]), radius = 10, color = Color.RED, thickness = 3)
            self._log_detection_step("Playfield Rect Corners", self._image)
            self._image.restore_state()

    def _log_playfield_detection_step(self, image, playfield_rect):
       if Logger.get_instance().save_blob_data:
            self._image.save_state()
            image.crop(playfield_rect)
            self._log_detection_step("Playfield", image)
            self._image.restore_state()
 def __init__(self, coordinate_factory):
     self._coordinate_factory = coordinate_factory
     self._contour_filter = ContourFilter(min_size_scale = 0.75, max_size_scale = 1.5, min_aspect_ratio = 0.75, max_aspect_ratio = 1.25)
Beispiel #9
0
class RobotDetector(Detector):
    DETECTION_RECT_SCALE_FACTOR = Vector2(1, 1.2)
    RECOGNITION_PATTERN_COLOR_RANGE = ColorRange(Color.from_hsv(280, 120, 180), Color.from_hsv(335, 255, 255))

    def __init__(self, robot_factory):
        self._robot_factory = robot_factory
        self._pattern_circle_filter = ContourFilter(
            min_size_scale=0.5, max_size_scale=1.5, min_aspect_ratio=0.7, max_aspect_ratio=1.3
        )
        self._pattern_filter = ContourFilter(min_aspect_ratio=0.75, max_aspect_ratio=1.25)

    def detect(self, image, playfield):
        self._pattern_circle_filter.target_size = Coordinate.translate_physical_to_game(
            Robot.RECOGNITION_CIRCLE_PHYSICAL_SIZE_CM, playfield
        )
        self._recognition_circle_distance = Coordinate.translate_physical_to_game(
            Robot.RECOGNITION_CIRCLE_DISTANCE_CM, playfield
        )
        return self._perform_detection(image.clone(), playfield)

    def _perform_detection(self, image, playfield):
        detection_rect = self._create_detection_area(image, playfield)
        mask = self._create_mask(
            image, self.RECOGNITION_PATTERN_COLOR_RANGE, opening_kernel_size=5, closure_kernel_size=5
        )
        contours = self._find_contours(mask, image)
        return self._detect_robot_from_contours(contours, detection_rect, playfield)

    def _create_detection_area(self, image, playfield):
        detection_rect = playfield.rect.clone().scale_centered(self.DETECTION_RECT_SCALE_FACTOR)
        image.crop(detection_rect)
        self._log_detection_step("Robot Detection Area", image)
        return detection_rect

    def _detect_robot_from_contours(self, contours, detection_rect, playfield):
        recognition_circles = self._get_robot_recognition_circles(contours, detection_rect)
        return self._get_robot_from_recognition_circles(recognition_circles)

    def _get_robot_recognition_circles(self, contours, detection_rect):
        recognition_circles = []
        filtered_contours = self._pattern_circle_filter.filter_contours(contours)
        for contour in filtered_contours:
            recognition_circles.append(OpenCV.get_contour_center(contour).offset(detection_rect.location))
        return recognition_circles

    def _get_robot_from_recognition_circles(self, recognition_circles):
        self._validate_recognition_circles(recognition_circles)
        robot = self._robot_factory.create_from_recognition_circles(
            recognition_circles, CoordinateSystem.CAMERA, RobotCreationSource.DETECTION
        )
        self._validate_robot(robot)
        return robot

    def _validate_recognition_circles(self, recognition_circles):
        if len(recognition_circles) != 3:
            raise DetectionError(
                "The required amount of recognition circles were not detected. Detected: {0}.".format(
                    len(recognition_circles)
                )
            )
        for pair in ListUtils.pairs(recognition_circles):
            pair_distance = Segment(pair[0], pair[1]).length
            if not MathUtils.in_range(
                pair_distance, self._recognition_circle_distance.x, self._recognition_circle_distance.y
            ):
                raise DetectionError(
                    "Distance between detected recognition circles was not of the correct length. Detected: {0}.".format(
                        pair_distance
                    )
                )

    def _validate_robot(self, robot):
        rotated_rect = robot.get_rotated_rect()
        if not self._pattern_filter.filter_rotated_rect(rotated_rect):
            raise DetectionError(
                "Detected recognition circles do not meet the required criterias. Aspect ratio: {0}".format(
                    rotated_rect.aspect_ratio
                )
            )
Beispiel #10
0
 def __init__(self, robot_factory):
     self._robot_factory = robot_factory
     self._pattern_circle_filter = ContourFilter(
         min_size_scale=0.5, max_size_scale=1.5, min_aspect_ratio=0.7, max_aspect_ratio=1.3
     )
     self._pattern_filter = ContourFilter(min_aspect_ratio=0.75, max_aspect_ratio=1.25)
Beispiel #11
0
class TreasureDetector(Detector):
    TREASURE_ZONE_SCALE_FACTOR = 0.0125
    TREASURE_COLOR_RANGE = ColorRange(Color.from_hsv(40, 100, 75), Color.from_hsv(70, 255, 255))
    ROBOT_MASK_RECT_SCALE = 1.2

    TOP_ZONE = 0
    BOTTOM_ZONE = 1

    def __init__(self, coordinate_factory):
        self._coordinate_factory = coordinate_factory
        self._contour_filter = ContourFilter(min_size_scale = Vector2(0.75, 1), max_size_scale = Vector2(2, 4), min_aspect_ratio = 1, max_aspect_ratio = 3)

    def detect(self, image, playfield, robot, islands):
        Logger.get_instance().log(self, "Treasure Detection", details = "Detection started.")
        self._contour_filter.target_size = Coordinate.translate_physical_to_game(Treasure.PHYSICAL_SIZE_CM.xy, playfield)
        return self._perform_detection(image, playfield, robot, islands)

    def _perform_detection(self, image, playfield, robot, islands):
        treasure_zones = self._define_treasure_zones(image, playfield)
        treasure_zone_mask = self._create_treasure_zone_mask(image, treasure_zones, robot, islands)
        treasure_zone_image = self._apply_mask(image, treasure_zone_mask)
        treasure_mask = self._create_mask(treasure_zone_image, self.TREASURE_COLOR_RANGE, opening_kernel_size = 3)
        contours = self._find_contours(treasure_mask, treasure_zone_image)
        return self._detect_treasures_from_contours(contours)

    def _define_treasure_zones(self, image, playfield):
        treasure_zones = ListUtils.init_list(2)
        zone_size = image.width * self.TREASURE_ZONE_SCALE_FACTOR
        charging_station_area_width = Coordinate.translate_physical_to_game(ChargingStation.PHYSICAL_SIZE_CM, playfield).width
        
        treasure_zones[self.TOP_ZONE] = Rect(Vector2(playfield.rect.left, playfield.rect.top - zone_size), Vector2(playfield.rect.width - charging_station_area_width, zone_size * 2))
        treasure_zones[self.BOTTOM_ZONE] = Rect(Vector2(playfield.rect.left, playfield.rect.bottom - zone_size), Vector2(playfield.rect.width - charging_station_area_width, zone_size * 2))
        return treasure_zones

    def _create_treasure_zone_mask(self, image, treasure_zones, robot, islands):
        mask = Image.from_attributes(image.width, image.height, ColorMode.BLACK_AND_WHITE)
        for treasure_zone in treasure_zones:
            OpenCV.fill_rectangle(mask, treasure_zone, Color.WHITE)
        self._mask_robot(mask, robot)
        self._mask_islands(mask, islands)
        self._log_detection_step("Treasure Zone Mask", mask)
        return mask

    def _mask_robot(self, mask, robot):
        if robot is not None:
            robot_mask_rect = robot.get_rotated_rect(CoordinateSystem.CAMERA).clone().scale_centered(self.ROBOT_MASK_RECT_SCALE)
            OpenCV.fill_rotated_rectangle(mask, robot_mask_rect, Color.BLACK)

    def _mask_islands(self, mask, islands):
        for island in islands:
            bounding_rect = island.get_bounding_rect(CoordinateSystem.CAMERA)
            OpenCV.fill_rectangle(mask, bounding_rect, Color.BLACK)

    def _detect_treasures_from_contours(self, contours):
        treasures = []
        filtered_contours = self._contour_filter.filter_contours(contours)
        for index, contour in enumerate(filtered_contours):
            treasures.append(self._create_treasure(contour, index))
        Logger.get_instance().log(self, "Treasure Detection", details = "Detected treasures: {0}.".format(len(treasures)))
        return treasures

    def _create_treasure(self, contour, label):
        treasure_rect = OpenCV.get_contour_bounding_rect(contour)
        location_coordinate = self._coordinate_factory.create(treasure_rect.center, CoordinateSystem.CAMERA, altitude_cm = Treasure.PHYSICAL_SIZE_CM.z / 2)
        return Treasure(location_coordinate, label)
Beispiel #12
0
 def __init__(self, coordinate_factory):
     self._coordinate_factory = coordinate_factory
     self._contour_filter = ContourFilter(min_size_scale = Vector2(0.75, 1), max_size_scale = Vector2(2, 4), min_aspect_ratio = 1, max_aspect_ratio = 3)
Beispiel #13
0
 def __init__(self):
     self._playfield_contour_filter = ContourFilter()
Beispiel #14
0
 def __init__(self):
     self._rect_finder = RectFinder()
     self._image = None
     self._contour_filter = ContourFilter(min_size_scale = 0.7, max_size_scale = 1.25, min_aspect_ratio = 0.75, max_aspect_ratio = 2.25)
Beispiel #15
0
 def __init__(self):
     self._gripper_contour_filter = ContourFilter(min_size_scale = 0.75, max_size_scale = 1.25)
 def __init__(self):
     self._treasure_contour_filter = ContourFilter(min_size_scale = 0.5, max_size_scale = 1.5)
 def __init__(self):
     self._treasure_contour_filter = ContourFilter(min_size_scale = Vector2(0.5, 0.5), max_size_scale = Vector2(2, 2))
Beispiel #18
0
class PlayfieldDetector(Detector):

    PLAYFIELD_COLOR_RANGE = ColorRange(Color.from_hsv(0, 0, 0), Color.from_hsv(360, 255, 75))
    RELATIVE_PLAYFIELD_AREA_SIZE = Vector2(0.95, 0.5)
    RELATIVE_DELIMITING_SEGMENT_SIZE = Vector2(0.55, 0.9)
    MIN_PLAYFIELD_VERTICE_COUNT = 5
    MAX_PLAYFIELD_VERTICE_COUNT = 7
    DELIMITING_SEGMENT_DISTANCE_FILTER = 10
    EXPECTED_DELIMITING_POINT_COUNT = 2
    EXPECTED_CONTOUR_COUNT = 1

    def __init__(self):
        self._playfield_contour_filter = ContourFilter()

    def detect(self, image):
        self._playfield_contour_filter.target_size = image.size * self.RELATIVE_PLAYFIELD_AREA_SIZE
        mask = self._create_playfield_detection_mask(image)
        surface_contour = self._find_surface_contour(mask, image)
        delimiting_segment = self._find_delimiting_segment(mask, image)
        self._validate_segment(delimiting_segment, image)
        return self._create_playfield(surface_contour, delimiting_segment, mask)

    def _create_playfield_detection_mask(self, image):
        mask = self._create_mask(image, self.PLAYFIELD_COLOR_RANGE)
        mask = self._apply_opening_morph_transform(mask, relative_kernel_size=15)
        mask = self._apply_closure_morph_transform(mask, relative_kernel_size=25)
        mask.invert()
        return mask

    def _find_surface_contour(self, mask, image):
        contours = self._find_contours(mask.clone(), image)
        filtered_contours = self._playfield_contour_filter.filter_contours(contours)
        if len(filtered_contours) != self.EXPECTED_CONTOUR_COUNT:
            raise DetectionError(
                "Playfield detection failed. Detected delimiting contours: {0}.".format(len(filtered_contours))
            )
        return filtered_contours[0]

    def _find_delimiting_segment(self, mask, image):
        hough_lines = OpenCV.hough_lines(mask, threshold=50, canny_threshold1=50, canny_threshold2=150)
        top_hough_lines, left_hough_lines, right_hough_lines = self._group_hough_lines(hough_lines)

        median_top_line = Segment.average(top_hough_lines)
        median_left_line = Segment.average(left_hough_lines)
        median_right_line = Segment.average(right_hough_lines)

        left_delimiting_point = median_top_line.intersection_point(median_left_line)
        right_delimiting_point = median_top_line.intersection_point(median_right_line)
        return Segment(left_delimiting_point, right_delimiting_point)

    def _group_hough_lines(self, hough_lines):
        top_hough_lines = list(filter(lambda line: MathUtils.in_range(line.angle, 345, 360), hough_lines))
        left_hough_lines = list(filter(lambda line: MathUtils.in_range(line.angle, 1, 74), hough_lines))
        right_hough_lines = list(filter(lambda line: MathUtils.in_range(line.angle, 270, 344), hough_lines))
        if len(top_hough_lines) == 0 or len(left_hough_lines) == 0 or len(right_hough_lines) == 0:
            raise DetectionError("Playfield detection failed. No hough lines found.")
        return top_hough_lines, left_hough_lines, right_hough_lines

    def _validate_segment(self, delimiting_segment, image):
        if not MathUtils.in_range(
            delimiting_segment.length,
            image.width * self.RELATIVE_DELIMITING_SEGMENT_SIZE.x,
            image.width * self.RELATIVE_DELIMITING_SEGMENT_SIZE.y,
        ):
            raise DetectionError(
                "Playfield detection failed. Delimiting segment length was not within the expected range."
            )

    def _create_playfield(self, surface_contour, delimiting_segment, mask):
        surface_contour = OpenCV.simplify_contour(surface_contour, epsilon_base=0.003)
        surface_contour_path = OpenCV.get_path_from_contour(surface_contour)
        return Playfield(surface_contour_path, delimiting_segment, mask)
Beispiel #19
0
 def __init__(self, coordinate_factory):
     self._island_factory = IslandFactory(coordinate_factory)
     self._simple_shape_finder = SimpleShapeFinder()
     self._contour_filter = ContourFilter(min_size_scale = 0.75, max_size_scale = 1.5, min_aspect_ratio = 0.8, max_aspect_ratio = 1.2, min_area_scale = 0.5, max_area_scale = 2)