Exemplo n.º 1
0
    def process(self, frame: Frame):
        y = cv2.cvtColor(frame.image, cv2.COLOR_BGR2YUV)[:, :, 0]

        weapon_images = self.REGIONS["weapon_names"].extract(y)
        weapon_images = [255 - imageops.normalise(i) for i in weapon_images]

        weapon_names = imageops.tesser_ocr_all(
            weapon_images,
            whitelist=string.ascii_uppercase,
            engine=imageops.tesseract_lstm,
            scale=2,
        )

        selected_weapons_regions = self.REGIONS[
            "selected_weapon_tell"].extract(frame.image)
        selected_weapons_colours = [
            np.median(r, axis=(0, 1)) for r in selected_weapons_regions
        ]

        def thresh_clip(im):
            im = np.max(im, axis=2)
            threshim = np.tile(im[:, 0], (im.shape[1], 1)).T
            im = cv2.subtract(im, threshim)
            tim = im > 20
            return tim

        frame.apex.weapons = Weapons(
            weapon_names,
            selected_weapons=(
                (
                    int(selected_weapons_colours[0][0]),
                    int(selected_weapons_colours[0][1]),
                    int(selected_weapons_colours[0][2]),
                ),
                (
                    int(selected_weapons_colours[1][0]),
                    int(selected_weapons_colours[1][1]),
                    int(selected_weapons_colours[1][2]),
                ),
            ),
            clip=self._ocr_digits(
                [im > 200 for im in self.REGIONS["clip"].extract(y)],
                self.CLIP_WEIGHTS),
            ammo=self._ocr_digits(
                [
                    thresh_clip(im)
                    for im in self.REGIONS["ammo"].extract(frame.image)
                ],
                self.AMMO_WEIGHTS,
            ),
        )

        # self.REGIONS.draw(frame.debug_image)
        _draw_weapons(frame.debug_image, frame.apex.weapons)

        return frame.apex.weapons.selected_weapons is not None
    def process(self, frame: Frame) -> bool:
        y = frame.image_yuv[:, :, 0]
        tank_region = np.max(self.REGIONS["tank_region"].extract_one(frame.image), axis=2)

        _, thresh = cv2.threshold(tank_region, 100, 255, cv2.THRESH_BINARY)
        # cv2.imshow('thresh1', thresh)

        tank_match_sm = cv2.matchTemplate(thresh, self.TANK_TEMPLATE, cv2.TM_CCORR_NORMED)
        _, match_sm, _, mxloc_sm = cv2.minMaxLoc(tank_match_sm)

        tank_match_lg = cv2.matchTemplate(thresh, self.TANK_LARGE_TEMPLATE, cv2.TM_CCORR_NORMED)
        _, match_lg, _, mxloc_lg = cv2.minMaxLoc(tank_match_lg)

        lock_match = cv2.matchTemplate(thresh, self.LOCK_TEMPLATE, cv2.TM_CCORR_NORMED)
        _, match_lock, _, mxloc_lock = cv2.minMaxLoc(lock_match)

        matched_i = arrayops.argmax([match_sm, match_lg, match_lock])
        # print([match_sm, match_lg, match_lock])
        match = [match_sm, match_lg, match_lock][matched_i]
        matched = ["tank", "tank_lg", "lock"][matched_i]
        best_match_pos = [mxloc_sm, mxloc_lg, mxloc_lock][matched_i]
        match_x = best_match_pos[0]
        # print(matched, match_x)

        frame.overwatch.role_select_match = round(match, 2)

        if match > self.REQUIRED_MATCH:
            grouped = match_x < 150

            logger.debug(
                f"Found match for {matched!r} with match={match:0.3f} ({match_sm:.2f}, {match_lg:.2f}, {match_lock:.2f}), x={match_x} => grouped={grouped}"
            )

            suffix = "_group" if grouped else "_solo"
            frame.overwatch.role_select = RoleSelect(
                placement_text=imageops.tesser_ocr_all(
                    self.REGIONS["placements" + suffix].extract(y), whitelist=string.digits + "/-"
                ),
                sr_text=big_noodle.ocr_all(self.REGIONS["srs" + suffix].extract(y), height=23, invert=True),
                account_name=imageops.tesser_ocr(
                    self.REGIONS["account_name"].extract_one(y), engine=imageops.tesseract_lstm
                ),
                grouped=grouped,
                image=lazy_upload(
                    "role_select", self.REGIONS.blank_out(frame.image), frame.timestamp, selection="last"
                ),
            )
            if frame.debug_image is not None:
                self.REGIONS.draw(frame.debug_image)
            _draw_role_select(frame.debug_image, frame.overwatch.role_select)
            return True

        return False
    def parse_score(self, frame: Frame) -> Tuple[Optional[int], Optional[int]]:
        score_ims = self.REGIONS["scores"].extract(frame.image)
        score_gray = [np.min(im, axis=2) for im in score_ims]
        score_norm = [
            imageops.normalise(im, bottom=80, top=100) for im in score_gray
        ]

        # debugops.normalise(score_gray[0])
        # cv2.imshow('score_ims', np.hstack(score_ims))
        # cv2.imshow('score_gray', np.hstack(score_gray))
        # cv2.imshow('score_ys_norm', np.hstack(score_norm))

        score = imageops.tesser_ocr_all(score_norm,
                                        expected_type=int,
                                        invert=True,
                                        engine=din_next_regular_digits)
        logger.debug(f"Got score={score}")
        return score[0], score[1]
    def process(self, frame: Frame) -> bool:
        result_y = self.REGIONS["result"].extract_one(frame.image_yuv[:, :, 0])
        _, result_thresh = cv2.threshold(result_y, 220, 255, cv2.THRESH_BINARY)
        match, result = imageops.match_templates(
            result_thresh,
            self.RESULTS,
            cv2.TM_CCORR_NORMED,
            required_match=self.RESULT_TEMPLATE_REQUIRED_MATCH,
            previous_match_context=(self.__class__.__name__, "result"),
        )

        if match > self.RESULT_TEMPLATE_REQUIRED_MATCH:
            logger.debug(f"Round result is {result} with match={match}")

            score_ims = self.REGIONS["scores"].extract(frame.image)
            score_gray = [
                imageops.normalise(np.max(im, axis=2)) for im in score_ims
            ]
            scores = imageops.tesser_ocr_all(
                score_gray,
                expected_type=int,
                engine=din_next_regular_digits,
                invert=True,
            )
            logger.debug(f"Round score is {scores}")

            frame.valorant.postgame = Postgame(
                victory=result == "victory",
                score=(scores[0], scores[1]),
                map=imageops.ocr_region(frame, self.REGIONS, "map"),
                game_mode=imageops.ocr_region(frame, self.REGIONS,
                                              "game_mode"),
                image=lazy_upload("postgame",
                                  self.REGIONS.blank_out(frame.image),
                                  frame.timestamp),
            )
            draw_postgame(frame.debug_image, frame.valorant.postgame)

            sort_mode_gray = np.min(
                self.SCOREBOARD_REGIONS["scoreboard_sort_mode"].extract_one(
                    frame.image),
                axis=2)
            sort_mode_filt = 255 - imageops.normalise(sort_mode_gray,
                                                      bottom=75)
            # cv2.imshow('sort_mode_gray', sort_mode_gray)
            sort_mode = imageops.tesser_ocr(sort_mode_filt,
                                            engine=imageops.tesseract_lstm)

            sort_mode_match = max([
                levenshtein.ratio(
                    textops.strip_string(sort_mode).upper(), expected)
                for expected in self.SCOREBOARD_SORT_MODES
            ])
            logger.debug(
                f"Got scoreboard sort mode: {sort_mode!r} match={sort_mode_match:.2f}"
            )

            if sort_mode_match > 0.75:
                frame.valorant.scoreboard = self._parse_scoreboard(frame)
                draw_scoreboard(frame.debug_image, frame.valorant.scoreboard)

            return True

        return False
    def _parse_scoreboard(self, frame: Frame) -> Scoreboard:
        agent_images = self.SCOREBOARD_REGIONS["agents"].extract(frame.image)

        name_images = self.SCOREBOARD_REGIONS["names"].extract(frame.image)

        stat_images = self.SCOREBOARD_REGIONS["stats"].extract(frame.image)
        stat_images_filt = [
            self._filter_statrow_image(im) for im in stat_images
        ]
        stat_image_rows = [
            stat_images_filt[r * 8:(r + 1) * 8] for r in range(10)
        ]

        # cv2.imshow(
        #     'stats',
        #     np.vstack([
        #         np.hstack([self._filter_statrow_image(n)] + r)
        #         for n, r in zip(name_images, stat_image_rows)
        #     ])
        # )

        stats = []
        for i, (agent_im, name_im, stat_row) in enumerate(
                zip(agent_images, name_images, stat_image_rows)):
            agent_match, agent = imageops.match_templates(
                agent_im,
                self.AGENT_TEMPLATES,
                method=cv2.TM_SQDIFF,
                required_match=self.AGENT_TEMPLATE_REQUIRED_MATCH,
                use_masks=True,
                previous_match_context=(self.__class__.__name__, "scoreboard",
                                        "agent", i),
            )
            if agent_match > self.AGENT_TEMPLATE_REQUIRED_MATCH:
                agent = None

            row_bg = name_im[np.max(name_im, axis=2) < 200]
            row_color = np.median(row_bg, axis=0).astype(np.int)

            # cv2.imshow('name', self._filter_statrow_image(name_im))
            # cv2.waitKey(0)
            stat = PlayerStats(
                agent,
                imageops.tesser_ocr(
                    self._filter_statrow_image(name_im),
                    engine=imageops.tesseract_lstm,
                ),
                row_color[0] > row_color[2],
                *imageops.tesser_ocr_all(
                    stat_row,
                    expected_type=int,
                    engine=din_next_regular_digits,
                ),
            )
            stats.append(stat)
            logger.debug(
                f"Got player stats: {stat} - agent match={agent_match:.2f}, row colour={tuple(row_color)}"
            )

        return Scoreboard(
            stats,
            image=lazy_upload("scoreboard",
                              self.SCOREBOARD_REGIONS.blank_out(frame.image),
                              frame.timestamp),
        )
    def process(self, frame: Frame):
        y = frame.image_yuv[:, :, 0]

        your_squad_image = self.REGIONS["your_squad"].extract_one(y)
        t, thresh = cv2.threshold(your_squad_image, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

        match, key = imageops.match_templates(thresh, self.TEMPLATES, cv2.TM_CCORR_NORMED, self.REQUIRED_MATCH)
        frame.apex.your_squad_match = round(match, 4)
        if match < self.REQUIRED_MATCH:
            return False

        name1_trios = np.min(self.REGIONS["names"].extract_one(frame.image), axis=2)
        name1_duos = np.min(self.REGIONS["names_duos"].extract_one(frame.image), axis=2)
        # name1_thresh_value = max(np.max(name1_duos), np.max(name1_trios)) * 0.95
        name1_thresh_value = 240
        # logger.debug(f"Name thresh: {name1_thresh_value}")

        name1_trios_score = int(np.sum(name1_trios > name1_thresh_value))
        name1_duos_score = int(np.sum(name1_duos > name1_thresh_value))
        logger.debug(f"Trios name score: {name1_trios_score} vs duos name score: {name1_duos_score}")

        # self.duos = name1_duos_score and name1_duos_score > name1_trios_score
        self.duos = name1_trios_score < 100
        logger.info(f"Using duos={self.duos}")

        if key == "your_squad":
            names_region_name = "names_duos" if self.duos else "names"
            names = imageops.tesser_ocr_all(
                self.REGIONS[names_region_name].extract(y),
                engine=imageops.tesseract_lstm,
                invert=True,
            )
            frame.apex.your_squad = YourSquad(
                tuple(self._to_name(n) for n in names),
                mode="duos" if self.duos else None,
                images=lazy_upload(
                    "your_squad",
                    np.hstack(self.REGIONS[names_region_name].extract(frame.image)),
                    frame.timestamp,
                ),
            )
            self.REGIONS.draw(frame.debug_image)
            _draw_squad(frame.debug_image, frame.apex.your_squad)
        elif key == "your_selection":
            frame.apex.your_selection = YourSelection(
                name=self._to_name(
                    imageops.tesser_ocr(
                        self.REGIONS["names"].extract(y)[1],
                        engine=imageops.tesseract_lstm,
                        invert=True,
                    )
                ),
                image=lazy_upload(
                    "your_selection",
                    self.REGIONS["names"].extract(frame.image)[1],
                    frame.timestamp,
                ),
            )
            self.REGIONS.draw(frame.debug_image)
            _draw_squad(frame.debug_image, frame.apex.your_selection)
        elif key == "champion_squad":
            names_region_name = "names_duos" if self.duos else "names"
            names = imageops.tesser_ocr_all(
                self.REGIONS[names_region_name].extract(y),
                engine=imageops.tesseract_lstm,
                invert=True,
            )
            frame.apex.champion_squad = ChampionSquad(
                tuple(self._to_name(n) for n in names),
                mode="duos" if self.duos else None,
                images=lazy_upload(
                    "champion_squad",
                    np.hstack(self.REGIONS[names_region_name].extract(frame.image)),
                    frame.timestamp,
                ),
            )
            self.REGIONS.draw(frame.debug_image)
            _draw_squad(frame.debug_image, frame.apex.champion_squad)

        return True
    def process(self, frame: Frame) -> bool:
        agent_name_yuv = self.REGIONS["agent_name"].extract_one(
            frame.image_yuv)
        agent_name_thresh = cv2.inRange(agent_name_yuv, (200, 85, 120),
                                        (255, 115, 150))
        # if hasattr(frame, 'source_image'):
        # 	cv2.imshow('agent_name_yuv', agent_name_yuv)
        # 	cv2.imshow('agent_name_thresh', agent_name_thresh)
        # 	cv2.imwrite(
        # 		os.path.join(os.path.dirname(__file__), 'data', 'agent_names', os.path.basename(frame.source_image)),
        # 		agent_name_thresh
        # 	)

        match, best_match = imageops.match_templates(
            agent_name_thresh,
            self.AGENT_NAME_TEMPLATES,
            method=cv2.TM_CCORR_NORMED,
            required_match=0.95,
            # verbose=True,
        )
        # self.REGIONS.draw(frame.debug_image)

        if match > self.AGENT_TEMPLATE_REQUIRED_MATCH:
            selected_agent_ims = self.REGIONS["selected_agents"].extract(
                frame.image)
            selected_agent_ims_gray = [
                255 - imageops.normalise(np.max(im, axis=2), bottom=50)
                for im in selected_agent_ims
            ]
            selected_agent_texts = imageops.tesser_ocr_all(
                selected_agent_ims_gray,
                engine=imageops.tesseract_lstm,
            )
            logger.info(f"Got selected_agent_texts={selected_agent_texts}")

            picking = True
            for i, text in enumerate(selected_agent_texts):
                for word in textops.strip_string(text, string.ascii_letters +
                                                 " .").split(" "):
                    match = levenshtein.ratio(word, best_match)
                    logger.debug(
                        f"Player {i}: Got match {match:.2f} for {word!r} = {best_match!r}"
                    )
                    if match > 0.7:
                        logger.info(
                            f"Found matching locked in agent {text!r} for selecting agent {best_match!r} - selection locked"
                        )
                        picking = False

            game_mode = imageops.ocr_region(frame, self.REGIONS, "game_mode")

            ranks = []
            for i, im in enumerate(self.REGIONS["player_ranks"].extract(
                    frame.image)):
                match, matched_rank = imageops.match_templates(
                    im,
                    self.RANK_TEMPLATES,
                    method=cv2.TM_SQDIFF,
                    use_masks=True,
                    required_match=15,
                    previous_match_context=("player_ranks", i),
                )
                ranks.append((matched_rank, round(match, 3)))

            player_name_ims = self.REGIONS["player_names"].extract(frame.image)
            player_name_gray = [
                255 - imageops.normalise(np.max(im, axis=2), bottom=50)
                for im in player_name_ims
            ]
            player_names = imageops.tesser_ocr_all(
                player_name_gray, engine=imageops.tesseract_lstm)

            frame.valorant.agent_select = AgentSelect(
                best_match,
                locked_in=not picking,
                map=imageops.ocr_region(frame, self.REGIONS, "map"),
                game_mode=game_mode,
                player_names=player_names,
                agents=selected_agent_texts,
                ranks=ranks,
                image=lazy_upload("agent_select",
                                  self.REGIONS.blank_out(frame.image),
                                  frame.timestamp,
                                  selection="last"),
            )
            draw_agent_select(frame.debug_image, frame.valorant.agent_select)
            return True

        return False
Exemplo n.º 8
0
    def _process_player_stats(self,
                              y: np.ndarray,
                              duos: bool = False,
                              shunt: int = 0) -> Tuple[PlayerStats, ...]:
        name_images = self.REGIONS["names"].shunt(x=shunt).extract(y)
        names = []
        for im in name_images:
            # self._mask_components_touching_edges(im)
            im = 255 - cv2.bitwise_and(
                im,
                cv2.dilate(
                    cv2.threshold(im, 0, 255,
                                  cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1],
                    None,
                ),
            )
            im = cv2.resize(im, (0, 0), fx=2, fy=2)
            im = cv2.GaussianBlur(im, (0, 0), 1)

            name = imageops.tesser_ocr(
                im,
                engine=imageops.tesseract_lstm,
            ).replace(" ", "")
            match = np.mean(imageops.tesseract_lstm.AllWordConfidences())
            logger.info(f"Got name {name!r} ~ {match:1.2f}")
            if match < 0.75:
                name = imageops.tesser_ocr(
                    im,
                    engine=imageops.tesseract_only,
                )
                logger.info(f"Using {name!r} instead")
            names.append(name)

        stat_images = self.REGIONS["stats"].shunt(x=shunt).extract(y)

        # for im in stat_images:
        #     self._mask_components_touching_edges(im)

        stats = imageops.tesser_ocr_all(
            stat_images,
            engine=ocr.tesseract_ttlakes_digits_specials,
        )

        for i in range(len(stats)):
            value = stats[i]
            logger.debug(f"Got stat {i}: {value!r}")
            if value:
                value = value.lower().replace(" ", "")
                for c1, c2 in "l1", "i1", "o0", (":", ""):
                    value = value.replace(c1, c2)
                value = textops.strip_string(value, string.digits + "/")
            if i < 3:
                try:
                    stats[i] = tuple([int(v) for v in value.split("/")])
                except ValueError as e:
                    logger.warning(f'Could not parse {value!r} as 3 ints" {e}')
                    stats[i] = None
            elif 6 <= i <= 8:
                # survival time
                if stats[i] is not None:
                    try:
                        seconds = int(value)
                    except ValueError as e:
                        logger.warning(
                            f'Could not parse "{stats[i]}" as int: {e}')
                        seconds = None
                    else:
                        seconds = mmss_to_seconds(seconds)
                        logger.info(f"MM:SS {stats[i]} -> {seconds}")
                    stats[i] = seconds
            else:
                try:
                    stats[i] = int(value)
                except ValueError as e:
                    logger.warning(f'Could not parse {value!r} as int" {e}')
                    stats[i] = None

        # typing: ignore
        # noinspection PyTypeChecker
        count = 3 if not duos else 2
        r = tuple([PlayerStats(names[i], *stats[i::3]) for i in range(count)])

        for s in r:
            if not s.kills:
                pass
            elif len(s.kills) == 3:
                s.assists = s.kills[1]
                s.knocks = s.kills[2]
                s.kills = s.kills[0]
            else:
                s.kills = s.kills[0]

        logger.info(f"Got {pprint.pformat(r)}")
        return r