Compare commits

...

2 Commits

Author SHA1 Message Date
Lukas Wölfer
6565792030 Worked on additional tools 2020-06-12 03:58:42 +02:00
Lukas Wölfer
9a38c60488 Worked on rust compatibility 2020-04-09 02:55:08 +02:00
8 changed files with 307 additions and 19 deletions

View File

@@ -41,27 +41,30 @@ def adjust_squares(
if not adjustment: if not adjustment:
adjustment = Adjustment(w=10, h=10) adjustment = Adjustment(w=10, h=10)
high_speed = False
def _adjustment_step(keycode: int) -> None: def _adjustment_step(keycode: int, high_speed: bool) -> None:
assert adjustment is not None assert adjustment is not None
x_keys = {81: -1, 83: +1, 104: -10, 115: +10} x_keys = {104: -1, 115: +1}
y_keys = {82: -1, 84: +1, 116: -10, 110: +10} y_keys = {116: -1, 110: +1}
w_keys = {97: -1, 117: +1} w_keys = {97: -1, 117: +1}
h_keys = {111: -1, 101: +1} h_keys = {111: -1, 101: +1}
dx_keys = {59: -1, 112: +1} dx_keys = {59: -1, 112: +1}
dy_keys = {44: -1, 46: +1} dy_keys = {44: -1, 46: +1}
high_speed_fac = 10
cur_high_speed_fac = high_speed_fac if high_speed else 1
if keycode in x_keys: if keycode in x_keys:
adjustment.x += x_keys[keycode] adjustment.x += x_keys[keycode] * cur_high_speed_fac
elif keycode in y_keys: elif keycode in y_keys:
adjustment.y += y_keys[keycode] adjustment.y += y_keys[keycode] * cur_high_speed_fac
elif keycode in w_keys: elif keycode in w_keys:
adjustment.w += w_keys[keycode] adjustment.w += w_keys[keycode] * cur_high_speed_fac
elif keycode in h_keys: elif keycode in h_keys:
adjustment.h += h_keys[keycode] adjustment.h += h_keys[keycode] * cur_high_speed_fac
elif keycode in dx_keys: elif keycode in dx_keys:
adjustment.dx += dx_keys[keycode] adjustment.dx += dx_keys[keycode] * cur_high_speed_fac
elif keycode in dy_keys: elif keycode in dy_keys:
adjustment.dy += dy_keys[keycode] adjustment.dy += dy_keys[keycode] * cur_high_speed_fac
while True: while True:
working_image = image.copy() working_image = image.copy()
@@ -75,7 +78,10 @@ def adjust_squares(
print(keycode) print(keycode)
if keycode == 27: if keycode == 27:
break break
_adjustment_step(keycode) if keycode == 229:
high_speed = not high_speed
continue
_adjustment_step(keycode, high_speed)
cv2.destroyWindow("Window") cv2.destroyWindow("Window")
return adjustment return adjustment

View File

@@ -6,6 +6,7 @@ from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
import cv2 import cv2
import numpy as np import numpy as np
import json
from ..board import Board, Card, NumberCard, SpecialCard from ..board import Board, Card, NumberCard, SpecialCard
from . import adjustment, card_finder from . import adjustment, card_finder
@@ -230,3 +231,41 @@ def parse_board(image: np.ndarray, conf: Configuration) -> Board:
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
def field_card_to_str(card: Card):
if card == SpecialCard.Hua:
return "Hua"
if isinstance(card, SpecialCard):
return {"Special": card.name}
elif isinstance(card, NumberCard):
return {"Number": {"value": card.number, "suit": card.suit.name}}
def bunker_card_to_str(card: Union[Tuple[SpecialCard, int], Optional[Card]]):
if card is None:
return "Empty"
if isinstance(card, tuple):
return {"Blocked": card[0].name}
return {"Stashed": field_card_to_str(card)}
def goal_card_to_str(card: Optional[NumberCard]):
if card is None:
return None
return {"value": card.number, "suit": card.suit.name}
def parse_to_json(image: np.ndarray, conf: Configuration) -> str:
field = parse_field(image, conf)
flower_gone = parse_hua(image, conf)
bunker = parse_bunker(image, conf)
goal = parse_goal(image, conf)
mystruct = {
"field": [[field_card_to_str(card) for card in row] for row in field],
"hua_set": flower_gone,
"bunker": [bunker_card_to_str(card) for card in bunker],
"goal": [goal_card_to_str(card) for card in goal],
}
return json.dumps(mystruct)

View File

@@ -99,10 +99,66 @@ def _save_adjustments(zip_file: zipfile.ZipFile, conf: Configuration) -> None:
adjustments = {} adjustments = {}
adjustments[FIELD_ADJUSTMENT_KEY] = dataclasses.asdict(conf.field_adjustment) adjustments[FIELD_ADJUSTMENT_KEY] = dataclasses.asdict(conf.field_adjustment)
adjustments[BORDER_ADJUSTMENT_KEY] = dataclasses.asdict(conf.border_adjustment) adjustments[BORDER_ADJUSTMENT_KEY] = dataclasses.asdict(conf.border_adjustment)
adjustments[GOAL_ADJUSTMENT_KEY] = dataclasses.asdict(conf.goal_adjustment)
zip_file.writestr( adjustments[BUNKER_ADJUSTMENT_KEY] = dataclasses.asdict(conf.bunker_adjustment)
ADJUSTMENT_FILE_NAME, json.dumps(adjustment), adjustments[HUA_ADJUSTMENT_KEY] = dataclasses.asdict(conf.hua_adjustment)
adjustments[SPECIAL_BUTTON_ADJUSTMENT_KEY] = dataclasses.asdict(
conf.special_button_adjustment
) )
print(adjustments)
zip_file.writestr(
ADJUSTMENT_FILE_NAME, json.dumps(adjustments),
)
def _save_special_images(zip_file: zipfile.ZipFile, conf: Configuration) -> None:
def _save_special_image(
zip_file: zipfile.ZipFile, images: List[np.ndarray], directory: str
) -> None:
for index, image in enumerate(images):
fd, myfile = tempfile.mkstemp(suffix=f".{PICTURE_EXTENSION}")
cv2.imwrite(myfile, image)
file_name = ""
zip_file.write(
myfile, arcname=f"{directory}/{index:03}.{PICTURE_EXTENSION}"
)
_save_special_image(zip_file, conf.card_border, CARD_BORDER_DIRECTORY)
_save_special_image(zip_file, conf.empty_card, EMPTY_CARD_DIRECTORY)
_save_special_image(zip_file, conf.green_card, GREEN_CARD_DIRECTORY)
_save_special_image(zip_file, conf.card_back, CARD_BACK_DIRECTORY)
def _generate_special_button_filename(
state: ButtonState, special_card: board.SpecialCard
) -> str:
state_char_map = {
ButtonState.normal: "n",
ButtonState.greyed: "g",
ButtonState.shiny: "s",
}
special_card_char_map = {
board.SpecialCard.Fa: "f",
board.SpecialCard.Zhong: "z",
board.SpecialCard.Bai: "b",
}
return f"{state_char_map[state]}{special_card_char_map[special_card]}"
def _save_special_button_images(
zip_file: zipfile.ZipFile,
special_button_images: List[Tuple[ButtonState, board.SpecialCard, np.ndarray]],
):
for index, (state, card, image) in enumerate(special_button_images):
fd, myfile = tempfile.mkstemp(suffix=f".{PICTURE_EXTENSION}")
cv2.imwrite(myfile, image)
file_name = ""
zip_file.write(
myfile,
arcname=f"{SPECIAL_BUTTON_DIRECTORY}/"
f"{_generate_special_button_filename(state,card)}"
f"{index:03}.{PICTURE_EXTENSION}",
)
def save(conf: Configuration, filename: str) -> None: def save(conf: Configuration, filename: str) -> None:
@@ -112,7 +168,8 @@ def save(conf: Configuration, filename: str) -> None:
with zipfile.ZipFile(zip_stream, "w") as zip_file: with zipfile.ZipFile(zip_stream, "w") as zip_file:
_save_adjustments(zip_file, conf) _save_adjustments(zip_file, conf)
_save_catalogue(zip_file, conf.catalogue) _save_catalogue(zip_file, conf.catalogue)
# TODO: Save card_borders and emtpy_card and green_card and special_buttons and card_back _save_special_images(zip_file, conf)
_save_special_button_images(zip_file, conf.special_buttons)
with open(filename, "wb") as zip_archive: with open(filename, "wb") as zip_archive:
zip_archive.write(zip_stream.getvalue()) zip_archive.write(zip_stream.getvalue())

View File

@@ -0,0 +1,78 @@
import argparse
import copy
import dataclasses
import json
import os
import cv2
import numpy as np
import shenzhen_solitaire.card_detection.adjustment as adjustment
import shenzhen_solitaire.card_detection.card_finder as card_finder
import shenzhen_solitaire.card_detection.configuration as configuration
from shenzhen_solitaire.card_detection.configuration import Configuration
def main() -> None:
"""Generate a configuration"""
parser = argparse.ArgumentParser(
description="Calibrate to fit all symbols. "
"Ideally use a screenshot with cards in the bunker, "
"in the goal and also with a killed hua card"
)
parser.add_argument(
"screenshot_path",
metavar="screenshot_path",
type=str,
help="Path to the screenshot",
)
parser.add_argument(
"--config",
metavar="config_path",
type=str,
default="test_config.zip",
help="Config path, either merge or write new",
)
args = parser.parse_args()
print(args.screenshot_path)
image = cv2.imread(args.screenshot_path)
if os.path.exists(args.config):
conf = configuration.load(args.config)
else:
conf = Configuration()
print("Field cards")
conf.field_adjustment = adjustment.adjust_squares(
image, count_x=8, count_y=13, adjustment=copy.deepcopy(conf.field_adjustment)
)
print("Field borders")
border_adjustment = adjustment.adjust_squares(
image, count_x=8, count_y=13, adjustment=copy.deepcopy(conf.field_adjustment)
)
conf.bunker_adjustment.w = conf.field_adjustment.w
conf.bunker_adjustment.h = conf.field_adjustment.h
print("Bunker cards")
bunker_adjustment = adjustment.adjust_squares(
image, count_x=3, count_y=1, adjustment=copy.deepcopy(conf.bunker_adjustment)
)
conf.goal_adjustment.w = conf.field_adjustment.w
conf.goal_adjustment.h = conf.field_adjustment.h
print("Goal cards")
goal_adjustment = adjustment.adjust_squares(
image, count_x=3, count_y=1, adjustment=copy.deepcopy(conf.goal_adjustment)
)
conf.hua_adjustment.w = conf.field_adjustment.w
conf.hua_adjustment.h = conf.field_adjustment.h
print("Hua card")
hua_adjustment = adjustment.adjust_squares(
image, count_x=1, count_y=1, adjustment=copy.deepcopy(conf.hua_adjustment)
)
configuration.save(conf, args.config)
if __name__ == "__main__":
main()

View File

@@ -8,11 +8,19 @@ import numpy as np
import shenzhen_solitaire.card_detection.adjustment as adjustment import shenzhen_solitaire.card_detection.adjustment as adjustment
import shenzhen_solitaire.card_detection.card_finder as card_finder import shenzhen_solitaire.card_detection.card_finder as card_finder
from shenzhen_solitaire.card_detection.configuration import Configuration from shenzhen_solitaire.card_detection.configuration import Configuration
import argparse
def main() -> None: def main() -> None:
"""Generate a configuration""" """Generate a configuration"""
image = cv2.imread("pictures/20190809172213_1.jpg")
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('screenshot_path', metavar='screenshot_path', type=str,
help='Path to the screenshot')
args = parser.parse_args()
print(args.screenshot_path)
image = cv2.imread(args.screenshot_path)
border_adjustment = adjustment.adjust_squares(image, count_x=8, count_y=13) border_adjustment = adjustment.adjust_squares(image, count_x=8, count_y=13)
border_square_pos = adjustment.adjust_squares( border_square_pos = adjustment.adjust_squares(

View File

@@ -0,0 +1,54 @@
import argparse
import cv2
import numpy as np
from shenzhen_solitaire.card_detection import configuration, adjustment, card_finder
from shenzhen_solitaire.card_detection.configuration import Configuration
def main() -> None:
"""Generate a configuration"""
parser = argparse.ArgumentParser(
description="Generate pictures for symbols. "
"Requires screenshot of field with no moved cards, "
"so 8 columns of 5 cards each"
)
parser.add_argument(
"screenshot_path",
metavar="screenshot_path",
type=str,
help="Path to the screenshot",
)
parser.add_argument(
"--conf",
dest="config_path",
type=str,
default="config.zip",
help="Path to existing config to be merged, or new config",
)
args = parser.parse_args()
print(args.screenshot_path)
image = cv2.imread(args.screenshot_path)
conf = configuration.load(args.config)
squares = card_finder.get_field_squares(image, conf.field_adjustment, 5, 8)
catalogue = card_finder.catalogue_cards(squares)
conf.card_border.extend(
card_finder.get_field_squares(image, conf.border_adjustment, 1, 1)
)
conf.green_card.extend(
card_finder.get_field_squares(image, conf.bunker_adjustment, 1, 3)
)
conf.green_card.extend(
card_finder.get_field_squares(image, conf.goal_adjustment, 1, 3)
)
conf.green_card.extend(
card_finder.get_field_squares(image, conf.hua_adjustment, 1, 1)
)
conf.catalogue.extend(catalogue)
configuration.save(conf, args.config_path)
if __name__ == "__main__":
main()

View File

@@ -1,15 +1,42 @@
import argparse
import cv2 import cv2
import numpy as np import numpy as np
import shenzhen_solitaire.card_detection.configuration as configuration from shenzhen_solitaire.card_detection import configuration, adjustment, card_finder
from shenzhen_solitaire.card_detection.configuration import Configuration
def main() -> None: def main() -> None:
"""Generate a configuration""" """Generate a configuration"""
image = cv2.imread("pictures/20190809172213_1.jpg") parser = argparse.ArgumentParser(
description="Generate pictures for symbols, "
"requires screenshot of field with no moved cards, "
"so 8 columns of 5 cards each"
)
parser.add_argument(
"screenshot_path",
metavar="screenshot_path",
type=str,
help="Path to the screenshot",
)
parser.add_argument(
"--conf",
dest="config_path",
type=str,
default="config.zip",
help="Path to existing config to be merged, or new config",
)
generated_config = configuration.generate(image) args = parser.parse_args()
configuration.save(generated_config, "test_config.zip") print(args.screenshot_path)
image = cv2.imread(args.screenshot_path)
adj = adjustment.adjust_field(image)
squares = card_finder.get_field_squares(image, adj, 5, 8)
catalogue = card_finder.catalogue_cards(squares)
generated_config = Configuration(field_adjustment=adj, catalogue=catalogue, meta={})
configuration.save(generated_config, args.config_path)
if __name__ == "__main__": if __name__ == "__main__":

19
tools/to_json.py Normal file
View File

@@ -0,0 +1,19 @@
from shenzhen_solitaire.card_detection.board_parser import parse_to_json
import shenzhen_solitaire.card_detection.configuration as configuration
import cv2
import sys
def main() -> None:
if len(sys.argv) < 2:
print("Give filename pls")
return
image = cv2.imread(str(sys.argv[1]))
conf = configuration.load("test_config.zip")
print(parse_to_json(image, conf))
if __name__ == "__main__":
main()