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 . """) 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()