Spaces:
Sleeping
Sleeping
Commit
·
c6905fb
1
Parent(s):
1b163b8
add source
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- README.md +8 -2
- app.py +199 -0
- data/KiwiMaru-Regular.ttf +3 -0
- data/images/bg.png +0 -0
- data/images/kana-chars//343/201/202.png +0 -0
- data/images/kana-chars//343/201/204.png +0 -0
- data/images/kana-chars//343/201/206.png +0 -0
- data/images/kana-chars//343/201/210.png +0 -0
- data/images/kana-chars//343/201/212.png +0 -0
- data/images/kana-chars//343/201/213.png +0 -0
- data/images/kana-chars//343/201/214.png +0 -0
- data/images/kana-chars//343/201/215.png +0 -0
- data/images/kana-chars//343/201/216.png +0 -0
- data/images/kana-chars//343/201/217.png +0 -0
- data/images/kana-chars//343/201/220.png +0 -0
- data/images/kana-chars//343/201/221.png +0 -0
- data/images/kana-chars//343/201/222.png +0 -0
- data/images/kana-chars//343/201/223.png +0 -0
- data/images/kana-chars//343/201/224.png +0 -0
- data/images/kana-chars//343/201/225.png +0 -0
- data/images/kana-chars//343/201/226.png +0 -0
- data/images/kana-chars//343/201/227.png +0 -0
- data/images/kana-chars//343/201/230.png +0 -0
- data/images/kana-chars//343/201/231.png +0 -0
- data/images/kana-chars//343/201/232.png +0 -0
- data/images/kana-chars//343/201/233.png +0 -0
- data/images/kana-chars//343/201/234.png +0 -0
- data/images/kana-chars//343/201/235.png +0 -0
- data/images/kana-chars//343/201/236.png +0 -0
- data/images/kana-chars//343/201/237.png +0 -0
- data/images/kana-chars//343/201/240.png +0 -0
- data/images/kana-chars//343/201/241.png +0 -0
- data/images/kana-chars//343/201/242.png +0 -0
- data/images/kana-chars//343/201/244.png +0 -0
- data/images/kana-chars//343/201/245.png +0 -0
- data/images/kana-chars//343/201/246.png +0 -0
- data/images/kana-chars//343/201/247.png +0 -0
- data/images/kana-chars//343/201/250.png +0 -0
- data/images/kana-chars//343/201/251.png +0 -0
- data/images/kana-chars//343/201/252.png +0 -0
- data/images/kana-chars//343/201/253.png +0 -0
- data/images/kana-chars//343/201/254.png +0 -0
- data/images/kana-chars//343/201/255.png +0 -0
- data/images/kana-chars//343/201/256.png +0 -0
- data/images/kana-chars//343/201/257.png +0 -0
- data/images/kana-chars//343/201/260.png +0 -0
- data/images/kana-chars//343/201/261.png +0 -0
- data/images/kana-chars//343/201/262.png +0 -0
- data/images/kana-chars//343/201/263.png +0 -0
- data/images/kana-chars//343/201/264.png +0 -0
README.md
CHANGED
|
@@ -14,7 +14,13 @@ license: mit
|
|
| 14 |
## Font Image Generation
|
| 15 |
|
| 16 |
```bash
|
| 17 |
-
python
|
| 18 |
```
|
| 19 |
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
## Font Image Generation
|
| 15 |
|
| 16 |
```bash
|
| 17 |
+
python image.py generate
|
| 18 |
```
|
| 19 |
|
| 20 |
+
## Font Source
|
| 21 |
+
|
| 22 |
+
- [Google Font - Kiwi Maru](https://fonts.google.com/specimen/Kiwi+Maru)
|
| 23 |
+
|
| 24 |
+
## Model Source
|
| 25 |
+
|
| 26 |
+
- [OpenVINO Open Model Zoo - Handwritten Japanese Recognition FP16-INT8](https://storage.openvinotoolkit.org/repositories/open_model_zoo/2023.0/models_bin/1/handwritten-japanese-recognition-0001/FP16-INT8)
|
app.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
from copy import deepcopy
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
import gradio as gr
|
| 6 |
+
import numpy as np
|
| 7 |
+
|
| 8 |
+
from utils import KanaData, Recognizer
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class App:
|
| 12 |
+
def __init__(
|
| 13 |
+
self,
|
| 14 |
+
kana_data_path="data/kana-data.json",
|
| 15 |
+
kana_char_dir="data/images/kana-chars",
|
| 16 |
+
bg_image_path="data/images/bg.png",
|
| 17 |
+
model_xml_path="model/model.xml",
|
| 18 |
+
model_char_path="model/chars.txt",
|
| 19 |
+
default_kana="あ",
|
| 20 |
+
font_name="Kiwi Maru",
|
| 21 |
+
brush_color="#111",
|
| 22 |
+
brush_size=15,
|
| 23 |
+
):
|
| 24 |
+
self.brush_color = brush_color
|
| 25 |
+
self.brush_size = brush_size
|
| 26 |
+
self.bg_image_path = bg_image_path
|
| 27 |
+
self.recognizer = Recognizer(model_xml_path, model_char_path)
|
| 28 |
+
|
| 29 |
+
self.kana_data = KanaData.load(kana_data_path)
|
| 30 |
+
self.kana_set = {kana for kana in self.kana_data.spell if len(kana) == 1}
|
| 31 |
+
|
| 32 |
+
self.kana_char_dir = Path(kana_char_dir)
|
| 33 |
+
self.kana_images = [str(p) for p in self.kana_char_dir.rglob("*")]
|
| 34 |
+
|
| 35 |
+
self.font_name = font_name
|
| 36 |
+
|
| 37 |
+
self.default_kana = default_kana
|
| 38 |
+
self.default_kana_image = str(self.kana_char_dir / f"{self.default_kana}.png")
|
| 39 |
+
roma = self.conv_kana_to_roma(self.default_kana)
|
| 40 |
+
self.default_roma = f"{self.default_kana} ({roma})"
|
| 41 |
+
|
| 42 |
+
self.init_app()
|
| 43 |
+
|
| 44 |
+
def init_app(self):
|
| 45 |
+
with self.init_blocks() as self.app:
|
| 46 |
+
self.init_states()
|
| 47 |
+
self.init_layout()
|
| 48 |
+
self.init_events()
|
| 49 |
+
self.init_storage()
|
| 50 |
+
|
| 51 |
+
def init_blocks(self):
|
| 52 |
+
return gr.Blocks(title="假名手寫練習")
|
| 53 |
+
|
| 54 |
+
def init_layout(self):
|
| 55 |
+
with gr.Sidebar("練習設定"):
|
| 56 |
+
self.init_setting_tab()
|
| 57 |
+
with gr.Tab("寫字練習"):
|
| 58 |
+
self.init_practice_tab()
|
| 59 |
+
|
| 60 |
+
def init_states(self):
|
| 61 |
+
self.curr_kana = gr.State(self.default_kana)
|
| 62 |
+
self.curr_kana_list = gr.State(list())
|
| 63 |
+
self.curr_kana_image = gr.State(self.default_kana_image)
|
| 64 |
+
|
| 65 |
+
def init_practice_tab(self):
|
| 66 |
+
self.sketchpad = gr.Sketchpad(
|
| 67 |
+
self.default_kana_image,
|
| 68 |
+
type="numpy",
|
| 69 |
+
image_mode="RGB",
|
| 70 |
+
brush=gr.Brush(self.brush_size, self.brush_color),
|
| 71 |
+
eraser=False,
|
| 72 |
+
layers=False,
|
| 73 |
+
label="寫字板",
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
with gr.Row():
|
| 77 |
+
self.target_txt = gr.Textbox(self.default_roma, label="練習目標")
|
| 78 |
+
self.result_txt = gr.Textbox(label="辨識結果")
|
| 79 |
+
|
| 80 |
+
with gr.Row():
|
| 81 |
+
self.next_btn = gr.Button("下一個字")
|
| 82 |
+
self.recog_btn = gr.Button("手寫辨識")
|
| 83 |
+
|
| 84 |
+
def init_setting_tab(self):
|
| 85 |
+
with gr.Row():
|
| 86 |
+
self.use_assist_chk = gr.Checkbox(True, label="顯示輔助字")
|
| 87 |
+
self.use_kana_hint_chk = gr.Checkbox(True, label="提示假名")
|
| 88 |
+
|
| 89 |
+
def init_events(self):
|
| 90 |
+
recog_kwargs = gr_kwargs(self.do_recog, self.sketchpad, self.result_txt)
|
| 91 |
+
|
| 92 |
+
next_inputs = [self.use_assist_chk, self.use_kana_hint_chk, self.curr_kana_list]
|
| 93 |
+
next_outputs = [self.curr_kana, self.sketchpad, self.curr_kana_image]
|
| 94 |
+
next_outputs += [self.target_txt, self.result_txt, self.curr_kana_list]
|
| 95 |
+
next_kwargs = gr_kwargs(self.get_rand_kana, next_inputs, next_outputs)
|
| 96 |
+
|
| 97 |
+
clear_kwargs = gr_kwargs(self.clear, self.curr_kana_image, self.sketchpad)
|
| 98 |
+
|
| 99 |
+
update_inputs = [self.curr_kana, self.use_assist_chk]
|
| 100 |
+
update_inputs += [self.use_kana_hint_chk, self.curr_kana_list]
|
| 101 |
+
update_outputs = [self.curr_kana, self.sketchpad, self.curr_kana_image]
|
| 102 |
+
update_outputs += [self.target_txt, self.curr_kana_list]
|
| 103 |
+
update_kwargs = gr_kwargs(self.update, update_inputs, update_outputs)
|
| 104 |
+
|
| 105 |
+
self.recog_btn.click(**recog_kwargs)
|
| 106 |
+
self.next_btn.click(**next_kwargs)
|
| 107 |
+
self.sketchpad.clear(**clear_kwargs)
|
| 108 |
+
self.use_assist_chk.change(**update_kwargs)
|
| 109 |
+
self.use_kana_hint_chk.change(**update_kwargs)
|
| 110 |
+
|
| 111 |
+
def init_storage(self):
|
| 112 |
+
components = [self.use_assist_chk, self.use_kana_hint_chk]
|
| 113 |
+
triggers = [component.change for component in components]
|
| 114 |
+
|
| 115 |
+
default_value = [component.value for component in components]
|
| 116 |
+
browser_state = gr.BrowserState(
|
| 117 |
+
default_value,
|
| 118 |
+
storage_key="storage-key",
|
| 119 |
+
secret="secret",
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
self.app.load(inputs=browser_state, outputs=components)(lambda data: data)
|
| 123 |
+
gr.on(triggers, inputs=components, outputs=browser_state)(lambda *data: data)
|
| 124 |
+
|
| 125 |
+
def launch_app(self):
|
| 126 |
+
font = gr.themes.GoogleFont(self.font_name)
|
| 127 |
+
text_size = gr.themes.sizes.text_lg
|
| 128 |
+
theme = gr.themes.Ocean(font=font, text_size=text_size)
|
| 129 |
+
self.app.launch(theme=theme)
|
| 130 |
+
|
| 131 |
+
def conv_kana_to_roma(self, kana):
|
| 132 |
+
return self.kana_data.spell[kana][0]
|
| 133 |
+
|
| 134 |
+
def init_kana_list(self):
|
| 135 |
+
curr_kana_list = deepcopy(self.kana_images)
|
| 136 |
+
random.shuffle(curr_kana_list)
|
| 137 |
+
return curr_kana_list
|
| 138 |
+
|
| 139 |
+
def get_kana(self, kana: str, use_assist: bool, use_kana_hint: bool, kana_list: list):
|
| 140 |
+
kana_list = kana_list if kana_list else self.init_kana_list()
|
| 141 |
+
kana_image = self.kana_char_dir / f"{kana}.png" if kana else kana_list.pop()
|
| 142 |
+
kana = Path(kana_image).stem
|
| 143 |
+
|
| 144 |
+
kana_image = kana_image if use_assist else self.bg_image_path
|
| 145 |
+
|
| 146 |
+
roma = self.conv_kana_to_roma(kana)
|
| 147 |
+
roma = f"{kana} ({roma})" if use_kana_hint else f"{roma}"
|
| 148 |
+
|
| 149 |
+
return kana, str(kana_image), roma, kana_list
|
| 150 |
+
|
| 151 |
+
def parse_item(self, item):
|
| 152 |
+
prob = item["prob"]
|
| 153 |
+
char = item["char"]
|
| 154 |
+
return f"{char} ({self.conv_kana_to_roma(char)}): {prob:.2%}"
|
| 155 |
+
|
| 156 |
+
def is_valid_item(self, item):
|
| 157 |
+
if item["prob"] < 1e-2:
|
| 158 |
+
return False
|
| 159 |
+
if item["char"] not in self.kana_set:
|
| 160 |
+
return False
|
| 161 |
+
return True
|
| 162 |
+
|
| 163 |
+
def do_recog(self, image: dict[str, np.ndarray]):
|
| 164 |
+
image = image["layers"][0]
|
| 165 |
+
image[image == 0] = 255
|
| 166 |
+
image[image != 255] = 0
|
| 167 |
+
_, nbest = self.recognizer(image)
|
| 168 |
+
return ", ".join(
|
| 169 |
+
self.parse_item(item)
|
| 170 |
+
for items in nbest
|
| 171 |
+
for item in items
|
| 172 |
+
if self.is_valid_item(item)
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
def get_rand_kana(self, assist, kana, chars):
|
| 176 |
+
char, img, roma, chars = self.get_kana(None, assist, kana, chars)
|
| 177 |
+
return char, img, img, roma, None, chars
|
| 178 |
+
|
| 179 |
+
def clear(self, curr_kana_image):
|
| 180 |
+
return curr_kana_image
|
| 181 |
+
|
| 182 |
+
def update(self, kana, use_assist, use_hint, kana_list):
|
| 183 |
+
kana_info = self.get_kana(kana, use_assist, use_hint, kana_list)
|
| 184 |
+
kana, image, roma, kana_list = kana_info
|
| 185 |
+
return kana, image, image, roma, kana_list
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def gr_kwargs(fn, inputs=None, outputs=None, show_progress="hidden", **kwargs):
|
| 189 |
+
return dict(
|
| 190 |
+
fn=fn,
|
| 191 |
+
inputs=inputs,
|
| 192 |
+
outputs=outputs,
|
| 193 |
+
show_progress=show_progress,
|
| 194 |
+
**kwargs,
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
if __name__ == "__main__":
|
| 199 |
+
App().launch_app()
|
data/KiwiMaru-Regular.ttf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b7f221e14182abb1d624ffd4c91bfb63cadee13c29d7d03d2e430897d3779111
|
| 3 |
+
size 5037676
|
data/images/bg.png
ADDED
|
data/images/kana-chars//343/201/202.png
ADDED
|
data/images/kana-chars//343/201/204.png
ADDED
|
data/images/kana-chars//343/201/206.png
ADDED
|
data/images/kana-chars//343/201/210.png
ADDED
|
data/images/kana-chars//343/201/212.png
ADDED
|
data/images/kana-chars//343/201/213.png
ADDED
|
data/images/kana-chars//343/201/214.png
ADDED
|
data/images/kana-chars//343/201/215.png
ADDED
|
data/images/kana-chars//343/201/216.png
ADDED
|
data/images/kana-chars//343/201/217.png
ADDED
|
data/images/kana-chars//343/201/220.png
ADDED
|
data/images/kana-chars//343/201/221.png
ADDED
|
data/images/kana-chars//343/201/222.png
ADDED
|
data/images/kana-chars//343/201/223.png
ADDED
|
data/images/kana-chars//343/201/224.png
ADDED
|
data/images/kana-chars//343/201/225.png
ADDED
|
data/images/kana-chars//343/201/226.png
ADDED
|
data/images/kana-chars//343/201/227.png
ADDED
|
data/images/kana-chars//343/201/230.png
ADDED
|
data/images/kana-chars//343/201/231.png
ADDED
|
data/images/kana-chars//343/201/232.png
ADDED
|
data/images/kana-chars//343/201/233.png
ADDED
|
data/images/kana-chars//343/201/234.png
ADDED
|
data/images/kana-chars//343/201/235.png
ADDED
|
data/images/kana-chars//343/201/236.png
ADDED
|
data/images/kana-chars//343/201/237.png
ADDED
|
data/images/kana-chars//343/201/240.png
ADDED
|
data/images/kana-chars//343/201/241.png
ADDED
|
data/images/kana-chars//343/201/242.png
ADDED
|
data/images/kana-chars//343/201/244.png
ADDED
|
data/images/kana-chars//343/201/245.png
ADDED
|
data/images/kana-chars//343/201/246.png
ADDED
|
data/images/kana-chars//343/201/247.png
ADDED
|
data/images/kana-chars//343/201/250.png
ADDED
|
data/images/kana-chars//343/201/251.png
ADDED
|
data/images/kana-chars//343/201/252.png
ADDED
|
data/images/kana-chars//343/201/253.png
ADDED
|
data/images/kana-chars//343/201/254.png
ADDED
|
data/images/kana-chars//343/201/255.png
ADDED
|
data/images/kana-chars//343/201/256.png
ADDED
|
data/images/kana-chars//343/201/257.png
ADDED
|
data/images/kana-chars//343/201/260.png
ADDED
|
data/images/kana-chars//343/201/261.png
ADDED
|
data/images/kana-chars//343/201/262.png
ADDED
|
data/images/kana-chars//343/201/263.png
ADDED
|
data/images/kana-chars//343/201/264.png
ADDED
|