In [1]:
from captcha.image import ImageCaptcha
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import random

number = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
ALPHABET = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
char_set = "".join(number + alphabet + ALPHABET)
char_set = "".join(number)


# 图像大小  
height = 60  
width = 160  
captcha_size = 4
n_class = len(char_set)

In [2]:
# 随机生成长度为4的字符串
def random_captcha_text(char_set = char_set, captcha_size = captcha_size):
    captcha_text = []
    for _ in range(captcha_size):
        c = random.choice(char_set)
        captcha_text.append(c)
    captcha_text = ''.join(captcha_text)
    return captcha_text

In [3]:
# 生成图片和label
def gen_captcha_text_and_image():
    image = ImageCaptcha()
    captcha_text = random_captcha_text()
    captcha = image.generate(captcha_text)
    captcha_image = Image.open(captcha)
    captcha_image = np.array(captcha_image)
    return captcha_text, captcha_image

In [4]:
text, image = gen_captcha_text_and_image()
plt.title(text)
plt.imshow(image)
plt.show()



In [5]:
# 生成一个训练batch  
def get_next_batch(batch_size=128):  
    # 创建2个空数组, 用来存放一个批次的数据
    while True:
        batch_x = np.zeros((batch_size, height, width, 3))  
        batch_y = [np.zeros((batch_size, n_class)) for i in range(captcha_size)]

        # 有时生成图像大小不是(60, 160, 3)  
        def wrap_gen_captcha_text_and_image():  
            while True:  
                text, image = gen_captcha_text_and_image()  
                if image.shape == (60, 160, 3):  
                    return text, image  
    
        for i in range(batch_size):  
            text, image = gen_captcha_text_and_image()  
        
            batch_x[i,:] = image 
            # one-hot编码label
        
            for j, ch in enumerate(text):
                batch_y[j][i, :] = 0
                batch_y[j][i, char_set.find(ch)] = 1
    
        yield batch_x, batch_y

In [6]:
from keras.models import *
from keras.layers import *
import numpy as np
import keras

def VGG():
    input_tensor = Input((height, width, 3))
    x = input_tensor
    x = Conv2D(32, (3, 3), activation='relu', padding='SAME')(x)
    x = Conv2D(32, (3, 3), activation='relu', padding='SAME')(x)
    x = MaxPooling2D((2, 2), padding='SAME')(x)

    x = Conv2D(64, (3, 3), activation='relu', padding='SAME')(x)
    x = Conv2D(64, (3, 3), activation='relu', padding='SAME')(x)
    x = MaxPooling2D((2, 2), padding='SAME')(x)

    x = Conv2D(128, (3, 3), activation='relu', padding='SAME')(x)
    x = Conv2D(128, (3, 3), activation='relu', padding='SAME')(x)
    x = MaxPooling2D((2, 2), padding='SAME')(x)
    
    x = Conv2D(256, (3, 3), activation='relu', padding='SAME')(x)
    x = Conv2D(256, (3, 3), activation='relu', padding='SAME')(x)
    x = MaxPooling2D((2, 2), padding='SAME')(x)
    
    x = Flatten()(x)
    x = Dropout(0.3)(x)
    x = [Dense(n_class, activation='softmax', name='c%d'%(i+1))(x) for i in range(4)]
    model = Model(inputs=input_tensor, outputs=x)
    return model


model = VGG()
model.compile(loss='categorical_crossentropy', optimizer='adadelta', metrics=['accuracy'])


/home/hjl/.local/lib/python3.5/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
  from ._conv import register_converters as _register_converters
Using TensorFlow backend.

In [7]:
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot

SVG(model_to_dot(model).create(prog='dot', format='svg'))


Out[7]:
G 140709437269776 input_1: InputLayer 140709437270952 conv2d_1: Conv2D 140709437269776->140709437270952 140709437800288 conv2d_2: Conv2D 140709437270952->140709437800288 140707797559280 max_pooling2d_1: MaxPooling2D 140709437800288->140707797559280 140707797204384 conv2d_3: Conv2D 140707797559280->140707797204384 140707797298088 conv2d_4: Conv2D 140707797204384->140707797298088 140707797368280 max_pooling2d_2: MaxPooling2D 140707797298088->140707797368280 140707796980792 conv2d_5: Conv2D 140707797368280->140707796980792 140707796983032 conv2d_6: Conv2D 140707796980792->140707796983032 140707797147040 max_pooling2d_3: MaxPooling2D 140707796983032->140707797147040 140707796695976 conv2d_7: Conv2D 140707797147040->140707796695976 140707796764656 conv2d_8: Conv2D 140707796695976->140707796764656 140707796823456 max_pooling2d_4: MaxPooling2D 140707796764656->140707796823456 140707796452016 flatten_1: Flatten 140707796823456->140707796452016 140707796452744 dropout_1: Dropout 140707796452016->140707796452744 140707796536400 c1: Dense 140707796452744->140707796536400 140707796168656 c2: Dense 140707796452744->140707796168656 140707796368576 c3: Dense 140707796452744->140707796368576 140707795727696 c4: Dense 140707796452744->140707795727696

In [9]:
model.fit_generator(generator = get_next_batch(), 
                    steps_per_epoch = 100,  
                    epochs=10, 
                    verbose=1,  
                    validation_data = get_next_batch(), 
                    validation_steps = 10)


Epoch 1/10
100/100 [==============================] - 37s 373ms/step - loss: 8.4056 - c1_loss: 1.8094 - c2_loss: 2.1967 - c3_loss: 2.2223 - c4_loss: 2.1772 - c1_acc: 0.3389 - c2_acc: 0.2009 - c3_acc: 0.1907 - c4_acc: 0.2080 - val_loss: 6.3267 - val_c1_loss: 0.9661 - val_c2_loss: 1.6737 - val_c3_loss: 1.9038 - val_c4_loss: 1.7831 - val_c1_acc: 0.6141 - val_c2_acc: 0.4008 - val_c3_acc: 0.3203 - val_c4_acc: 0.4188
Epoch 2/10
100/100 [==============================] - 34s 341ms/step - loss: 3.1517 - c1_loss: 0.4232 - c2_loss: 0.8813 - c3_loss: 1.0607 - c4_loss: 0.7866 - c1_acc: 0.8580 - c2_acc: 0.7142 - c3_acc: 0.6537 - c4_acc: 0.7463 - val_loss: 0.6896 - val_c1_loss: 0.0566 - val_c2_loss: 0.1680 - val_c3_loss: 0.3041 - val_c4_loss: 0.1608 - val_c1_acc: 0.9844 - val_c2_acc: 0.9586 - val_c3_acc: 0.9109 - val_c4_acc: 0.9516
Epoch 3/10
100/100 [==============================] - 35s 345ms/step - loss: 0.5337 - c1_loss: 0.0490 - c2_loss: 0.1250 - c3_loss: 0.2319 - c4_loss: 0.1278 - c1_acc: 0.9856 - c2_acc: 0.9607 - c3_acc: 0.9323 - c4_acc: 0.9600 - val_loss: 0.1191 - val_c1_loss: 0.0101 - val_c2_loss: 0.0275 - val_c3_loss: 0.0531 - val_c4_loss: 0.0285 - val_c1_acc: 0.9977 - val_c2_acc: 0.9930 - val_c3_acc: 0.9828 - val_c4_acc: 0.9953
Epoch 4/10
100/100 [==============================] - 35s 345ms/step - loss: 0.1867 - c1_loss: 0.0143 - c2_loss: 0.0337 - c3_loss: 0.0936 - c4_loss: 0.0451 - c1_acc: 0.9955 - c2_acc: 0.9895 - c3_acc: 0.9729 - c4_acc: 0.9863 - val_loss: 0.3333 - val_c1_loss: 0.0516 - val_c2_loss: 0.0652 - val_c3_loss: 0.1258 - val_c4_loss: 0.0906 - val_c1_acc: 0.9852 - val_c2_acc: 0.9828 - val_c3_acc: 0.9555 - val_c4_acc: 0.9727
Epoch 5/10
100/100 [==============================] - 34s 343ms/step - loss: 0.1688 - c1_loss: 0.0129 - c2_loss: 0.0335 - c3_loss: 0.0783 - c4_loss: 0.0442 - c1_acc: 0.9958 - c2_acc: 0.9912 - c3_acc: 0.9769 - c4_acc: 0.9862 - val_loss: 0.0785 - val_c1_loss: 0.0026 - val_c2_loss: 0.0114 - val_c3_loss: 0.0506 - val_c4_loss: 0.0139 - val_c1_acc: 0.9992 - val_c2_acc: 0.9961 - val_c3_acc: 0.9906 - val_c4_acc: 0.9969
Epoch 6/10
100/100 [==============================] - 34s 344ms/step - loss: 0.0814 - c1_loss: 0.0052 - c2_loss: 0.0148 - c3_loss: 0.0426 - c4_loss: 0.0188 - c1_acc: 0.9984 - c2_acc: 0.9959 - c3_acc: 0.9884 - c4_acc: 0.9945 - val_loss: 0.0551 - val_c1_loss: 0.0056 - val_c2_loss: 0.0088 - val_c3_loss: 0.0329 - val_c4_loss: 0.0078 - val_c1_acc: 0.9984 - val_c2_acc: 0.9969 - val_c3_acc: 0.9930 - val_c4_acc: 0.9977
Epoch 7/10
100/100 [==============================] - 34s 343ms/step - loss: 0.3698 - c1_loss: 0.0575 - c2_loss: 0.0896 - c3_loss: 0.1188 - c4_loss: 0.1038 - c1_acc: 0.9809 - c2_acc: 0.9737 - c3_acc: 0.9630 - c4_acc: 0.9688 - val_loss: 0.0178 - val_c1_loss: 3.0960e-04 - val_c2_loss: 0.0018 - val_c3_loss: 0.0063 - val_c4_loss: 0.0093 - val_c1_acc: 1.0000 - val_c2_acc: 1.0000 - val_c3_acc: 0.9992 - val_c4_acc: 0.9969
Epoch 8/10
100/100 [==============================] - 34s 343ms/step - loss: 0.0625 - c1_loss: 0.0060 - c2_loss: 0.0113 - c3_loss: 0.0272 - c4_loss: 0.0180 - c1_acc: 0.9984 - c2_acc: 0.9966 - c3_acc: 0.9923 - c4_acc: 0.9945 - val_loss: 0.0257 - val_c1_loss: 0.0017 - val_c2_loss: 0.0066 - val_c3_loss: 0.0126 - val_c4_loss: 0.0048 - val_c1_acc: 1.0000 - val_c2_acc: 0.9977 - val_c3_acc: 0.9977 - val_c4_acc: 1.0000
Epoch 9/10
100/100 [==============================] - 34s 342ms/step - loss: 0.0859 - c1_loss: 0.0079 - c2_loss: 0.0155 - c3_loss: 0.0396 - c4_loss: 0.0230 - c1_acc: 0.9980 - c2_acc: 0.9959 - c3_acc: 0.9891 - c4_acc: 0.9932 - val_loss: 0.0083 - val_c1_loss: 0.0011 - val_c2_loss: 0.0024 - val_c3_loss: 0.0024 - val_c4_loss: 0.0024 - val_c1_acc: 0.9992 - val_c2_acc: 1.0000 - val_c3_acc: 0.9992 - val_c4_acc: 0.9992
Epoch 10/10
100/100 [==============================] - 34s 342ms/step - loss: 0.0514 - c1_loss: 0.0036 - c2_loss: 0.0091 - c3_loss: 0.0260 - c4_loss: 0.0127 - c1_acc: 0.9991 - c2_acc: 0.9975 - c3_acc: 0.9921 - c4_acc: 0.9963 - val_loss: 0.0338 - val_c1_loss: 0.0034 - val_c2_loss: 0.0051 - val_c3_loss: 0.0172 - val_c4_loss: 0.0081 - val_c1_acc: 0.9984 - val_c2_acc: 0.9984 - val_c3_acc: 0.9977 - val_c4_acc: 0.9969
Out[9]:
<keras.callbacks.History at 0x7ff90c0b2780>

In [ ]: