import os import random from copy import deepcopy import gradio as gr import numpy as np from romkan import to_roma from ocr import Recognizer kana_list = """ あいうえお かきくけこ がぎぐげご さしすせそ ざじずぜぞ たちつてと だぢづでど なにぬねの はひふへほ ばびぶべぼ ぱぴぷぺぽ まみむめも やゆよ らりるれろ わを ん アイウエオ カキクケコ ガギグゲゴ サシスセソ ザジズゼゾ タチツテト ダヂヅデド ナニヌネノ ハヒフヘホ バビブベボ パピプペポ マミムメモ ヤユヨ ラリルレロ ワヲ ン """ kana_list = [c for c in kana_list if c.strip()] chars = [os.path.join(dn, fn) for dn, _, ff in os.walk("chars") for fn in ff] def init_chars(): curr_chars = deepcopy(chars) random.shuffle(curr_chars) return curr_chars def get_char(ch=None, assist=True, kana=True, curr_chars: list = []): if not curr_chars: curr_chars = init_chars() if ch is None: img_path = curr_chars.pop() ch = img_path[-5] else: img_path = f"chars/{ch}.png" if not assist: img_path = "bg.png" info = f"{ch} ({to_roma(ch)})" if not kana: info = f"{to_roma(ch)}" return ch, img_path, info, curr_chars font = gr.themes.GoogleFont("Noto Sans") theme = gr.themes.Soft(font=font) with gr.Blocks(theme=theme, title="Kana Writer") as app: ch = "あ" char_img = "chars/あ.png" roma = "あ (a)" curr_img_path = gr.State(char_img) curr_char = gr.State(ch) curr_chars = gr.State([]) recog = Recognizer("model/model.xml", "model/char_list.txt") with gr.Tab("寫字練習"): brush = gr.Brush(default_color="#111", default_size=15) sketch = gr.Sketchpad( char_img, type="numpy", brush=brush, layers=False, image_mode="RGB", eraser=False, show_label=False, ) with gr.Row(): header = gr.Textbox(roma, label="目標") result = gr.Textbox(label="辨識結果") with gr.Row(): with gr.Row(): assist = gr.Checkbox(True, label="輔助") kana = gr.Checkbox(True, label="假名") rand_btn = gr.Button("隨機") check_btn = gr.Button("辨識") def parse_item(item): prob = item["prob"] char = item["char"] return f"{char} ({to_roma(char)}): {prob:.2%}" def valid_item(item): if item["prob"] < 1e-2: return False if item["char"] not in kana_list: return False return True def do_recog(img: dict[str, np.ndarray]): img: np.ndarray = img["layers"][0] img[img == 0] = 255 img[img != 255] = 0 _, nbest = recog(img) return ", ".join(parse_item(item) for items in nbest for item in items if valid_item(item)) def rand_char(assist, kana, chars): char, img, roma, chars = get_char(None, assist, kana, chars) return char, img, img, roma, None, chars def clear(curr_char): return curr_char def change_opt(char, assist, kana, chars): char, img, roma, chars = get_char(char, assist, kana, chars) return char, img, img, roma, chars check_btn.click(do_recog, sketch, result, show_progress="minimal") rand_btn.click( rand_char, [assist, kana, curr_chars], [curr_char, sketch, curr_img_path, header, result, curr_chars], show_progress="minimal", ) sketch.clear(clear, curr_img_path, sketch, show_progress="minimal") assist.change( change_opt, [curr_char, assist, kana, curr_chars], [curr_char, sketch, curr_img_path, header, curr_chars], show_progress="minimal", ) kana.change( change_opt, [curr_char, assist, kana, curr_chars], [curr_char, sketch, curr_img_path, header, curr_chars], show_progress="minimal", ) app.launch()