In [1]:
%%bash
yes|pip2 uninstall captcha
yes|pip2 uninstall Pillow
pip2 install -U Pillow
pip2 install captcha
In [2]:
from io import BytesIO
from captcha.image import ImageCaptcha,WheezyCaptcha
from IPython.display import Image
In [3]:
captcha = ImageCaptcha()
data = captcha.generate('1234')
captcha.write('1234', 'out.png')
Image("out.png")
Out[3]:
In [4]:
wheezy_captcha = WheezyCaptcha()
captcha.generate('567890')
captcha.write('567890', 'out.png')
Image("out.png")
Out[4]:
In [5]:
%%bash
cat /etc/fonts/fonts.conf
In [6]:
%%bash
ls /usr/share/fonts/truetype/*
In [7]:
import glob
In [8]:
fonts = glob.glob('/usr/share/fonts/truetype/dejavu/*.ttf')
In [9]:
captcha = ImageCaptcha(fonts=fonts)
data = captcha.generate('1234')
captcha.write('1234', 'out.png')
Image("out.png")
Out[9]:
In [10]:
wheezy_captcha = WheezyCaptcha(fonts=fonts)
captcha.generate_image('567890')
Out[10]:
In [11]:
captcha = ImageCaptcha(fonts=fonts)
captcha.generate_image('a34c')
Out[11]:
In [19]:
import numpy as np
import PIL
In [20]:
img = captcha.generate_image('9e3o')
arr = np.asarray(img, dtype="float32")/255.0
In [21]:
data = np.empty((1, 3, 60, 160), dtype="float32")
data[0, :, :, :] = np.rollaxis(arr, 2)
In [22]:
help(img.resize)
In [23]:
img.resize((200, 100))
Out[23]:
In [24]:
img.resize((200, 100), PIL.Image.LANCZOS)
Out[24]:
The following code is adapted from https://github.com/skyduy/CNN_keras/blob/master/core/train_with_acc_2.py
In [25]:
from numpy import argmax, array
from sklearn.cross_validation import train_test_split
from keras.callbacks import Callback, ModelCheckpoint
from keras.models import Graph
from keras.utils import np_utils
from keras.layers.core import Dense, Flatten, Dropout
from keras.layers.convolutional import Convolution2D, MaxPooling2D
In [49]:
import math
import random
import IPython.display as display
In [195]:
SAMPLE_SIZE = 1000
SHOW_SAMPLE_SIZE = 5
INVALID_DIGIT = -1
DIGIT_COUNT = 4
DIGIT_FORMAT_STR = "%%0%dd" % DIGIT_COUNT
# print DIGIT_FORMAT_STR
labels = []
images = []
for i in range(0, SAMPLE_SIZE):
digits = 0
last_digit = INVALID_DIGIT
for j in range(0, DIGIT_COUNT):
digit = last_digit
while digit == last_digit:
digit = random.randint(0, 9)
last_digit = digit
digits = digits * 10 + digit
digits_as_str = DIGIT_FORMAT_STR % digits
labels.append(digits_as_str)
images.append(captcha.generate_image(digits_as_str))
for index in range(SHOW_SAMPLE_SIZE):
display.display(labels[index])
display.display(images[index])
In [196]:
# standard width for the whole captcha image
IMAGE_STD_WIDTH = 200
# standard height for the whole captcha image
IMAGE_STD_HEIGHT = 80
# when spliting an image into digits, how much wider do we want, as an rate
EXTRA_RATE = 0.15
# how much wider do we want, as a width
EXTRA_WIDTH = int(math.floor(IMAGE_STD_WIDTH * EXTRA_RATE))
# the standard digit image width
DIGIT_IMAGE_STD_WIDTH_WITH_EXTRA = IMAGE_STD_WIDTH / DIGIT_COUNT + EXTRA_WIDTH
digit_labels = np.empty(SAMPLE_SIZE * DIGIT_COUNT)
digit_image_data = np.empty((SAMPLE_SIZE * DIGIT_COUNT, 3, IMAGE_STD_HEIGHT, DIGIT_IMAGE_STD_WIDTH_WITH_EXTRA), dtype="float32")
for index in range(0, SHOW_SAMPLE_SIZE):
img = images[index].resize((IMAGE_STD_WIDTH, IMAGE_STD_HEIGHT), PIL.Image.LANCZOS)
display.display(img)
for digit_index in range(0, DIGIT_COUNT):
# (left, upper, right, lower)
left = max(0, IMAGE_STD_WIDTH * (digit_index + 0.0) / DIGIT_COUNT - EXTRA_WIDTH)
right = min(IMAGE_STD_WIDTH, IMAGE_STD_WIDTH * (digit_index + 1.0) / DIGIT_COUNT + EXTRA_WIDTH)
crop_box = (left, 0, right, IMAGE_STD_HEIGHT)
processed_img = img.crop(crop_box)
processed_img = processed_img.resize((DIGIT_IMAGE_STD_WIDTH_WITH_EXTRA, IMAGE_STD_HEIGHT), PIL.Image.LANCZOS)
display.display(processed_img)
img_arr = np.asarray(processed_img, dtype="float32") / 255.0
digit_image_data[index * DIGIT_COUNT + digit_index, :, :, :] = np.rollaxis(img_arr, 2)
Spliting images is dead end. Try not to.
In [197]:
# standard width for the whole captcha image
IMAGE_STD_WIDTH = 200
# standard height for the whole captcha image
IMAGE_STD_HEIGHT = 200
digit_labels = list()
for digit_index in range(0, DIGIT_COUNT):
digit_labels.append(np.empty(SAMPLE_SIZE, dtype="int8"))
digit_image_data = np.empty((SAMPLE_SIZE, 3, IMAGE_STD_HEIGHT, IMAGE_STD_WIDTH), dtype="float32")
for index in range(0, SAMPLE_SIZE):
img = images[index].resize((IMAGE_STD_WIDTH, IMAGE_STD_HEIGHT), PIL.Image.LANCZOS)
if index < SHOW_SAMPLE_SIZE:
display.display(img)
img_arr = np.asarray(img, dtype="float32") / 255.0
digit_image_data[index, :, :, :] = np.rollaxis(img_arr, 2)
for digit_index in range(0, DIGIT_COUNT):
digit_labels[digit_index][index] = labels[index][digit_index]
In [199]:
digit_labels
Out[199]:
In [200]:
digit_labels[0][0]
Out[200]:
In [201]:
digit_image_data.size == SAMPLE_SIZE * 3 * IMAGE_STD_HEIGHT * IMAGE_STD_WIDTH
Out[201]:
In [202]:
digit_image_data[0].shape
Out[202]:
In [203]:
# goal is (80,200,3)
np.rollaxis(digit_image_data[0], 0, 3).shape
Out[203]:
In [204]:
img = captcha.generate_image('1234')
data = np.asarray(img, dtype="float32") / 255.0
PIL.Image.fromarray(np.uint8(data * 255.0), 'RGB')
Out[204]:
In [205]:
display.display(PIL.Image.fromarray(np.uint8(np.rollaxis(digit_image_data[0], 0, 3) * 255.0), 'RGB'))
In [206]:
X, Y_all = digit_image_data, digit_labels[0]
X_train, X_test, y_train_num, y_test = train_test_split(X, Y_all, test_size=0.1, random_state=0)
In [207]:
print y_train_num
In [208]:
for img in X_train[0:SHOW_SAMPLE_SIZE]:
display.display(PIL.Image.fromarray(np.uint8(np.rollaxis(img, 0, 3) * 255.0), 'RGB'))
In [209]:
CLASS_COUNT = 10
y_train = np_utils.to_categorical(y_train_num, CLASS_COUNT)
y_train
Out[209]:
In [210]:
RGB_COLOR_COUNT = 3
POOL_SIZE = (2, 2)
CONV1_NB_FILTERS = IMAGE_STD_HEIGHT / 2 + 2
CONV2_NB_FILTERS = IMAGE_STD_HEIGHT + 2 * 2
graph = Graph()
# graph.add_input(name='input', input_shape=(3, 40, 40))
graph.add_input(name='input', input_shape=(RGB_COLOR_COUNT, IMAGE_STD_HEIGHT, IMAGE_STD_WIDTH))
# http://stackoverflow.com/questions/36243536/what-is-the-number-of-filter-in-cnn/36243662
graph.add_node(Convolution2D(22, 5, 5, activation='relu'), name='conv1', input='input')
graph.add_node(MaxPooling2D(pool_size=POOL_SIZE), name='pool1', input='conv1')
graph.add_node(Convolution2D(44, 3, 3, activation='relu'), name='conv2', input='pool1')
graph.add_node(MaxPooling2D(pool_size=POOL_SIZE), name='pool2', input='conv2')
graph.add_node(Dropout(0.25), name='drop', input='pool2')
graph.add_node(Flatten(), name='flatten', input='drop')
graph.add_node(Dense(256, activation='relu'), name='ip', input='flatten')
graph.add_node(Dropout(0.5), name='drop_out', input='ip')
graph.add_node(Dense(CLASS_COUNT, activation='softmax'), name='result', input='drop_out')
graph.add_output(name='out', input='result')
In [211]:
graph.compile(
optimizer='adadelta',
loss={
'out': 'categorical_crossentropy',
}
)
In [212]:
class ValidateAcc(Callback):
def on_epoch_end(self, epoch, logs={}):
print '\n————————————————————————————————————'
graph.load_weights('tmp/weights.%02d.hdf5' % epoch)
r = graph.predict({'input': X_test}, verbose=0)
y_predict = array([argmax(i) for i in r['out']])
length = len(y_predict) * 1.0
acc = sum(y_predict == y_test) / length
print 'Single picture test accuracy: %2.2f%%' % (acc * 100)
print 'Theoretical accuracy: %2.2f%% ~ %2.2f%%' % ((5*acc-4)*100, pow(acc, 5)*100)
print '————————————————————————————————————'
In [213]:
%%bash
rm -rf tmp
rm -rf model
mkdir tmp
mkdir model
In [214]:
check_point = ModelCheckpoint(filepath="tmp/weights.{epoch:02d}.hdf5")
back = ValidateAcc()
print 'Begin train on %d samples... test on %d samples...' % (len(y_train), len(y_test))
graph.fit(
{'input': X_train, 'out': y_train},
batch_size=128, nb_epoch=3, callbacks=[check_point, back]
)
print '... saving'
graph.save_weights('model/model_2.hdf5')
In [217]:
check_point = ModelCheckpoint(filepath="tmp/weights.{epoch:02d}.hdf5")
back = ValidateAcc()
print 'Begin train on %d samples... test on %d samples...' % (len(y_train), len(y_test))
graph.load_weights('model/model_2.hdf5')
graph.fit(
{'input': X_train, 'out': y_train},
batch_size=128, nb_epoch=5, callbacks=[check_point, back]
)
print '... saving'
graph.save_weights('model/model_2.hdf5', overwrite=True)
In [219]:
check_point = ModelCheckpoint(filepath="tmp/weights.{epoch:02d}.hdf5")
back = ValidateAcc()
print 'Begin train on %d samples... test on %d samples...' % (len(y_train), len(y_test))
graph.load_weights('model/model_2.hdf5')
graph.fit(
{'input': X_train, 'out': y_train},
batch_size=128, nb_epoch=10, callbacks=[check_point, back]
)
print '... saving'
graph.save_weights('model/model_2.hdf5', overwrite=True)
In [ ]: