File size: 6,487 Bytes
aedbeed
 
 
 
 
 
6ee449a
 
aedbeed
 
2d81c22
 
 
 
 
 
 
aedbeed
 
6bf3506
6639f73
 
6bf3506
aedbeed
 
 
 
 
 
 
 
 
 
 
 
6bf3506
aedbeed
6ee449a
aedbeed
 
 
6ee449a
aedbeed
 
6ee449a
aedbeed
 
 
6ee449a
aedbeed
 
 
 
 
 
 
6ee449a
aedbeed
 
 
 
 
 
6ee449a
aedbeed
 
 
6ee449a
 
 
 
 
 
 
 
 
 
aedbeed
 
 
 
6ee449a
 
 
 
de49ebf
 
6ee449a
aedbeed
 
 
 
 
 
 
 
 
 
6ee449a
 
 
 
aedbeed
 
6ee449a
aedbeed
 
6ee449a
aedbeed
 
 
 
6ee449a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de49ebf
6ee449a
de49ebf
 
6ee449a
 
 
de49ebf
6ee449a
 
 
 
aedbeed
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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 validate_pbn, create_single_pbn_string

# 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:
        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()

    pbn_str = create_single_pbn_string(df, board_no=board_no)
    try:
        validate_pbn(pbn_str)
    except Exception as e:
        print(e)
        gr.Warning(f'Fail to save: {e}')
        return files, files, d

    file_name = f'board_{board_no:03d}.pbn'
    file_path = os.path.join(d,file_name)
    with open(file_path, 'w') as f:
        f.write(pbn_str)

    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

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([])
    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')
                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])

demo.queue().launch()