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""" """Contains parse_board function"""
import copy
import itertools 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 cv2
import numpy as np import numpy as np
from ..board import Board, Card, NumberCard, SpecialCard from ..board import Board, Card, NumberCard, SpecialCard
from . import card_finder from . import adjustment, card_finder
from .configuration import Configuration from .configuration import Configuration, ButtonState
def grouper( def grouper(
@@ -19,20 +20,27 @@ def grouper(
return itertools.zip_longest(*args, fillvalue=fillvalue) 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( def get_field_square_iterator(
image: np.ndarray, conf: Configuration, row_count: int, column_count: int image: np.ndarray, conf: Configuration, row_count: int, column_count: int
) -> Iterable[Tuple[np.ndarray, np.ndarray]]: ) -> Iterable[Tuple[np.ndarray, np.ndarray]]:
"""Return iterator for both the square, as well as the matching card border""" """Return iterator for both the square, as well as the matching card border"""
fake_adjustments = conf.field_adjustment my_adj = fake_adjustment(conf.field_adjustment)
fake_adjustments.x -= 5 my_border_adj = fake_adjustment(conf.border_adjustment)
fake_adjustments.y -= 5
fake_adjustments.h += 10
fake_adjustments.w += 10
squares = card_finder.get_field_squares( 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( 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_squares = grouper(squares, row_count)
grouped_border_squares = grouper(border_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: def parse_hua(image: np.ndarray, conf: Configuration) -> bool:
"""Return true if hua is in the hua spot, false if hua spot is empty""" """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( def parse_bunker(
image: np.ndarray, conf: Configuration image: np.ndarray, conf: Configuration
) -> List[Union[Tuple[SpecialCard, int], Optional[Card]]]: ) -> 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]: 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: def parse_board(image: np.ndarray, conf: Configuration) -> Board:
result = Board() result = Board()
result.field = parse_field(image, conf) result.field = parse_field(image, conf)
# result.flower_gone = parse_hua(image, conf) result.flower_gone = parse_hua(image, conf)
# result.bunker = parse_bunker(image, conf) result.bunker = parse_bunker(image, conf)
# result.goal = parse_goal(image, conf) result.goal = parse_goal(image, conf)
return result return result

View File

@@ -1,13 +1,17 @@
"""Contains function to manually test the visual detection of a board""" """Contains function to manually test the visual detection of a board"""
import copy
import unittest import unittest
from typing import List, Tuple, Union
import cv2 import cv2
import numpy as np 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 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 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): for correct_row, my_row in zip(boards.B20190809172206_1.field, my_board.field):
self.assertListEqual(correct_row, my_row) 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)