diff --git a/shenzhen_solitaire/card_detection/adjustment.py b/shenzhen_solitaire/card_detection/adjustment.py index 4ae9847..460cf3f 100644 --- a/shenzhen_solitaire/card_detection/adjustment.py +++ b/shenzhen_solitaire/card_detection/adjustment.py @@ -41,27 +41,30 @@ def adjust_squares( if not adjustment: 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 - x_keys = {81: -1, 83: +1, 104: -10, 115: +10} - y_keys = {82: -1, 84: +1, 116: -10, 110: +10} + x_keys = {104: -1, 115: +1} + y_keys = {116: -1, 110: +1} w_keys = {97: -1, 117: +1} h_keys = {111: -1, 101: +1} dx_keys = {59: -1, 112: +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: - adjustment.x += x_keys[keycode] + adjustment.x += x_keys[keycode] * cur_high_speed_fac elif keycode in y_keys: - adjustment.y += y_keys[keycode] + adjustment.y += y_keys[keycode] * cur_high_speed_fac elif keycode in w_keys: - adjustment.w += w_keys[keycode] + adjustment.w += w_keys[keycode] * cur_high_speed_fac elif keycode in h_keys: - adjustment.h += h_keys[keycode] + adjustment.h += h_keys[keycode] * cur_high_speed_fac elif keycode in dx_keys: - adjustment.dx += dx_keys[keycode] + adjustment.dx += dx_keys[keycode] * cur_high_speed_fac elif keycode in dy_keys: - adjustment.dy += dy_keys[keycode] + adjustment.dy += dy_keys[keycode] * cur_high_speed_fac while True: working_image = image.copy() @@ -75,7 +78,10 @@ def adjust_squares( print(keycode) if keycode == 27: break - _adjustment_step(keycode) + if keycode == 229: + high_speed = not high_speed + continue + _adjustment_step(keycode, high_speed) cv2.destroyWindow("Window") return adjustment diff --git a/shenzhen_solitaire/card_detection/configuration.py b/shenzhen_solitaire/card_detection/configuration.py index 9287070..0f89083 100644 --- a/shenzhen_solitaire/card_detection/configuration.py +++ b/shenzhen_solitaire/card_detection/configuration.py @@ -99,10 +99,66 @@ def _save_adjustments(zip_file: zipfile.ZipFile, conf: Configuration) -> None: adjustments = {} adjustments[FIELD_ADJUSTMENT_KEY] = dataclasses.asdict(conf.field_adjustment) adjustments[BORDER_ADJUSTMENT_KEY] = dataclasses.asdict(conf.border_adjustment) - - zip_file.writestr( - ADJUSTMENT_FILE_NAME, json.dumps(adjustment), + adjustments[GOAL_ADJUSTMENT_KEY] = dataclasses.asdict(conf.goal_adjustment) + adjustments[BUNKER_ADJUSTMENT_KEY] = dataclasses.asdict(conf.bunker_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: @@ -112,7 +168,8 @@ def save(conf: Configuration, filename: str) -> None: with zipfile.ZipFile(zip_stream, "w") as zip_file: _save_adjustments(zip_file, conf) _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: zip_archive.write(zip_stream.getvalue()) diff --git a/tools/generate/all_borders.py b/tools/generate/all_borders.py new file mode 100644 index 0000000..0770de6 --- /dev/null +++ b/tools/generate/all_borders.py @@ -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() diff --git a/tools/generate/border.py b/tools/generate/border.py index 754d573..1fc8795 100644 --- a/tools/generate/border.py +++ b/tools/generate/border.py @@ -8,11 +8,19 @@ import numpy as np import shenzhen_solitaire.card_detection.adjustment as adjustment import shenzhen_solitaire.card_detection.card_finder as card_finder from shenzhen_solitaire.card_detection.configuration import Configuration +import argparse def main() -> None: """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_square_pos = adjustment.adjust_squares( diff --git a/tools/generate/catalogue.py b/tools/generate/catalogue.py new file mode 100644 index 0000000..2583aba --- /dev/null +++ b/tools/generate/catalogue.py @@ -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() diff --git a/tools/generate/field.py b/tools/generate/field.py index f19368c..bd2a14c 100644 --- a/tools/generate/field.py +++ b/tools/generate/field.py @@ -1,15 +1,42 @@ +import argparse + import cv2 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: """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) - configuration.save(generated_config, "test_config.zip") + args = parser.parse_args() + 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__":