diff --git a/poker/gui/ui/table_setup_form.ui b/poker/gui/ui/table_setup_form.ui index e373eb4c..02fcd303 100644 --- a/poker/gui/ui/table_setup_form.ui +++ b/poker/gui/ui/table_setup_form.ui @@ -28,7 +28,7 @@ - 0 + 2 @@ -805,1749 +805,1780 @@ Cards - - - - - - - For Tables where cards may be different at times (e.g. rotated) - - - - - - - <html><head/><body><p>If this is ticked, my cards will be recognized via neural network instead of exact temlate matching. This will have a higher tolerance for recognition and allow cards to be slightly different or rotated. </p></body></html> - - - Neural Network based recognition for My Cards - - - - - - - - - <html><head/><body><p>Exact location of the card the bot is holding. This should be as close to the same size as the card templates so it's easier for recognition with neural network. </p></body></html> - - - Right Card Area - - - - - - - - 4 - 0 - - - - <html><head/><body><p>Exact location of the card the bot is holding. This should be as close to the same size as the card templates so it's easier for recognition with neural network. </p></body></html> - - - Left Card Area - - - - - - - Show - - - - - - - Show - - - - - - - - - - - Ensure to mark as narrowly as possible. Don't mark the full card, but only the number and the symbol. - - - Images - - + + + + 9 + 9 + 352 + 751 + + + + Ensure to mark as narrowly as possible. Don't mark the full card, but only the number and the symbol. + + + Images + + + + - - - - - - - - 16 - - - - - - - Qt::AlignCenter - - - - - - - - 35 - 16777215 - - - - 2C - - - false - - - - - - - - 35 - 16777215 - - - - 3C - - - - - - - - 35 - 16777215 - - - - 4C - - - - - - - - 35 - 16777215 - - - - 5C - - - - - - - - 35 - 16777215 - - - - 6C - - - - - - - - 35 - 16777215 - - - - 7C - - - - - - - - 35 - 16777215 - - - - 8C - - - - - - - - 35 - 16777215 - - - - 9C - - - - - - - - 35 - 16777215 - - - - TC - - - - - - - - 35 - 16777215 - - - - JC - - - - - - - - 35 - 16777215 - - - - QC - - - - - - - - 35 - 16777215 - - - - KC - - - - - - - - 35 - 16777215 - - - - AC - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - + + + + + + 16 + + + + + + + Qt::AlignCenter + + - - - - - - - 16 - - - - - - - Qt::AlignCenter - - - - - - - - 35 - 16777215 - - - - 2D - - - - - - - - 35 - 16777215 - - - - 3D - - - - - - - - 35 - 16777215 - - - - 4D - - - - - - - - 35 - 16777215 - - - - 5D - - - - - - - - 35 - 16777215 - - - - 6D - - - - - - - - 35 - 16777215 - - - - 7D - - - - - - - - 35 - 16777215 - - - - 8D - - - - - - - - 35 - 16777215 - - - - 9D - - - - - - - - 35 - 16777215 - - - - TD - - - - - - - - 35 - 16777215 - - - - JD - - - - - - - - 35 - 16777215 - - - - QD - - - - - - - - 35 - 16777215 - - - - KD - - - - - - - - 35 - 16777215 - - - - AD - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - + + + + + 35 + 16777215 + + + + 2C + + + false + + - - - - - - - 16 - - - - - - - Qt::AlignCenter - - - - - - - - 35 - 16777215 - - - - 2S - - - - - - - - 35 - 16777215 - - - - 3S - - - - - - - - 35 - 16777215 - - - - 4S - - - - - - - - 35 - 16777215 - - - - 5S - - - - - - - - 35 - 16777215 - - - - 6S - - - - - - - - 35 - 16777215 - - - - 7S - - - - - - - - 35 - 16777215 - - - - 8S - - - - - - - - 35 - 16777215 - - - - 9S - - - - - - - - 35 - 16777215 - - - - TS - - - - - - - - 35 - 16777215 - - - - JS - - - - - - - - 35 - 16777215 - - - - QS - - - - - - - - 35 - 16777215 - - - - KS - - - - - - - - 35 - 16777215 - - - - AS - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - + + + + + 35 + 16777215 + + + + 3C + + + + + + + + 35 + 16777215 + + + + 4C + + + + + + + + 35 + 16777215 + + + + 5C + + + + + + + + 35 + 16777215 + + + + 6C + + + + + + + + 35 + 16777215 + + + + 7C + + + + + + + + 35 + 16777215 + + + + 8C + + + + + + + + 35 + 16777215 + + + + 9C + + + + + + + + 35 + 16777215 + + + + TC + + + + + + + + 35 + 16777215 + + + + JC + + + + + + + + 35 + 16777215 + + + + QC + + + + + + + + 35 + 16777215 + + + + KC + + + + + + + + 35 + 16777215 + + + + AC + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + + + + + + 16 + + + + + + + Qt::AlignCenter + + + + + + + + 35 + 16777215 + + + + 2D + + + + + + + + 35 + 16777215 + + + + 3D + + + + + + + + 35 + 16777215 + + + + 4D + + + + + + + + 35 + 16777215 + + + + 5D + + + + + + + + 35 + 16777215 + + + + 6D + + + + + + + + 35 + 16777215 + + + + 7D + + + + + + + + 35 + 16777215 + + + + 8D + + + + + + + + 35 + 16777215 + + + + 9D + + + + + + + + 35 + 16777215 + + + + TD + + + + + + + + 35 + 16777215 + + + + JD + + + + + + + + 35 + 16777215 + + + + QD + + + + + + + + 35 + 16777215 + + + + KD + + + + + + + + 35 + 16777215 + + + + AD + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + + + + + + 16 + + + + + + + Qt::AlignCenter + + + + + + + + 35 + 16777215 + + + + 2S + + + + + + + + 35 + 16777215 + + + + 3S + + + + + + + + 35 + 16777215 + + + + 4S + + + + + + + + 35 + 16777215 + + + + 5S + + + + + + + + 35 + 16777215 + + + + 6S + + + + + + + + 35 + 16777215 + + + + 7S + + + + + + + + 35 + 16777215 + + + + 8S + + + + + + + + 35 + 16777215 + + + + 9S + + + + + + + + 35 + 16777215 + + + + TS + + + + + + + + 35 + 16777215 + + + + JS + + + + + + + + 35 + 16777215 + + + + QS + + + + + + + + 35 + 16777215 + + + + KS + + + + + + + + 35 + 16777215 + + + + AS + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + + 35 + 16777215 + + + + 3H + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + + 35 + 16777215 + + + + 7H + + + + + + + false + + + + 35 + 16777215 + + + + V + + - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - - 35 - 16777215 - - - - 3H - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - - 35 - 16777215 - - - - 7H - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - - 35 - 16777215 - - - - 8H - - - - - - - - 35 - 16777215 - - - - 5H - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - - 35 - 16777215 - - - - 9H - - - - - - - - 35 - 16777215 - - - - JH - - - - - - - - 16 - - - - - - - Qt::AlignCenter - - - - - - - - 35 - 16777215 - - - - 4H - - - - - - - - 35 - 16777215 - - - - QH - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - - 35 - 16777215 - - - - TH - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - - 35 - 16777215 - - - - KH - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - - 35 - 16777215 - - - - AH - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - - 35 - 16777215 - - - - 6H - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - - - - - 35 - 16777215 - - - - 2H - - - - - - - false - - - - 35 - 16777215 - - - - V - - - - + + + + + 35 + 16777215 + + + + 8H + + - - - - - - - - Used to recognize which players are still in the game. Mark very narrowly to ensure it's the same for all players. + + + + + 35 + 16777215 + - Covered card + 5H - - - - Qt::Vertical + + + + false + + + + 35 + 16777215 + - + + V + + + + + + - 20 - 40 + 35 + 16777215 - + + 9H + + - - - - <html><head/><body><p>The dealer button image. Make sure to select very narrowly and don't include any background that might change depending on it's location</p></body></html> + + + + + 35 + 16777215 + - Dealer button + JH - - + + + + + 16 + + + + + + + Qt::AlignCenter + + + + + + + + 35 + 16777215 + + + + 4H + + + + + + + + 35 + 16777215 + + + + QH + + + + + false + + + 35 + 16777215 + + - Show + V - - + + + + + 35 + 16777215 + + + + TH + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + + 35 + 16777215 + + + + KH + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + false + + + 35 + 16777215 + + - Show + V + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + + 35 + 16777215 + + + + AH + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + + 35 + 16777215 + + + + 6H + + + + + + + false + + + + 35 + 16777215 + + + + V + + + + + + + + 35 + 16777215 + + + + 2H + + + + + + + false + + + + 35 + 16777215 + + + + V - - - - - - - 0 - 0 - - - - <html><head/><body><p>Train the Neural Network to recognize the cards. This can take several minutes. For faster speed install CUDA and CUDNN (To find out more google Tensorflow GPU setup). The whole process can take up to 30 minutes. Please be patient.</p><p><br/></p></body></html> - - - Train Neural Network - - - - + + + + + + + false + + + Show + + + + + + + Used to recognize which players are still in the game. Mark very narrowly to ensure it's the same for all players. + + + Covered card + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 13 + 13 + + + + + + + + <html><head/><body><p>The dealer button image. Make sure to select very narrowly and don't include any background that might change depending on it's location</p></body></html> + + + Dealer button + + + + + + + false + + + Show + + + + + + + + + + + 20 + 750 + 329 + 162 + + + + + + + For Tables where cards may be different at times (e.g. rotated) + + + + + + + <html><head/><body><p>If this is ticked, my cards will be recognized via neural network instead of exact temlate matching. This will have a higher tolerance for recognition and allow cards to be slightly different or rotated. </p></body></html> + + + Neural Network based recognition for My Cards + + + + + + + false + + + <html><head/><body><p>If this is ticked, table cards will be recognized via neural network instead of exact temlate matching. This will have a higher tolerance for recognition and allow cards to be slightly different or rotated. </p></body></html> + + + 0 + + + Neural Network based recognition for Table Cards + + + + + + + + + <html><head/><body><p>Exact location of the card the bot is holding. This should be as close to the same size as the card templates so it's easier for recognition with neural network. </p></body></html> + + + Right Card Area + + + + + + + + 4 + 0 + + + + <html><head/><body><p>Exact location of the card the bot is holding. This should be as close to the same size as the card templates so it's easier for recognition with neural network. </p></body></html> + + + Left Card Area + + + + + + + Show + + + + + + + Show + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Train the Neural Network to recognize the cards. This can take several minutes. For faster speed install CUDA and CUDNN (To find out more google Tensorflow GPU setup). The whole process can take up to 30 minutes. Please be patient.</p><p><br/></p></body></html> + + + Train Neural Network + + + + + + + @@ -3157,7 +3188,7 @@ Saving screenshots will not override exisiting screenshots. 0 0 - 879 + 890 69 @@ -3215,8 +3246,8 @@ Saving screenshots will not override exisiting screenshots. 0 0 - 879 - 666 + 890 + 655 diff --git a/poker/scraper/table_scraper.py b/poker/scraper/table_scraper.py index fa838632..1d2144bb 100644 --- a/poker/scraper/table_scraper.py +++ b/poker/scraper/table_scraper.py @@ -1,5 +1,7 @@ """Recognize table""" import logging +from PIL import Image +import numpy as np from poker.scraper.table_scraper_nn import predict from poker.scraper.table_setup_actions_and_signals import CARD_SUITES, CARD_VALUES @@ -110,11 +112,95 @@ def get_my_cards_nn(self): def get_table_cards2(self): """Get the cards on the table""" self.table_cards = [] - for value in CARD_VALUES: - for suit in CARD_SUITES: - if is_template_in_search_area(self.table_dict, self.screenshot, - value.lower() + suit.lower(), 'table_cards_area'): - self.table_cards.append(value + suit) + if 'use_neural_network_table' in self.table_dict and ( + self.table_dict['use_neural_network_table'] == '2' or self.table_dict[ + 'use_neural_network_table'] == 'CheckState.Checked'): + self.get_table_cards_nn() + return True + else: + for value in CARD_VALUES: + for suit in CARD_SUITES: + if is_template_in_search_area(self.table_dict, self.screenshot, + value.lower() + suit.lower(), 'table_cards_area'): + self.table_cards.append(value + suit) + log.info(f"Table cards: {self.table_cards}") + if len(self.table_cards) == 1 or len(self.table_cards) == 2: + log.warning(f"Only recognized {len(self.table_cards)} cards on the table. " + f"This can happen if cards are sliding in or if some of the templates are wrong") + return False + return True + + def get_table_cards_nn(self): + # Sacamos una foto de la zona de las cartas en la mesa + nn_image_area = self.table_dict['table_cards_area'] + nn_image = self.screenshot.crop( + (nn_image_area['x1'], nn_image_area['y1'], nn_image_area['x2'], nn_image_area['y2'])) + nn_image_rgb = nn_image.convert('RGB') + + # Define los colores a reemplazar y el color de reemplazo + target_colors = [(37, 82, 29), (105, 136, 98), (86, 65, 31)] + lower_replace_range = (226, 226, 226) + replacement_color = (255, 255, 255) + + # Convertimos la imagen a un array de numpy para un procesamiento más chido + np_image = np.array(nn_image_rgb) + + # Inicializamos la máscara con False + mask = np.zeros(np_image.shape[:2], dtype=bool) + + # Iteramos sobre cada color objetivo y actualizamos la máscara + for color in target_colors: + mask |= np.all(np.abs(np_image - color) <= 20, axis=-1) + + # Reemplazamos los píxeles encontrados con el color de reemplazo + np_image[mask] = replacement_color + + # Encontramos los píxeles en el rango de colores a reemplazar por blanco + replace_mask = np.all((np_image >= lower_replace_range) & (np_image <= replacement_color), axis=-1) + + # Reemplazamos los píxeles encontrados con el color blanco + np_image[replace_mask] = replacement_color + + # Convertimos el array de nuevo a una imagen PIL + nn_image_result = Image.fromarray(np_image) + + # Dividimos la imagen en 10 partes a lo ancho + cantidad_de_cortes = 10 + width_per_card = nn_image_result.width // cantidad_de_cortes + + + # Lista para almacenar las imágenes finales + final_images = [] + + # Iteramos sobre las partes de la imagen original + for i in range(cantidad_de_cortes): + left = i * width_per_card + right = (i + 1) * width_per_card + + # Extraemos cada parte de la imagen original + image_part = nn_image_result.crop((left, 0, right, nn_image_result.height)) + + # Convertir la parte de la imagen a formato OpenCV (numpy array) + image_array = np.array(image_part) + + # Definir el color blanco y la tolerancia permitida + color_blanco = (255, 255, 255) + tolerancia = 30 # Puedes ajustar este valor según tus necesidades + + # Calcular el porcentaje de píxeles blancos en la imagen + white_percentage = np.sum(np.all(np.abs(image_array - color_blanco) < tolerancia, axis=-1)) / np.prod(image_array.shape[:-1]) + + # Si el porcentaje es menor al 72%, añadimos la imagen a la lista final + if white_percentage < 0.83: + final_images.append(image_part) + + # Procesamos y guardamos las imágenes que cumplen con la condición + for i, image_part in enumerate(final_images, start=1): + # Procesamos la carta con la red neuronal y almacenamos el resultado + card_result = predict(image_part, self.nn_model, self.table_dict['_class_mapping']) + self.table_cards.append(card_result) + # Guardamos la imagen directamente con el nombre predicho + image_part.save(get_dir('log') + f'/pics/table/{card_result}.png') log.info(f"Table cards: {self.table_cards}") if len(self.table_cards) == 1 or len(self.table_cards) == 2: log.warning(f"Only recognized {len(self.table_cards)} cards on the table. " @@ -122,6 +208,7 @@ def get_table_cards2(self): return False return True + def get_dealer_position2(self): # pylint: disable=inconsistent-return-statements """Determines position of dealer, where 0=myself, continous counter clockwise""" for i in range(self.total_players): diff --git a/poker/scraper/table_scraper_nn.py b/poker/scraper/table_scraper_nn.py index 24b8670c..0edc364d 100644 --- a/poker/scraper/table_scraper_nn.py +++ b/poker/scraper/table_scraper_nn.py @@ -16,6 +16,7 @@ TRAIN_FOLDER = get_dir('pics', "training_cards") VALIDATE_FOLDER = get_dir('pics', "validate_cards") TEST_FOLDER = get_dir('tests', "test_cards") +TEST_FOLDER_TRAIN = get_dir('pics', "test_cards") log = logging.getLogger(__name__) @@ -54,9 +55,10 @@ def adjust_colors(a, tol=120): # tol - tolerance to decides on the "-ish" facto class CardNeuralNetwork(): @staticmethod - def create_augmented_images(table_name): + def create_augmented_images(table_name, train_count=600, validate_count=600, test_count=500): shutil.rmtree(TRAIN_FOLDER, ignore_errors=True) shutil.rmtree(VALIDATE_FOLDER, ignore_errors=True) + shutil.rmtree(TEST_FOLDER_TRAIN, ignore_errors=True) log.info("Augmenting data with random pictures based on templates") @@ -66,14 +68,14 @@ def create_augmented_images(table_name): width_shift_range=0.05, height_shift_range=0.05, shear_range=0.02, - zoom_range=0.05, + zoom_range=[0.9, 1.5], horizontal_flip=False, fill_mode='nearest') mongo = MongoManager() table = mongo.get_table(table_name) - for folder in [TRAIN_FOLDER, VALIDATE_FOLDER]: + for folder, count in zip([TRAIN_FOLDER, VALIDATE_FOLDER, TEST_FOLDER_TRAIN], [train_count, validate_count, test_count]): card_ranks_original = '23456789TJQKA' original_suits = 'CDHS' @@ -101,8 +103,8 @@ def create_augmented_images(table_name): save_format='png', ): i += 1 - if i > 500: - break # otherwise the generator would loop indefinitely + if i >= count: + break # Limit the number of generated images def train_neural_network(self): from tensorflow.keras.preprocessing.image import ImageDataGenerator @@ -128,17 +130,31 @@ def train_neural_network(self): class_mode='binary', color_mode='rgb') + self.test_generator = ImageDataGenerator( + rescale=0.00, + shear_range=0.00, + zoom_range=0.00, + horizontal_flip=False).flow_from_directory( + directory=os.path.join(SCRAPER_DIR, TEST_FOLDER_TRAIN), + target_size=(img_height, img_width), + batch_size=128, + class_mode='binary', + color_mode='rgb', + shuffle=False) + num_classes = 52 input_shape = (50, 15, 3) epochs = 50 - from tensorflow.keras.callbacks import TensorBoard, EarlyStopping, LearningRateScheduler + from tensorflow.keras.callbacks import TensorBoard, EarlyStopping from tensorflow.keras.constraints import MaxNorm - from tensorflow.keras.layers import Conv2D, MaxPooling2D, BatchNormalization + from tensorflow.keras.layers import Conv2D, MaxPooling2D from tensorflow.keras.layers import Dropout, Flatten, Dense from tensorflow.keras.models import Sequential - from tensorflow.keras.losses import sparse_categorical_crossentropy - from tensorflow.keras import optimizers - from tensorflow.math import exp + from tensorflow.keras.optimizers import Adam + + # Configurar el optimizador con una tasa de aprendizaje específica + custom_optimizer = Adam(learning_rate=0.001) + model = Sequential() model.add(Conv2D(64, (3, 3), input_shape=input_shape, activation='relu', padding='same')) model.add(Conv2D(64, (3, 3), activation='relu', padding='same')) @@ -161,11 +177,13 @@ def train_neural_network(self): from tensorflow.keras.losses import sparse_categorical_crossentropy from tensorflow.keras import optimizers model.compile(loss=sparse_categorical_crossentropy, - optimizer=optimizers.Adam(), + optimizer=custom_optimizer, metrics=['accuracy']) log.info(model.summary()) + self.model = model + early_stop = EarlyStopping(monitor='val_accuracy', min_delta=0, patience=5, # increased patience as sometimes more epochs are beneficial @@ -184,9 +202,27 @@ def train_neural_network(self): validation_data=self.validation_generator, callbacks=[early_stop]) self.model = model + score = model.evaluate(self.validation_generator, steps=52) print('Validation loss:', score[0]) print('Validation accuracy:', score[1]) + log.info(model.summary()) + self.test_neural_network() + + def test_neural_network(self): + log.info("Testing the neural network") + + if self.model is not None: + # Evaluar el modelo en el conjunto de prueba + test_score = self.model.evaluate(self.test_generator, steps=52) + log.info('Test loss: %f', test_score[0]) + log.info('Test accuracy: %f', test_score[1]) + + # Devolver el porcentaje de resultados de la prueba + return test_score[1] + else: + log.error("Model not trained. Please train the model before testing.") + return None def save_model_to_disk(self): # serialize model to JSON diff --git a/poker/scraper/table_screen_based.py b/poker/scraper/table_screen_based.py index 17ff6609..7def5404 100644 --- a/poker/scraper/table_screen_based.py +++ b/poker/scraper/table_screen_based.py @@ -568,7 +568,6 @@ def get_new_hand(self, mouse, h, p): self.Game_Number = 0 h.game_number_on_screen = 0 self.get_my_funds(h, p) - h.lastGameID = str(h.GameID) h.GameID = int(round(np.random.uniform(0, 999999999), 0)) cards = ' '.join(self.mycards) @@ -581,23 +580,19 @@ def get_new_hand(self, mouse, h, p): t_algo = threading.Thread(name='Algo', target=self.call_genetic_algorithm, args=(p,)) t_algo.daemon = True t_algo.start() - self.gui_signals.signal_funds_chart_update.emit(self.game_logger) self.gui_signals.signal_bar_chart_update.emit(self.game_logger, p.current_strategy) - h.myLastBet = 0 h.myFundsHistory.append(self.myFunds) h.previousCards = self.mycards h.lastSecondRoundAdjustment = 0 h.last_round_bluff = False # reset the bluffing marker h.round_number = 0 - mouse.move_mouse_away_from_buttons_jump() self.take_screenshot(False, p) else: log.debug("Game number on screen: " + str(h.game_number_on_screen)) self.get_my_funds(h, p) - return True def upload_collusion_wrapper(self, p, h): diff --git a/poker/scraper/table_setup_actions_and_signals.py b/poker/scraper/table_setup_actions_and_signals.py index 528b5cef..f5069a7f 100644 --- a/poker/scraper/table_setup_actions_and_signals.py +++ b/poker/scraper/table_setup_actions_and_signals.py @@ -98,6 +98,7 @@ def connect_signals_with_slots(self): self.ui.topleft_corner.clicked.connect(lambda: self.save_topleft_corner()) self.ui.current_player.currentIndexChanged[int].connect(lambda: self._update_selected_player()) self.ui.use_neural_network.clicked.connect(lambda: self._save_use_nerual_network_checkbox()) + self.ui.use_neural_network_table.clicked.connect(lambda: self._save_use_nerual_network_table_checkbox()) self.ui.max_players.currentIndexChanged[int].connect(lambda: self._save_max_players()) self.ui.spinBox_nthSecond.valueChanged.connect(lambda: self._update_nth_second()) self.ui.spinBox_xTimes.valueChanged.connect(lambda: self._update_x_times()) @@ -231,6 +232,18 @@ def _save_use_nerual_network_checkbox(self): mongo.update_state(state=is_set, label=label, table_name=self.table_name) log.info("Saving complete") + def _save_use_nerual_network_table_checkbox(self): + owner = mongo.get_table_owner(self.table_name) + if owner != COMPUTER_NAME: + pop_up("Not authorized.", + "You can only edit your own tables. Please create a new copy or start with a new blank table") + return + label = 'use_neural_network_table' + is_set = self.ui.use_neural_network_table.checkState() + log.info(f"Saving use neural network table tickbox {is_set}") + mongo.update_state(state=is_set, label=label, table_name=self.table_name) + log.info("Saving complete") + def _connect_cards_with_save_slot(self): # contains cards in the deck deck = [x.lower() + y.lower() for x in CARD_VALUES for y in CARD_SUITES] @@ -739,6 +752,18 @@ def load(self): log.info(f"No available data for {check_box}") self.signal_check_box.emit(check_box, 0) + check_boxes = ['use_neural_network_table'] + for check_box in check_boxes: + try: + if isinstance(table[check_box], int): + nn = 1 if table[check_box] > 0 else 0 + if isinstance(table[check_box], str): + nn = 1 if table[check_box] == 'CheckState.Checked' else 0 + self.signal_check_box.emit(check_box, int(nn)) + except KeyError: + log.info(f"No available data for {check_box}") + self.signal_check_box.emit(check_box, 0) + try: all_values = [self.ui.max_players.itemText(i) for i in range(self.ui.max_players.count())] index = all_values.index(str(table['max_players']['value'])) diff --git a/poker/tests/test_montecarlo.py b/poker/tests/test_montecarlo.py index 54431c47..795f0d13 100644 --- a/poker/tests/test_montecarlo.py +++ b/poker/tests/test_montecarlo.py @@ -149,7 +149,7 @@ def test_evaluator( # Unittest to ensure correct winning probabilities are returned def test_monteCarlo(self): # pylint: disable=too-many-statements def testRun(Simulation, my_cards, cards_on_table, players, expected_results, opponent_range=1): - maxRuns = 15000 # maximum number of montecarlo runs + maxruns = 15000 # maximum number of montecarlo runs testRuns = 5 # make several testruns to get standard deviation of winning probability secs = 1 # cut simulation short if amount of seconds are exceeded @@ -157,7 +157,7 @@ def testRun(Simulation, my_cards, cards_on_table, players, expected_results, opp for _ in range(testRuns): start_time = time.time() + secs logger = MagicMock() - Simulation.run_montecarlo(logger, my_cards, cards_on_table, players, 1, maxRuns=maxRuns, + Simulation.run_montecarlo(logger, my_cards, cards_on_table, players, 1, max_runs=maxruns, timeout=start_time, ghost_cards='', opponent_range=opponent_range) equity = Simulation.equity total_result.append(equity * 100) diff --git a/poker/tests/test_tensorflow.py b/poker/tests/test_tensorflow.py index 25f28184..514e2498 100644 --- a/poker/tests/test_tensorflow.py +++ b/poker/tests/test_tensorflow.py @@ -3,6 +3,7 @@ import pytest from PIL import Image +import os from poker.scraper.table_scraper_nn import TEST_FOLDER, predict from poker.tools.helper import ON_CI @@ -27,16 +28,34 @@ def test_load_nn_model(): @pytest.mark.skipif(ON_CI, reason='too long for ci') def test_train_card_neural_network_and_predict(): - from poker.scraper.table_scraper_nn import CardNeuralNetwork + from poker.scraper.table_scraper_nn import CardNeuralNetwork, predict n = CardNeuralNetwork() - n.create_augmented_images('GG Poker2') + n.create_augmented_images('GGpoker') n.train_neural_network() n.save_model_to_disk() n.load_model() for card in ['AH', '5C', 'QS', '6C', 'JC', '2H']: - filename = f'{TEST_FOLDER}/' + card + '.png' - img = Image.open(filename) - prediction = predict(img, n.loaded_model, n.class_mapping) - log.info(f"Prediction: {prediction} vs actual: {card}") - assert card == prediction + # Obtener el nombre de la carpeta específica + folder_name = card + + # Acceder a la carpeta correspondiente + folder_path = os.path.join(TEST_FOLDER, folder_name) + + # Buscar archivos en la carpeta + for file_name in os.listdir(folder_path): + if file_name.startswith(card) and file_name.endswith('.png'): + # La imagen coincide con el nombre de la tarjeta + file_path = os.path.join(folder_path, file_name) + img = Image.open(file_path) + prediction = predict(img, n.loaded_model, n.class_mapping) + log.info(f"Prediction: {prediction} vs actual: {card}") + assert card == prediction + + elif card in file_name and file_name.endswith('.png'): + # La imagen tiene el nombre similar a "5C_0_27" + file_path = os.path.join(folder_path, file_name) + img = Image.open(file_path) + prediction = predict(img, n.loaded_model, n.class_mapping) + log.info(f"Prediction for similar name: {prediction} vs actual: {card}") + assert card == prediction