Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
app.py
ADDED
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
from PIL import Image
|
3 |
+
import streamlit as st
|
4 |
+
from streamlit_drawable_canvas import st_canvas
|
5 |
+
import numpy as np
|
6 |
+
from keras.models import load_model
|
7 |
+
from keras.preprocessing import image
|
8 |
+
import keras.utils as image
|
9 |
+
import cv2
|
10 |
+
from data import Getdetail
|
11 |
+
|
12 |
+
st.title('JLPTN5 handwriting Kanji recognition.')
|
13 |
+
|
14 |
+
labels = ['一', '七', '万', '三', '上', '下', '中', '九', '二', '五', '人', '今', '休', '何', '先', '入', '八', '六', '円', '出', '分', '前', '北', '十', '千', '午', '半', '南', '友', '右', '名', '四', '国', '土', '外', '大', '天', '女', '子', '学', '小', '山', '川', '左', '年', '後', '日', '明', '時', '書', '月', '木', '本', '来', '東', '校', '母', '毎', '気', '水', '火', '父', '生', '男', '白', '百', '聞', '行', '西', '見', '話', '語', '読', '車', '金', '長', '間', '雨', '電', '食', '高']
|
15 |
+
model = load_model("./Lenet5_Dropout2.h5")
|
16 |
+
|
17 |
+
st.sidebar.title("NihonGO !")
|
18 |
+
st.sidebar.caption("Handwriting kanji recognition system.")
|
19 |
+
options = ("Sketchpad input", "Image input", "about JLPTN5", "about NihonGO !")
|
20 |
+
choice = st.sidebar.selectbox("Select input options",options)
|
21 |
+
st.sidebar.caption("Dev by : Tanaanan Chalermpan")
|
22 |
+
st.sidebar.caption("Computer Science Kasetsart U. Thailand")
|
23 |
+
|
24 |
+
|
25 |
+
|
26 |
+
def predict_img_sketch(img_path):
|
27 |
+
st.write("Predicted result.")
|
28 |
+
if img_path is not None:
|
29 |
+
img = Image.fromarray(img_path) # use pillow to load a image
|
30 |
+
img = img.convert('L')
|
31 |
+
img = np.array(img) # convert img to an array
|
32 |
+
ret, thresh1 = cv2.threshold(img, 110, 255, cv2.THRESH_BINARY) # Apply binary threshold
|
33 |
+
thresh1 = cv2.cvtColor(thresh1, cv2.COLOR_GRAY2RGB) # Convert back to RGB
|
34 |
+
img_array = cv2.resize(thresh1, (120, 120)) # Resize the image
|
35 |
+
img_array = np.expand_dims(img_array, axis=0)
|
36 |
+
preds = model.predict(img_array)
|
37 |
+
return preds
|
38 |
+
|
39 |
+
def image_display(img_path):
|
40 |
+
st.write("Predicted image.")
|
41 |
+
if img_path is not None:
|
42 |
+
img = Image.open(img_path)
|
43 |
+
img = img.resize((120,120)).convert('L')
|
44 |
+
st.image(img, caption='Uploaded Image', use_column_width=True)
|
45 |
+
|
46 |
+
def predict_img_input(img_path):
|
47 |
+
st.write("Predicted result.")
|
48 |
+
if img_path is not None:
|
49 |
+
img = Image.open(img_path) # use pillow to load a image
|
50 |
+
img = img.convert('L')
|
51 |
+
img = np.array(img) # convert img to an array
|
52 |
+
ret, thresh1 = cv2.threshold(img, 110, 255, cv2.THRESH_BINARY) # Apply binary threshold
|
53 |
+
thresh1 = cv2.cvtColor(thresh1, cv2.COLOR_GRAY2RGB) # Convert back to RGB
|
54 |
+
img_array = cv2.resize(thresh1, (120, 120)) # Resize the image
|
55 |
+
img_array = np.expand_dims(img_array, axis=0)
|
56 |
+
preds = model.predict(img_array)
|
57 |
+
return preds
|
58 |
+
|
59 |
+
if choice == "Sketchpad input":
|
60 |
+
|
61 |
+
|
62 |
+
col1,col2 = st.columns(2)
|
63 |
+
with col2:
|
64 |
+
stroke_width = st.slider("Stroke width: ", 8, 25, 8)
|
65 |
+
|
66 |
+
with col1:
|
67 |
+
# ref : https://github.com/andfanilo/streamlit-drawable-canvas
|
68 |
+
canvas_result = st_canvas(
|
69 |
+
fill_color="rgba(255, 165, 0, 0.3)", # Fixed fill color with some opacity
|
70 |
+
stroke_width=stroke_width,
|
71 |
+
stroke_color="#000000", # stroke to black
|
72 |
+
background_color="#FFFFFF", # background to write
|
73 |
+
background_image= None,
|
74 |
+
update_streamlit=False,
|
75 |
+
height=300,
|
76 |
+
width=300,
|
77 |
+
drawing_mode="freedraw",
|
78 |
+
point_display_radius=0,
|
79 |
+
key="canvas",
|
80 |
+
)
|
81 |
+
# Do something interesting with the image data and paths
|
82 |
+
if canvas_result.image_data is not None:
|
83 |
+
st.image(canvas_result.image_data, caption='display_show')
|
84 |
+
|
85 |
+
with col2:
|
86 |
+
# check if sketchinput is empty from len json_data
|
87 |
+
is_empty = 0
|
88 |
+
if canvas_result.image_data is not None: # beware of slow loading
|
89 |
+
is_empty = canvas_result.json_data["objects"] # need to convert obj to str because PyArrow
|
90 |
+
|
91 |
+
if is_empty:
|
92 |
+
# display prediction
|
93 |
+
pred_raw = predict_img_sketch(canvas_result.image_data)
|
94 |
+
if pred_raw is not None:
|
95 |
+
pred_class, accuracy = labels[np.argmax(pred_raw)], np.max(pred_raw)*100
|
96 |
+
st.write(f"Class : {labels[np.argmax(pred_raw)]}")
|
97 |
+
st.write(f"with accuracy : {accuracy:.2f} %")
|
98 |
+
|
99 |
+
st.divider()
|
100 |
+
strokes = Getdetail()[pred_class]['Strokes']
|
101 |
+
meaning = Getdetail()[pred_class]['Meaning']
|
102 |
+
onyoumi = Getdetail()[pred_class]['Onyoumi']
|
103 |
+
kunyomi = Getdetail()[pred_class]['Kunyoumi']
|
104 |
+
link_ref = Getdetail()[pred_class]['Reference']
|
105 |
+
|
106 |
+
st.write(f"Strokes : {strokes}")
|
107 |
+
st.write(f"Meaning : {meaning.capitalize()}")
|
108 |
+
st.write(f"Onyoumi : {onyoumi}")
|
109 |
+
st.write(f"Kunyoumi : {kunyomi}")
|
110 |
+
st.write(f"for more detail : [Shirabe jisho]({link_ref})")
|
111 |
+
|
112 |
+
elif choice == "Image input":
|
113 |
+
upload_img = st.file_uploader("Choose a kanji image", type=['.jpg', '.png', '.jpeg'])
|
114 |
+
col1,col2 = st.columns(2)
|
115 |
+
|
116 |
+
with col1:
|
117 |
+
image_display(upload_img)
|
118 |
+
|
119 |
+
with col2:
|
120 |
+
# display prediction
|
121 |
+
pred_raw = predict_img_input(upload_img)
|
122 |
+
if pred_raw is not None:
|
123 |
+
pred_class, accuracy = labels[np.argmax(pred_raw)], np.max(pred_raw)*100
|
124 |
+
st.write(f"Class : {labels[np.argmax(pred_raw)]}")
|
125 |
+
st.write(f"with accuracy : {accuracy:.2f} %")
|
126 |
+
|
127 |
+
st.write('---------------------------------------')
|
128 |
+
strokes = Getdetail()[pred_class]['Strokes']
|
129 |
+
meaning = Getdetail()[pred_class]['Meaning']
|
130 |
+
onyoumi = Getdetail()[pred_class]['Onyoumi']
|
131 |
+
kunyomi = Getdetail()[pred_class]['Kunyoumi']
|
132 |
+
link_ref = Getdetail()[pred_class]['Reference']
|
133 |
+
|
134 |
+
st.write(f"Strokes : {strokes}")
|
135 |
+
st.write(f"Meaning : {meaning.capitalize()}")
|
136 |
+
st.write(f"Onyoumi : {onyoumi}")
|
137 |
+
st.write(f"Kunyoumi : {kunyomi}")
|
138 |
+
st.write(f"for more detail : [Shirabe jisho]({link_ref})")
|
139 |
+
|
140 |
+
elif choice == "about JLPTN5":
|
141 |
+
df = pd.read_csv('./passjapanesetest.com_N5_Kanji.csv', encoding='cp932')
|
142 |
+
st.subheader("JLPT คืออะไร")
|
143 |
+
detail_1 = '''ย่อมาจาก Japanese Language Proficiency Test การสอบวัดระดับภาษาญี่ปุ่น คือ การสอบที่จัดขึ้นเพื่อวัดความสามารถทางภาษาญี่ปุ่น ของชาวต่างชาติผู้เรียนภาษาญี่ปุ่น
|
144 |
+
|
145 |
+
การสอบวัดระดับภาษาญี่ปุ่นครั้งแรก จัดขึ้นในปี ค.ศ. 1984 มีผู้สมัครสอบ 7,998 คน และมีผู้สมัครสอบเพิ่มขึ้นมากทุกปี สถิติในปี ค.ศ. 2019 มีผู้สมัครสอบถึง 1,362,167 คนทั่วโลก'''
|
146 |
+
st.markdown(detail_1)
|
147 |
+
st.markdown('''- โดยระดับ N5 ถือเป็นระดับที่ง่ายที่สุดในการสอบ แต่พาร์ทที่หินที่สุดส่วนหนึ่งก็คือ พาร์ท คันจิ N5 ***ผู้พัฒนาจึงสร้างระบบที่วิเคราะห์ตัวอักษรคันจิได้จากลายมือเขียน
|
148 |
+
เพื่อที่จะเป็นตัวช่วยหนึ่งในการให้ผู้ใช้งานได้ใช้ในการฝึกฝนได้***''')
|
149 |
+
|
150 |
+
st.caption("List of JLPTN5 Kanji")
|
151 |
+
st.write(df)
|
152 |
+
|
153 |
+
st.caption('ref : [jeducation](https://jeducation.com/main/education/jlpt/) , [passjapanesetest](http://www.passjapanesetest.com/jlpt-n5-kanji-list/)')
|
154 |
+
|
155 |
+
elif choice == "about NihonGO !":
|
156 |
+
st.subheader("NihonGO ! คืออะไร")
|
157 |
+
detail_2 = """NihonGO ! : เป็นระบบที่ใช้ในการวิเคราะห์ตัวอักษรคันจิจากลายมือเขียนโดยอัติโนมัติ โดยใช้หลักการของ Deep learning (Convolutional Neural Network) ในการแยกแยะตัวอักษรคันจิ
|
158 |
+
เพื่อใช้เป็นตัวช่วยในการฝึกฝนการเขียนคันจิ และเป็นตัวช่วยในการฝึกฝนในการเตรียมสอบวัดระดับ JLPT"""
|
159 |
+
st.markdown(detail_2)
|
160 |
+
st.caption("Development.")
|
161 |
+
st.markdown("- JLPTN5 : ระบบสามารถแยกแยะคันจิจำนวน 80 ตัวพื้นฐานตามเนื้อหาคันจิจากการสอบวัดระดับ JLPTN5")
|
162 |
+
st.image('total_kanji.png', caption='JLPTN5 Kanji detail')
|
163 |
+
st.subheader("Feature")
|
164 |
+
st.markdown(""" 1. บอกคันจิที่ระบบตรวจจับได้พร้อมกับค่าความเชื่อมั่น (accuracy score.)
|
165 |
+
|
166 |
+
2. จำนวนเส้น stroke ที่ใช้ในการเขียนคันจิ
|
167 |
+
|
168 |
+
3. เสียงอ่านแบบ Onyoumi กับ Kunyoumi พร้อมกับตัวอักษร Romanji
|
169 |
+
|
170 |
+
4. Link เนื้อหาเพิ่มเติมโดยระเอียดกับ website Shirabe jisho""")
|
171 |
+
st.subheader("Guideline")
|
172 |
+
st.markdown(""" - เพื่อความแม่นยำของระบบ ตอนนำข้อมูลรูปภาพเข้า หรือเขียนในระบบแนะนำให้ผู้ใช้งานเขียนตัวคันจิให้อยู่ตรงกลาง และมีขนาดที่ใหญ่พอดีกับขนาดของกรอบ""")
|
173 |
+
st.caption("ref : [kanshudo](https://www.kanshudo.com/collections/jlpt_kanji)")
|
174 |
+
|
175 |
+
|
176 |
+
|