Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
yukoba committed Sep 29, 2016
0 parents commit 6a61284
Show file tree
Hide file tree
Showing 17 changed files with 363 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.sh eol=lf
* text=auto
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.idea/workspace.xml
.idea/dictionaries/
ETL8G/
target/
tmp/
temp/
__pycache__/
Thumbs.db
*.aux
*.dvi
*.log
*.pdf
*.gz
*.dat
.~lock.*
22 changes: 22 additions & 0 deletions .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .idea/copyright/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/encodings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions CnnJapaneseCharacter.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
46 changes: 46 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Papers
I implemented Charlie Tsai "Recognizing Handwritten Japanese Characters Using Deep Convolutional Neural Networks".
http://cs231n.stanford.edu/reports2016/262_Report.pdf

This is just a VGG-like convnet.
https://arxiv.org/abs/1409.1556

# Librarys
You need following librarys.
- Anaconda (Python 3.5)
- Theano 0.8.2
- Keras 1.1.0
- scikit-learn 0.17.1 (Included in Anaconda)
- CUDA 7.5
- cuDNN 5.0

# Config files
~/.theanorc
```
[global]
floatX = float32
device = gpu
[lib]
cnmem = 1
[nvcc]
flags=-D_FORCE_INLINES
```

~/.keras/keras.json
```
{
"epsilon": 1e-07,
"image_dim_ordering": "tf",
"floatx": "float32",
"backend": "theano"
}
```

# Dataset
Please download dataset from http://etlcdb.db.aist.go.jp/?page_id=651 and extract to ETL8G folder.
Dataset contains 160 person hiragana characters.

# How to run
Just run ```python learn.py```.
Binary file added src/hiragana.npz
Binary file not shown.
89 changes: 89 additions & 0 deletions src/learn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# This code is based on
# https://github.com/fchollet/keras/blob/master/examples/mnist_cnn.py
# https://github.com/fchollet/keras/blob/master/examples/cifar10_cnn.py

import numpy as np
import scipy
from sklearn.cross_validation import train_test_split
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Convolution2D, MaxPooling2D
from keras.utils import np_utils
from keras import initializations
from keras import backend as K

nb_classes = 72
# input image dimensions
img_rows, img_cols = 64, 64
# img_rows, img_cols = 127, 128

ary = np.load("hiragana.npz")['arr_0'].reshape([-1, 127, 128]).astype(np.float32) / 15.0
X_train = np.zeros([nb_classes * 160, img_rows, img_cols], dtype=np.float32)
for i in range(nb_classes * 160):
X_train[i] = scipy.misc.imresize(ary[i], (img_rows, img_cols), mode='F')
# X_train[i] = ary[i]
Y_train = np.repeat(np.arange(nb_classes), 160)

X_train, X_test, Y_train, Y_test = train_test_split(X_train, Y_train, test_size=0.2)

if K.image_dim_ordering() == 'th':
X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols)
X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)

# convert class vectors to binary class matrices
Y_train = np_utils.to_categorical(Y_train, nb_classes)
Y_test = np_utils.to_categorical(Y_test, nb_classes)

model = Sequential()


def my_init(shape, name=None):
return initializations.normal(shape, scale=0.1, name=name)


# Best val_acc: 0.9679 (just tried only once)
# 25 minutes on Amazon EC2 g2.2xlarge
def m6_1():
model.add(Convolution2D(32, 3, 3, init=my_init, input_shape=input_shape))
model.add(Activation('relu'))
model.add(Convolution2D(32, 3, 3, init=my_init))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.5))

model.add(Convolution2D(64, 3, 3, init=my_init))
model.add(Activation('relu'))
model.add(Convolution2D(64, 3, 3, init=my_init))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.5))

model.add(Flatten())
model.add(Dense(256, init=my_init))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(nb_classes))
model.add(Activation('softmax'))


# Best val_acc: 0.8016 (just tried only once)
def classic_neural():
model.add(Flatten(input_shape=input_shape))
model.add(Dense(256))
model.add(Activation('relu'))
model.add(Dropout(0.5))

model.add(Dense(nb_classes))
model.add(Activation('softmax'))


m6_1()
# classic_neural()

model.compile(loss='categorical_crossentropy', optimizer='adagrad', metrics=['accuracy'])
model.fit(X_train, Y_train, batch_size=16, nb_epoch=40, validation_data=(X_test, Y_test))
33 changes: 33 additions & 0 deletions src/read_hiragana_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import struct
import numpy as np
from PIL import Image

sz_record = 8199


def read_record_ETL8G(f):
s = f.read(sz_record)
r = struct.unpack('>2H8sI4B4H2B30x8128s11x', s)
iF = Image.frombytes('F', (128, 127), r[14], 'bit', 4)
iL = iF.convert('L')
return r + (iL,)


def read_hiragana():
# Character type = 72, person = 160, y = 127, x = 128
ary = np.zeros([72, 160, 127, 128], dtype=np.uint8)

for j in range(1, 33):
filename = '../ETL8G/ETL8G_{:02d}'.format(j)
with open(filename, 'rb') as f:
for id_dataset in range(5):
moji = 0
for i in range(956):
r = read_record_ETL8G(f)
if b'.HIRA' in r[2]:
ary[moji, (j - 1) * 5 + id_dataset] = np.array(r[-1])
moji += 1
np.savez_compressed("hiragana.npz", ary)

read_hiragana()

16 changes: 16 additions & 0 deletions test/plt_hiragana.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import numpy as np
# import scipy
import matplotlib.pyplot as plt

nb_classes = 72
img_rows, img_cols = 64, 64

ary = np.load("../src/hiragana.npz")['arr_0'].reshape([-1, 127, 128]).astype(np.float32) / 15.0
X_train = np.zeros([nb_classes * 160, img_rows, img_cols], dtype=np.float32)
# for i in range(nb_classes * 160):
# X_train[i] = scipy.misc.imresize(ary[i], (img_rows, img_cols), mode='F')
y_train = np.repeat(np.arange(nb_classes), 160)

plt.imshow(ary[71 * 160 + 1])
print(y_train[71 * 160 + 1])
plt.show()
64 changes: 64 additions & 0 deletions test/read_file_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# This code is from http://etlcdb.db.aist.go.jp/?page_id=2461

import struct
from PIL import Image

sz_record = 8199


def read_record_ETL8G(f):
s = f.read(8199)
r = struct.unpack('>2H8sI4B4H2B30x8128s11x', s)
iF = Image.frombytes('F', (128, 127), r[14], 'bit', 4)
iL = iF.convert('L')
return r + (iL,)


def test1():
filename = '../ETL8G/ETL8G_01'
id_record = 0

with open(filename, 'rb') as f:
f.seek(id_record * sz_record)
r = read_record_ETL8G(f)

print(r[0:-2], hex(r[1]))
iE = Image.eval(r[-1], lambda x: 255 - x * 16)
fn = '../tmp/ETL8G_{:d}_{:s}.png'.format((r[0] - 1) % 20 + 1, hex(r[1])[-4:])
iE.save(fn, 'PNG')


def test2():
filename = '../ETL8G/ETL8G_01'
id_dataset = 0
new_img = Image.new('L', (128 * 32, 128 * 30))

with open(filename, 'rb') as f:
f.seek(id_dataset * 956 * sz_record)
for i in range(956):
r = read_record_ETL8G(f)
new_img.paste(r[-1], (128 * (i % 32), 128 * (i // 32)))
iE = Image.eval(new_img, lambda x: 255 - x * 16)
fn = '../tmp/ETL8G_ds{:03d}.png'.format(id_dataset)
iE.save(fn, 'PNG')


def dump_all():
for j in range(1, 33):
for id_dataset in range(5):
new_img = Image.new('L', (128 * 32, 128 * 30))

filename = '../ETL8G/ETL8G_{:02d}'.format(j)
with open(filename, 'rb') as f:
f.seek(id_dataset * 956 * sz_record)
for i in range(956):
r = read_record_ETL8G(f)
new_img.paste(r[-1], (128 * (i % 32), 128 * (i // 32)))
iE = Image.eval(new_img, lambda x: 255 - x * 16)
fn = '../tmp/ETL8G_ds{:02d}_{:01d}.png'.format(j, id_dataset)
iE.save(fn, 'PNG')


test1()
test2()
# dump_all()

0 comments on commit 6a61284

Please sign in to comment.