Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pandas as pd | |
| import subprocess | |
| import os | |
| from tempfile import mkdtemp | |
| from timeit import default_timer as timer | |
| from hand_record import create_hand_record_pdf | |
| from pbn_util import create_pbn_file | |
| from bridge_util import validate_dataframe, df_info, roll_direction | |
| # Download model and libraries from repo | |
| try: | |
| token = os.environ.get("model_token") | |
| if token: | |
| subprocess.run(["git", "clone", f"https://oauth2:{token}@huggingface.co/vincentlui/bridge_hand_detect"]) | |
| subprocess.run(["git", "pull"]) | |
| except: | |
| print('Fail to download code') | |
| try: | |
| from bridge_hand_detect2.predict import CardDetectionModel | |
| except Exception as e: | |
| print(e) | |
| from bridge_hand_detect.predict import CardDetectionModel | |
| custom_css = \ | |
| """ | |
| /* Hide sort buttons at gr.DataFrame */ | |
| .sort-button { | |
| display: none !important; | |
| } | |
| """ | |
| INPUT_IMG_HEIGHT = 480 | |
| OUTPUT_IMG_HEIGHT = 320 | |
| css = ".output_img {display:block; margin-left: auto; margin-right: auto}" | |
| model = CardDetectionModel() | |
| def predict(image_path, top_hand_idx): | |
| start = timer() | |
| df = None | |
| try: | |
| hands, (width,height) = model(image_path, augment=True, top_hand_idx=top_hand_idx) | |
| print(hands) | |
| # Output dataframe | |
| df = default_df.copy(deep=True)#pd.DataFrame(['♠', '♥', '♦', '♣'], columns=['']) | |
| for hand in hands: | |
| df[hand.direction] = [''.join(c) for c in hand.cards] | |
| except Exception as e: | |
| print(e) | |
| raise gr.Error('Cannot process image') | |
| end = timer() | |
| print(f'Process time: {end - start:.02f} seconds') | |
| return df | |
| default_df = pd.DataFrame({'':['♠', '♥', '♦', '♣'], | |
| 'N': ['']*4, | |
| 'E': ['']*4, | |
| 'S': ['']*4, | |
| 'W': ['']*4}) | |
| def save_file(df, cache_dir, files, board_no): | |
| d = cache_dir | |
| if cache_dir is None: | |
| d = mkdtemp() | |
| try: | |
| validate_dataframe(df) | |
| except Exception as e: | |
| print(e) | |
| gr.Warning(f'Fail to save pbn. Error in table entries. {e}') | |
| return files, files, d | |
| file_name = f'board_{board_no:03d}.pbn' | |
| file_path = os.path.join(d,file_name) | |
| create_pbn_file(df, file_path, board_no=board_no) | |
| if not file_path in files: | |
| files.append(file_path) | |
| return files, files, d | |
| def create_hand_record(files, event, site): | |
| file_path = create_hand_record_pdf(files, event=event, site=site) | |
| return file_path | |
| def print_df_info(df): | |
| return df_info(df) | |
| def change_direction_in_df(df, top_direction_idx:int, current_top_idx:int): | |
| roll_idx = current_top_idx - top_direction_idx | |
| return roll_direction(df, roll_idx), top_direction_idx | |
| with gr.Blocks(css=custom_css) as demo: | |
| gr.Markdown( | |
| """ | |
| # Bridge Hand Scanner - Read all four hands from an image | |
| This app scans an image taken from (e.g. your smartphone) and reads all 52 cards. The top hand is regarded as North. | |
| The results can be exported as a PBN file, which can be imported to other bridge software such as double dummy solvers. | |
| 1. Upload an image showing all four hands fanned as shown in the example. | |
| 2. Click *Submit*. The scan result will be displayed in the table. | |
| 3. Verify the output and correct any missing or wrong card in the table. | |
| 4. Enter the information of the deal. | |
| 5. Click *Save* to generate a PBN file. | |
| 6. In the tab *Hand Record*, You can upload all the PBN files and create a hand record as a PDF file. | |
| Tips: | |
| - This AI reads the values at corners of the playing cards. Make sure they are visible and as large as possible. | |
| - To get the best accuracy, place the cards following the layout in the examples. | |
| Please send your comments to <vincentlui123@gmail.com>. | |
| """) | |
| total = gr.State(0) | |
| gradio_cache_dir = gr.State() | |
| files = gr.State([]) | |
| current_top_idx = gr.State(0) | |
| with gr.Tab('Scan Image'): | |
| with gr.Row(): | |
| with gr.Column(): | |
| a1 = gr.Image(type="filepath",sources=['upload'],interactive=True,height=INPUT_IMG_HEIGHT) | |
| with gr.Row(): | |
| a2 = gr.ClearButton() | |
| a3 = gr.Button('Submit',variant="primary") | |
| with gr.Accordion("Board Details",open=True): | |
| with gr.Row(): | |
| a_board_no = gr.Number(label="Board", value=1, minimum=1, maximum=999, interactive=True, min_width=80) | |
| a_top = gr.Dropdown(['N','E','S','W'], label='Top', value='N', interactive=True, min_width=80, type='index') | |
| a_deck = gr.Dropdown(['Standard (AKQJ)'], label='Deck', | |
| value='Standard (AKQJ)', type='index', interactive=True, min_width=80, scale=2) | |
| # with gr.Accordion("Contract Details",open=False) as a_c: | |
| # with gr.Row(): | |
| # with gr.Column(scale=3): | |
| # with gr.Group(): | |
| # a_level = gr.Radio(['1','2','3','4','5','6','7','AP'], label='Contract') | |
| # a_trump = gr.Radio(['♠', '♥', '♦', '♣', 'NT'], show_label=False) | |
| # a_dbl = gr.Radio(['X', 'XX'], show_label=False) | |
| # a_declarer = gr.Radio(['N','E','S','W'], label='Declarer', min_width=80, scale=1) | |
| a4 = gr.Examples('examples', a1) | |
| with gr.Column(): | |
| b1 = gr.Dataframe(value=default_df, datatype="str", row_count=(4,'fixed'), col_count=(5,'fixed'), | |
| headers=['', 'N', 'E', 'S', 'W'], | |
| interactive=True, column_widths=['8%', '23%','23%','23%','23%']) | |
| b2 = gr.Button('Save') | |
| b_info_panel = gr.TextArea(lines=5,show_label=False, interactive=False) | |
| b3 = gr.File(interactive=False, file_count='multiple') | |
| with gr.Tab('Hand Record'): | |
| with gr.Row(): | |
| with gr.Column(): | |
| tab2_upload_file = gr.Files(interactive=True) | |
| tab2_event = gr.Textbox(max_lines=1, placeholder='Event name', label='Event') | |
| tab2_site = gr.Textbox(max_lines=1, placeholder='Site name', label='Site') | |
| tab2_submit_button = gr.Button('Create Hand Record',variant="primary") | |
| tab2_download_file = gr.File(interactive=False) | |
| tab2_submit_button.click(create_hand_record, [tab2_upload_file, tab2_event, tab2_site], tab2_download_file) | |
| a2.add([a1,b1]) | |
| a3.click(predict, [a1, a_top], [b1]) | |
| b2.click(save_file, [b1, gradio_cache_dir, files, a_board_no], [b3, tab2_upload_file, gradio_cache_dir]) | |
| b1.change(print_df_info, b1, b_info_panel) | |
| a_top.change(change_direction_in_df, [b1, a_top, current_top_idx], [b1, current_top_idx]) | |
| demo.queue().launch() |