Example #1
0
    def detect(self, image):
        """
        Args:
            image: Screenshot.

        Returns:
            bool: If success.
        """
        start_time = time.time()
        image = np.array(image)
        self.image = image

        # Image initialization
        image = rgb2gray(crop(image, self.config.DETECTING_AREA))

        # Perspective transform
        image_trans = cv2.warpPerspective(image, self.homo_data,
                                          self.homo_size)

        # Edge detection
        image_edge = cv2.Canny(image_trans, 100, 150)
        image_edge = cv2.bitwise_and(image_edge, self.ui_mask_homo_stroke)
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
        image_edge = cv2.morphologyEx(image_edge, cv2.MORPH_CLOSE, kernel)
        # Image.fromarray(image_edge, mode='L').show()

        # Find free tile
        if self.search_tile_center(
                image_edge,
                threshold_good=self.config.HOMO_CENTER_GOOD_THRESHOLD,
                threshold=self.config.HOMO_CENTER_THRESHOLD):
            pass
        elif self.search_tile_corner(
                image_edge, threshold=self.config.HOMO_CORNER_THRESHOLD):
            pass
        elif self.search_tile_rectangle(
                image_edge, threshold=self.config.HOMO_RECTANGLE_THRESHOLD):
            pass
        else:
            raise MapDetectionError('Failed to find a free tile')

        self.homo_loca %= self.config.HOMO_TILE

        # Detect map edges
        image_edge = cv2.bitwise_and(
            cv2.dilate(image_edge, kernel),
            cv2.inRange(image_trans, *self.config.HOMO_EDGE_COLOR_RANGE))
        self.detect_edges(image_edge,
                          hough_th=self.config.HOMO_EDGE_HOUGHLINES_THRESHOLD)

        # Log
        time_cost = round(time.time() - start_time, 3)
        logger.info('%ss  %s   edge_lines: %s hori, %s vert' %
                    (float2str(time_cost), '_' if self.lower_edge else ' ',
                     self._map_edge_count[1], self._map_edge_count[0]))
        logger.info(
            'Edges: %s%s%s   homo_loca: %s' %
            ('/' if self.left_edge else ' ', '_' if self.upper_edge else ' ',
             '\\' if self.right_edge else ' ',
             point2str(*self.homo_loca, length=3)))
Example #2
0
 def load_homography(self,
                     storage=None,
                     perspective=None,
                     image=None,
                     file=None):
     """
     Args:
         storage (tuple): ((x, y), [upper-left, upper-right, bottom-left, bottom-right])
         perspective (Perspective):
         image (np.ndarray):
         file (str): File path of image
     """
     if storage is not None:
         self.find_homography(*storage)
     elif perspective is not None:
         hori = perspective.horizontal[0].add(perspective.horizontal[-1])
         vert = perspective.vertical[0].add(perspective.vertical[-1])
         src_pts = hori.cross(vert).points
         x = len(perspective.vertical) - 1
         y = len(perspective.horizontal) - 1
         self.find_homography(size=(x, y), src_pts=src_pts)
     elif image is not None:
         perspective_ = Perspective(self.config)
         perspective_.load(image)
         self.load_homography(perspective=perspective_)
     elif file is not None:
         image_ = np.array(Image.open(file).convert('RGB'))
         perspective_ = Perspective(self.config)
         perspective_.load(image_)
         self.load_homography(perspective=perspective_)
     else:
         raise MapDetectionError(
             'No data feed to load_homography, please input at least one.')
Example #3
0
    def load(self, image):
        """
        Args:
            image:
        """
        image = self._image_clear_ui(np.array(image))
        self.image = image
        super().load(image)

        # Create local view map
        grids = {}

        for loca, points in self.generate():
            if area_in_area(area1=corner2area(points), area2=self.config.DETECTING_AREA):
                grids[loca] = self.grid_class(location=loca, image=image, corner=points, config=self.config)

        # Handle grids offset
        offset = list(grids.keys())
        if not len(offset):
            raise MapDetectionError('No map grids found')
        offset = np.min(offset, axis=0)
        if np.sum(np.abs(offset)) > 0:
            logger.attr_align('grids_offset', tuple(offset.tolist()))
            self.grids = {}
            for loca, grid in grids.items():
                x, y = np.subtract(loca, offset)
                grid.location = (x, y)
                self.grids[(x, y)] = grid
        else:
            self.grids = grids
        self.shape = np.max(list(self.grids.keys()), axis=0)

        # Find local view center
        for loca, grid in self.grids.items():
            offset = grid.screen2grid([self.config.SCREEN_CENTER])[0].astype(int)
            points = grid.grid2screen(np.add([[0.5, 0], [-0.5, 0], [0, 0.5], [0, -0.5]], offset))
            self.swipe_base = np.array([np.linalg.norm(points[0] - points[1]), np.linalg.norm(points[2] - points[3])])
            self.center_loca = tuple(np.add(loca, offset).tolist())
            logger.attr_align('center_loca', self.center_loca)
            if self.center_loca in self:
                self.center_offset = self.grids[self.center_loca].screen2grid([self.config.SCREEN_CENTER])[0]
            else:
                x = max(self.center_loca[0] - self.shape[0], 0) if self.center_loca[0] > 0 else self.center_loca[0]
                y = max(self.center_loca[1] - self.shape[1], 0) if self.center_loca[1] > 0 else self.center_loca[1]
                self.center_offset = offset - self.center_loca
                raise MapDetectionError(f'Camera outside map: offset=({x}, {y})')
            break
Example #4
0
    def get_current_zone(self):
        """
        Returns:
            Zone:

        Raises:
            MapDetectionError: If failed to parse zone name.
            ScriptError:
        """
        name = self.get_zone_name()
        logger.info(f'Map name processed: {name}')
        try:
            self.zone = self.name_to_zone(name)
        except ScriptError as e:
            raise MapDetectionError(*e.args)
        logger.attr('Zone', self.zone)
        return self.zone
    def load(self, image):
        """
        Args:
            image (np.ndarray): Shape (720, 1280, 3)
        """
        start_time = time.time()
        self.image = image

        # Image initialisation
        image = self.load_image(image)

        # Lines detection
        inner_h = self.detect_lines(
            image,
            is_horizontal=True,
            param=self.config.INTERNAL_LINES_FIND_PEAKS_PARAMETERS,
            threshold=self.config.INTERNAL_LINES_HOUGHLINES_THRESHOLD,
            theta=self.config.HORIZONTAL_LINES_THETA_THRESHOLD).move(
                *self.config.DETECTING_AREA[:2])
        inner_v = self.detect_lines(
            image,
            is_horizontal=False,
            param=self.config.INTERNAL_LINES_FIND_PEAKS_PARAMETERS,
            threshold=self.config.INTERNAL_LINES_HOUGHLINES_THRESHOLD,
            theta=self.config.VERTICAL_LINES_THETA_THRESHOLD).move(
                *self.config.DETECTING_AREA[:2])
        edge_h = self.detect_lines(
            image,
            is_horizontal=True,
            param=self.config.EDGE_LINES_FIND_PEAKS_PARAMETERS,
            threshold=self.config.EDGE_LINES_HOUGHLINES_THRESHOLD,
            theta=self.config.HORIZONTAL_LINES_THETA_THRESHOLD,
            pad=self.config.DETECTING_AREA[2] -
            self.config.DETECTING_AREA[0]).move(
                *self.config.DETECTING_AREA[:2])
        edge_v = self.detect_lines(
            image,
            is_horizontal=False,
            param=self.config.EDGE_LINES_FIND_PEAKS_PARAMETERS,
            threshold=self.config.EDGE_LINES_HOUGHLINES_THRESHOLD,
            theta=self.config.VERTICAL_LINES_THETA_THRESHOLD,
            pad=self.config.DETECTING_AREA[3] -
            self.config.DETECTING_AREA[1]).move(
                *self.config.DETECTING_AREA[:2])

        # Lines pre-cleansing
        horizontal = inner_h.add(edge_h).group()
        vertical = inner_v.add(edge_v).group()
        edge_h = edge_h.group()
        edge_v = edge_v.group()
        if not self.config.TRUST_EDGE_LINES:
            edge_h = edge_h.delete(inner_h)  # Experimental, reduce edge lines.
            edge_v = edge_v.delete(inner_v)
        self.horizontal = horizontal
        self.vertical = vertical

        # Calculate perspective
        self.crossings = self.horizontal.cross(self.vertical)
        self.vanish_point = optimize.brute(self._vanish_point_value,
                                           self.config.VANISH_POINT_RANGE)
        distance_point_x = optimize.brute(
            self._distant_point_value, self.config.DISTANCE_POINT_X_RANGE)[0]
        self.distant_point = (distance_point_x, self.vanish_point[1])
        logger.attr_align('vanish_point',
                          point2str(*self.vanish_point, length=5))
        logger.attr_align('distant_point',
                          point2str(*self.distant_point, length=5))
        if np.linalg.norm(np.subtract(self.vanish_point,
                                      self.distant_point)) < 10:
            raise MapDetectionError('Vanish point and distant point too close')

        # Re-generate lines. Useless after mid_cleanse function added.
        # self.horizontal = self.crossings.link(None, is_horizontal=True).group()
        # self.vertical = self.crossings.link(self.vanish_point).group()
        # self.draw(self.crossings.link(self.distant_point))
        # print(edge_h)
        # print(inner_h.group())

        # Lines cleansing
        # self.draw()
        self.map_inner = get_map_inner(self.crossings.points)
        self.horizontal, self.lower_edge, self.upper_edge = self.line_cleanse(
            self.horizontal, inner=inner_h.group(), edge=edge_h)
        self.vertical, self.left_edge, self.right_edge = self.line_cleanse(
            self.vertical, inner=inner_v.group(), edge=edge_v)

        # self.draw()
        # print(self.horizontal)
        # print(self.lower_edge, self.upper_edge)
        # print(self.vertical)
        # print(self.left_edge, self.right_edge)

        # Log
        time_cost = round(time.time() - start_time, 3)
        logger.info('%ss  %s   Horizontal: %s (%s inner, %s edge)' %
                    (float2str(time_cost), '_' if self.lower_edge else ' ',
                     len(self.horizontal), len(horizontal), len(edge_h)))
        logger.info(
            'Edges: %s%s%s    Vertical: %s (%s inner, %s edge)' %
            ('/' if self.left_edge else ' ', '_' if self.upper_edge else ' ',
             '\\' if self.right_edge else ' ', len(
                 self.vertical), len(vertical), len(edge_v)))
Example #6
0
    def update(self, camera=True):
        """Update map image

        Args:
            camera: True to update camera position and perspective data.
        """
        self.device.screenshot()
        if not camera:
            self.view.update(image=self.device.image)
            return True

        self._view_init()
        try:
            if not self.is_in_map() \
                    and not self.is_in_strategy_submarine_move():
                raise MapDetectionError('Image to detect is not in_map')
            self.view.load(self.device.image)
        except MapDetectionError as e:
            if self.info_bar_count():
                logger.info('Perspective error cause by info bar. Waiting.')
                self.handle_info_bar()
                return self.update(camera=camera)
            elif self.appear(GET_ITEMS_1):
                logger.warning('Items got. Trying handling mystery.')
                self.handle_mystery()
                return self.update(camera=camera)
            elif self.handle_story_skip():
                logger.warning('Perspective error cause by story. Handling.')
                self.ensure_no_story(skip_first_screenshot=False)
                return self.update(camera=camera)
            elif self.is_in_stage():
                logger.warning('Image is in stage')
                raise CampaignEnd('Image is in stage')
            elif self.appear(AUTO_SEARCH_MENU_CONTINUE, offset=self._auto_search_menu_offset):
                logger.warning('Image is in auto search menu')
                self.ensure_auto_search_exit()
                raise CampaignEnd('Image is in auto search menu')
            elif not self.is_in_map() \
                    and not self.is_in_strategy_submarine_move():
                logger.warning('Image to detect is not in_map')
                if self.appear_then_click(GAME_TIPS, offset=(20, 20)):
                    logger.warning('Game tips found, retrying')
                    self.device.screenshot()
                    self.view.load(self.device.image)
                else:
                    raise e
            elif 'Camera outside map' in str(e):
                string = str(e)
                logger.warning(string)
                x, y = string.split('=')[1].strip('() ').split(',')
                self._map_swipe((-int(x.strip()), -int(y.strip())))
            else:
                raise e

        if self._prev_view is not None and np.linalg.norm(self._prev_swipe) > 0:
            if self.config.MAP_SWIPE_PREDICT:
                swipe = self._prev_view.predict_swipe(
                    self.view,
                    with_current_fleet=self.config.MAP_SWIPE_PREDICT_WITH_CURRENT_FLEET,
                    with_sea_grids=self.config.MAP_SWIPE_PREDICT_WITH_SEA_GRIDS
                )
                if swipe is not None:
                    self._prev_swipe = swipe
            self.camera = tuple(np.add(self.camera, self._prev_swipe))
            self._prev_view = None
            self._prev_swipe = None
            self.show_camera()

        # Set camera position
        if self.view.left_edge:
            x = 0 + self.view.center_loca[0]
        elif self.view.right_edge:
            x = self.map.shape[0] - self.view.shape[0] + self.view.center_loca[0]
        else:
            x = self.camera[0]
        if self.view.upper_edge:
            y = self.map.shape[1] - self.view.shape[1] + self.view.center_loca[1]
        elif self.view.lower_edge:
            y = 0 + self.view.center_loca[1]
        else:
            y = self.camera[1]

        if self.camera != (x, y):
            logger.attr_align('camera_corrected', f'{location2node(self.camera)} -> {location2node((x, y))}')
        self.camera = (x, y)
        self.show_camera()

        self.predict()
Example #7
0
    def _update(self, camera=True):
        """Update map image

        Args:
            camera: True to update camera position and perspective data.
        """
        self.device.screenshot()
        if not camera:
            self.view.update(image=self.device.image)
            return True

        self._view_init()
        try:
            if not self.is_in_map() \
                    and not self.is_in_strategy_submarine_move():
                logger.warning('Image to detect is not in_map')
                raise MapDetectionError('Image to detect is not in_map')
            self.view.load(self.device.image)
        except MapDetectionError as e:
            if self.info_bar_count():
                logger.warning('Perspective error caused by info bar')
                self.handle_info_bar()
                return False
            elif self.appear(GET_ITEMS_1):
                logger.warning('Perspective error caused by get_items')
                self.handle_mystery()
                return False
            elif self.handle_story_skip():
                logger.warning('Perspective error caused by story')
                self.ensure_no_story(skip_first_screenshot=False)
                return False
            elif self.is_in_stage():
                logger.warning('Image is in stage')
                raise CampaignEnd('Image is in stage')
            elif self.appear(AUTO_SEARCH_MENU_CONTINUE,
                             offset=self._auto_search_menu_offset):
                logger.warning('Image is in auto search menu')
                self.ensure_auto_search_exit()
                raise CampaignEnd('Image is in auto search menu')
            elif self.appear(GLOBE_GOTO_MAP, offset=(20, 20)):
                logger.warning('Image is in OS globe map')
                self.ui_click(GLOBE_GOTO_MAP,
                              check_button=self.is_in_map,
                              offset=(20, 20),
                              retry_wait=3,
                              skip_first_screenshot=True)
                return False
            elif self.appear(AUTO_SEARCH_REWARD, offset=(50, 50)):
                logger.warning(
                    'Perspective error caused by AUTO_SEARCH_REWARD')
                if hasattr(self, 'os_auto_search_quit'):
                    self.os_auto_search_quit()
                    return False
                else:
                    logger.warning(
                        'Cannot find method os_auto_search_quit(), use ui_click() instead'
                    )
                    self.ui_click(AUTO_SEARCH_REWARD,
                                  check_button=self.is_in_map,
                                  offset=(50, 50),
                                  retry_wait=3,
                                  skip_first_screenshot=True)
                    return False
            elif 'opsi' in self.config.task.command.lower(
            ) and self.handle_popup_confirm('OPSI'):
                # Always confirm popups in OpSi, same popups in os_map_goto_globe()
                logger.warning('Perspective error caused by popups')
                return False
            elif not self.is_in_map() \
                    and not self.is_in_strategy_submarine_move():
                if self.appear(GAME_TIPS, offset=(20, 20)):
                    logger.warning('Perspective error caused by game tips')
                    self.device.click(GAME_TIPS)
                    return False
                else:
                    raise e
            elif 'Camera outside map' in str(e):
                string = str(e)
                logger.warning(string)
                x, y = string.split('=')[1].strip('() ').split(',')
                self._map_swipe((-int(x.strip()), -int(y.strip())))
            else:
                raise e

        if self._prev_view is not None and np.linalg.norm(
                self._prev_swipe) > 0:
            if self.config.MAP_SWIPE_PREDICT:
                swipe = self._prev_view.predict_swipe(
                    self.view,
                    with_current_fleet=self.config.
                    MAP_SWIPE_PREDICT_WITH_CURRENT_FLEET,
                    with_sea_grids=self.config.MAP_SWIPE_PREDICT_WITH_SEA_GRIDS
                )
                if swipe is not None:
                    self._prev_swipe = swipe
            self.camera = tuple(np.add(self.camera, self._prev_swipe))
            self._prev_view = None
            self._prev_swipe = None
            self.show_camera()

        # Set camera position
        if self.view.left_edge:
            x = 0 + self.view.center_loca[0]
        elif self.view.right_edge:
            x = self.map.shape[0] - self.view.shape[0] + self.view.center_loca[
                0]
        else:
            x = self.camera[0]
        if self.view.upper_edge:
            y = self.map.shape[1] - self.view.shape[1] + self.view.center_loca[
                1]
        elif self.view.lower_edge:
            y = 0 + self.view.center_loca[1]
        else:
            y = self.camera[1]

        if self.camera != (x, y):
            logger.attr_align(
                'camera_corrected',
                f'{location2node(self.camera)} -> {location2node((x, y))}')
        self.camera = (x, y)
        self.show_camera()

        self.predict()
        return True