Added virtenv

This commit is contained in:
Lukas Wölfer
2020-02-09 19:37:09 +01:00
parent 30cd0c20ac
commit 2a05095c49
9 changed files with 232 additions and 18 deletions

12
Pipfile Normal file
View File

@@ -0,0 +1,12 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
pyautogui = "*"
[requires]
python_version = "3.8"

View File

@@ -1,14 +1,15 @@
"""Contains parse_board function"""
import copy
import itertools
from typing import Any, Iterable, List, Optional, Tuple, Union, Dict
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
import cv2
import numpy as np
from ..board import Board, Card, NumberCard, SpecialCard
from . import card_finder
from .configuration import Configuration
from . import adjustment, card_finder
from .configuration import Configuration, ButtonState
def grouper(
@@ -19,20 +20,27 @@ def grouper(
return itertools.zip_longest(*args, fillvalue=fillvalue)
def fake_adjustment(adj: adjustment.Adjustment) -> adjustment.Adjustment:
result = copy.deepcopy(adj)
result.x -= 5
result.y -= 5
result.h += 10
result.w += 10
return result
def get_field_square_iterator(
image: np.ndarray, conf: Configuration, row_count: int, column_count: int
) -> Iterable[Tuple[np.ndarray, np.ndarray]]:
"""Return iterator for both the square, as well as the matching card border"""
fake_adjustments = conf.field_adjustment
fake_adjustments.x -= 5
fake_adjustments.y -= 5
fake_adjustments.h += 10
fake_adjustments.w += 10
my_adj = fake_adjustment(conf.field_adjustment)
my_border_adj = fake_adjustment(conf.border_adjustment)
squares = card_finder.get_field_squares(
image, fake_adjustments, count_x=row_count, count_y=column_count
image, my_adj, count_x=row_count, count_y=column_count
)
border_squares = card_finder.get_field_squares(
image, conf.border_adjustment, count_x=row_count, count_y=column_count
image, my_border_adj, count_x=row_count, count_y=column_count
)
grouped_squares = grouper(squares, row_count)
grouped_border_squares = grouper(border_squares, row_count)
@@ -96,23 +104,132 @@ def parse_field(image: np.ndarray, conf: Configuration) -> List[List[Card]]:
def parse_hua(image: np.ndarray, conf: Configuration) -> bool:
"""Return true if hua is in the hua spot, false if hua spot is empty"""
raise NotImplementedError()
my_hua_adj = fake_adjustment(conf.hua_adjustment)
hua_square = card_finder.get_field_squares(image, my_hua_adj, count_x=1, count_y=1)[
0
]
hua_templates = [
image for image, card_type in conf.catalogue if card_type == SpecialCard.Hua
]
best_hua = max(
match_template(template=template, search_image=hua_square)
for template in hua_templates
)
best_green = max(
match_template(template=template, search_image=hua_square)
for template in conf.green_card
)
return best_hua > best_green
def parse_bunker_field(
image: np.ndarray,
green_cards: List[np.ndarray],
card_backs: List[np.ndarray],
catalogue: List[Tuple[np.ndarray, Card]],
) -> Union[Tuple[SpecialCard, int], Optional[Card]]:
best_green = max(
match_template(template=template, search_image=image)
for template in green_cards
)
best_back = max(
match_template(template=template, search_image=image) for template in card_backs
)
best_card_value, best_card_name = max(
((match_template(template, image), name) for template, name in catalogue),
key=lambda x: x[0],
)
return max(
[
(best_green, None),
(best_back, (SpecialCard.Hua, 0)),
(best_card_value, best_card_name),
],
key=lambda x: x[0],
)[1]
def parse_special_button(
image: np.ndarray,
position: SpecialCard,
buttons: List[Tuple[ButtonState, SpecialCard, np.ndarray]],
) -> ButtonState:
"""Return true if special button is greyed out, e.g. this dragon card is removed from the field"""
square_fits = [
(match_template(template, image), state, name)
for state, name, template in buttons
]
best_state, best_name = max(square_fits, key=lambda x: x[0])[1:]
assert best_name == position
return best_state
def parse_bunker(
image: np.ndarray, conf: Configuration
) -> List[Union[Tuple[SpecialCard, int], Optional[Card]]]:
raise NotImplementedError()
bunker_squares = card_finder.get_field_squares(
image, fake_adjustment(conf.bunker_adjustment), count_x=1, count_y=3
)
button_squares = card_finder.get_field_squares(
image, fake_adjustment(conf.special_button_adjustment), count_x=3, count_y=1
)
dragon_sequence = [SpecialCard.Zhong, SpecialCard.Fa, SpecialCard.Bai]
dragons = [
card_type
for dragon_image, card_type in zip(button_squares, dragon_sequence)
if parse_special_button(dragon_image, card_type, conf.special_buttons)
== ButtonState.greyed
]
dragon_iter = iter(dragons)
matches = [
parse_bunker_field(square, conf.green_card, conf.card_back, conf.catalogue)
for square in bunker_squares
]
matches = [(next(dragon_iter), 0) if isinstance(x, tuple) else x for x in matches]
assert next(dragon_iter, None) is None
return matches
def parse_goal_field(
image: np.ndarray,
catalogue: List[Tuple[np.ndarray, Card]],
green_cards: List[np.ndarray],
) -> Optional[NumberCard]:
square_fits = [
(match_template(template, image), name) for template, name in catalogue
]
best_card_value, best_card_name = max(square_fits, key=lambda x: x[0])
best_green_value = max(match_template(template, image) for template in green_cards)
if best_green_value > best_card_value:
return None
assert isinstance(best_card_name, NumberCard)
return best_card_name
def parse_goal(image: np.ndarray, conf: Configuration) -> Dict[NumberCard.Suit, int]:
raise NotImplementedError()
goal_squares = card_finder.get_field_squares(
image, fake_adjustment(conf.goal_adjustment), count_x=1, count_y=3
)
goal_list = [
parse_goal_field(square, conf.catalogue, conf.green_card)
for square in goal_squares
]
base_goal_dict = {suit: 0 for suit in NumberCard.Suit}
base_goal_dict.update(
{x.suit: x.number for x in (x for x in goal_list if x is not None)}
)
return base_goal_dict
def parse_board(image: np.ndarray, conf: Configuration) -> Board:
result = Board()
result.field = parse_field(image, conf)
# result.flower_gone = parse_hua(image, conf)
# result.bunker = parse_bunker(image, conf)
# result.goal = parse_goal(image, conf)
result.flower_gone = parse_hua(image, conf)
result.bunker = parse_bunker(image, conf)
result.goal = parse_goal(image, conf)
return result

View File

@@ -1,13 +1,17 @@
"""Contains function to manually test the visual detection of a board"""
import copy
import unittest
from typing import List, Tuple, Union
import cv2
import numpy as np
from shenzhen_solitaire import board
from shenzhen_solitaire.card_detection import adjustment, board_parser
import shenzhen_solitaire.card_detection.configuration as configuration
from shenzhen_solitaire import board
from shenzhen_solitaire.board import Card, NumberCard, SpecialCard
from shenzhen_solitaire.card_detection import adjustment, board_parser
from . import boards
@@ -21,3 +25,84 @@ class CardDetectionTest(unittest.TestCase):
for correct_row, my_row in zip(boards.B20190809172206_1.field, my_board.field):
self.assertListEqual(correct_row, my_row)
def test_hua_detection(self) -> None:
"""Read a board and check if it can detect if the flower is gone"""
loaded_config = configuration.load("test_config.zip")
imagenames = [
("BaiBlack", False),
("BaiShiny", True),
("BunkerCards", True),
("FaShiny", False),
("ZhongShiny", False),
]
for imagename, flower_gone in imagenames:
image = cv2.imread(f"pictures/specific/{imagename}.jpg")
my_board = board_parser.parse_board(image, loaded_config)
self.assertEqual(flower_gone, my_board.flower_gone)
def test_bunker_parsing(self) -> None:
loaded_config = configuration.load("test_config.zip")
imagenames: List[
Tuple[str, List[Union[Tuple[SpecialCard, int], Card, None]]]
] = [
(
"BaiBlack",
[(SpecialCard.Bai, 0), None, NumberCard(NumberCard.Suit.Green, 3)],
),
(
"BaiShiny",
[(SpecialCard.Zhong, 0), SpecialCard.Bai, (SpecialCard.Fa, 0)],
),
(
"BunkerCards",
[
NumberCard(NumberCard.Suit.Black, 6),
NumberCard(NumberCard.Suit.Green, 9),
NumberCard(NumberCard.Suit.Green, 8),
],
),
("FaShiny", [None, NumberCard(NumberCard.Suit.Green, 6), SpecialCard.Fa]),
(
"ZhongShiny",
[
(SpecialCard.Fa, 0),
NumberCard(NumberCard.Suit.Green, 6),
SpecialCard.Zhong,
],
),
]
for imagename, bunker in imagenames:
image = cv2.imread(f"pictures/specific/{imagename}.jpg")
my_board = board_parser.parse_board(image, loaded_config)
self.assertListEqual(bunker, my_board.bunker)
def test_goal_parsing(self) -> None:
loaded_config = configuration.load("test_config.zip")
imagenames: List[Tuple[str, List[NumberCard]]] = [
("BaiBlack", [NumberCard(NumberCard.Suit.Green, 2)],),
(
"BaiShiny",
[
NumberCard(NumberCard.Suit.Green, 3),
NumberCard(NumberCard.Suit.Red, 2),
NumberCard(NumberCard.Suit.Black, 3),
],
),
(
"BunkerCards",
[
NumberCard(NumberCard.Suit.Red, 1),
NumberCard(NumberCard.Suit.Black, 1),
],
),
("FaShiny", [NumberCard(NumberCard.Suit.Green, 2)]),
("ZhongShiny", [NumberCard(NumberCard.Suit.Green, 2)]),
]
base_goal_dict = {suit: 0 for suit in NumberCard.Suit}
for imagename, goal in imagenames:
image = cv2.imread(f"pictures/specific/{imagename}.jpg")
my_goal_dict = copy.deepcopy(base_goal_dict)
my_goal_dict.update({x.suit: x.number for x in goal})
my_board = board_parser.parse_board(image, loaded_config)
self.assertDictEqual(my_goal_dict, my_board.goal)