├── checkpoints └── readme.txt ├── data ├── __init__.py ├── images │ ├── __init__.py │ ├── captcha_overview.png │ └── preprocess.py ├── font │ ├── Xenotron.ttf │ ├── X-Cryption.ttf │ └── Xenophobia.ttf └── generateData.py ├── README.md └── train ├── config.py ├── train.py └── utils.py /checkpoints/readme.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- -------------------------------------------------------------------------------- /data/images/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CaptchaVariLength 2 | details refer to : http://www.jianshu.com/p/25655870b458 3 | -------------------------------------------------------------------------------- /data/font/Xenotron.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slyne/CaptchaVariLength/HEAD/data/font/Xenotron.ttf -------------------------------------------------------------------------------- /data/font/X-Cryption.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slyne/CaptchaVariLength/HEAD/data/font/X-Cryption.ttf -------------------------------------------------------------------------------- /data/font/Xenophobia.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slyne/CaptchaVariLength/HEAD/data/font/Xenophobia.ttf -------------------------------------------------------------------------------- /data/images/captcha_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slyne/CaptchaVariLength/HEAD/data/images/captcha_overview.png -------------------------------------------------------------------------------- /train/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import json 4 | 5 | image_shape = (3,60,250) 6 | max_caption_len = 8 7 | images_dir = "../data/images/pic" 8 | labels_dir = "../data/images/labels" 9 | ch_index_dir = "../data/images/ch_index" 10 | model_output = "../checkpoints/" 11 | ch_index = json.load(open(ch_index_dir)) 12 | vocab_size = len(ch_index) -------------------------------------------------------------------------------- /data/generateData.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 3 | from captcha.image import ImageCaptcha 4 | from random import sample 5 | 6 | image = ImageCaptcha() #fonts=[ "font/Xenotron.ttf"] 7 | characters = list("abcdefghijklmnopqrstuvwxyz") 8 | 9 | def generate_data(digits_num, output, total): 10 | num = 0 11 | while(num'] = index 37 | 38 | with open("ch_index", "w") as inp: 39 | json.dump(ch_index, inp) 40 | 41 | max_caption_length = 7 + 1 42 | id_labels = [] 43 | for label in labels: 44 | template = [ch_index['']] * max_caption_length 45 | for (i,c) in enumerate(label): 46 | template[i] = ch_index[c] 47 | id_labels.append(template) 48 | print "save labels" 49 | with open("labels", "wb") as inp: 50 | np.save(inp, id_labels) -------------------------------------------------------------------------------- /train/train.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from keras.callbacks import ModelCheckpoint, Callback 5 | import numpy as np 6 | from utils import load_data, create_simpleCnnRnn,create_imgText 7 | from config import image_shape,max_caption_len,vocab_size,model_output 8 | 9 | class ValidateAcc(Callback): 10 | def __init__(self, image_model, val_data, val_label, model_output): 11 | self.image_model = image_model 12 | self.val = val_data 13 | self.val_label = val_label 14 | self.model_output = model_output 15 | 16 | def on_epoch_end(self, epoch, logs={}): 17 | print '\n———————————--------' 18 | self.image_model.load_weights(self.model_output+'weights.%02d.hdf5' % epoch) 19 | r = self.image_model.predict(val, verbose=0) 20 | y_predict = np.asarray([np.argmax(i, axis=1) for i in r]) 21 | val_true = np.asarray([np.argmax(i, axis = 1) for i in self.val_label]) 22 | length = len(y_predict) * 1.0 23 | correct = 0 24 | for (true,predict) in zip(val_true,y_predict): 25 | print true,predict 26 | if list(true) == list(predict): 27 | correct += 1 28 | print "Validation set acc is: ", correct/length 29 | print '\n———————————--------' 30 | 31 | 32 | import os 33 | import glob 34 | from keras.models import load_model 35 | from keras.optimizers import SGD 36 | ''' 37 | image_model = create_simpleCnnRnn(image_shape, max_caption_len,vocab_size) 38 | sgd = SGD(lr=0.001, decay=1e-6, momentum=0.9, nesterov=True) 39 | image_model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy']) 40 | 41 | if not os.path.exists(model_output): 42 | os.makedirs(model_output) 43 | else: 44 | list_of_files = glob.glob(model_output+"/*") 45 | if len(list_of_files) != 0: 46 | latest_file = max(list_of_files, key=os.path.getctime) 47 | print("load model.....{}".format(latest_file)) 48 | image_model = load_model(latest_file) 49 | ''' 50 | 51 | image_model = create_imgText(image_shape, max_caption_len,vocab_size) 52 | 53 | split_ratio = 0.7 54 | train,train_label,val,val_label = load_data(split_ratio) 55 | 56 | val_acc_check_pointer = ValidateAcc(image_model,val,val_label,model_output) 57 | check_pointer = ModelCheckpoint(filepath=model_output + "weights.{epoch:02d}.hdf5") 58 | sgd = SGD(lr=0.0001, decay=1e-6, momentum=0.9, nesterov=True) 59 | image_model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy']) 60 | image_model.fit(train, train_label, 61 | shuffle=True, batch_size=16, nb_epoch=20, validation_split=0.2, callbacks=[check_pointer, val_acc_check_pointer]) 62 | 63 | #image_model.save("../checkpoints/model2.hdf5") 64 | -------------------------------------------------------------------------------- /train/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import numpy as np 4 | from keras.utils.np_utils import to_categorical 5 | from keras.layers import Convolution2D, MaxPooling2D, GRU, TimeDistributed 6 | from keras.layers import Dense, Dropout, Activation, Flatten, RepeatVector,Bidirectional 7 | from keras.layers.normalization import BatchNormalization 8 | from keras.models import Sequential 9 | 10 | from keras import backend as K 11 | K.set_image_dim_ordering("th") 12 | from config import * 13 | 14 | import random 15 | 16 | def load_data(split_ratio): 17 | with open(images_dir, "rb") as f: 18 | images = np.load(f) 19 | with open(labels_dir, "rb") as f: 20 | labels = np.load(f) 21 | 22 | vocab_size = len(ch_index) 23 | labels_categorical = np.asarray([to_categorical(label, vocab_size) for label in labels]) 24 | print "images shape", images.shape 25 | # print images[0] 26 | print "input labels shape", labels_categorical.shape 27 | total = images.shape[0] 28 | seed = range(total) 29 | random.shuffle(seed) 30 | split_index = int(total*split_ratio) 31 | train_data = images[seed[0:split_index]] 32 | train_label = labels_categorical[seed[0:split_index]] 33 | val_data = images[seed[split_index:]] 34 | val_label = labels_categorical[seed[split_index:]] 35 | return (train_data, train_label, val_data, val_label) 36 | 37 | 38 | def create_simpleCnnRnn(image_shape, max_caption_len,vocab_size): 39 | image_model = Sequential() 40 | # image_shape : C,W,H 41 | # input: 100x100 images with 3 channels -> (3, 100, 100) tensors. 42 | # this applies 32 convolution filters of size 3x3 each. 43 | image_model.add(Convolution2D(32, 3, 3, border_mode='valid', input_shape=image_shape)) 44 | image_model.add(BatchNormalization()) 45 | image_model.add(Activation('relu')) 46 | image_model.add(Convolution2D(32, 3, 3)) 47 | image_model.add(BatchNormalization()) 48 | image_model.add(Activation('relu')) 49 | image_model.add(MaxPooling2D(pool_size=(2, 2))) 50 | image_model.add(Dropout(0.25)) 51 | image_model.add(Convolution2D(64, 3, 3, border_mode='valid')) 52 | image_model.add(BatchNormalization()) 53 | image_model.add(Activation('relu')) 54 | image_model.add(Convolution2D(64, 3, 3)) 55 | image_model.add(BatchNormalization()) 56 | image_model.add(Activation('relu')) 57 | image_model.add(MaxPooling2D(pool_size=(2, 2))) 58 | image_model.add(Dropout(0.25)) 59 | image_model.add(Flatten()) 60 | # Note: Keras does automatic shape inference. 61 | image_model.add(Dense(128)) 62 | image_model.add(RepeatVector(max_caption_len)) 63 | image_model.add(Bidirectional(GRU(output_dim=128, return_sequences=True))) 64 | #image_model.add(GRU(output_dim=128, return_sequences=True)) 65 | image_model.add(TimeDistributed(Dense(vocab_size))) 66 | image_model.add(Activation('softmax')) 67 | return image_model 68 | 69 | 70 | from seq2seq.models import AttentionSeq2Seq, Seq2Seq 71 | def create_imgText(image_shape, max_caption_len,vocab_size): 72 | image_model = Sequential() 73 | # image_shape : C,W,H 74 | # input: 100x100 images with 3 channels -> (3, 100, 100) tensors. 75 | # this applies 32 convolution filters of size 3x3 each. 76 | image_model.add(Convolution2D(32, 3, 3, border_mode='valid', input_shape=image_shape)) 77 | image_model.add(BatchNormalization()) 78 | image_model.add(Activation('relu')) 79 | image_model.add(Convolution2D(32, 3, 3)) 80 | image_model.add(BatchNormalization()) 81 | image_model.add(Activation('relu')) 82 | image_model.add(MaxPooling2D(pool_size=(2, 2))) 83 | image_model.add(Dropout(0.25)) 84 | image_model.add(Convolution2D(64, 3, 3, border_mode='valid')) 85 | image_model.add(BatchNormalization()) 86 | image_model.add(Activation('relu')) 87 | image_model.add(Convolution2D(64, 3, 3)) 88 | image_model.add(BatchNormalization()) 89 | image_model.add(Activation('relu')) 90 | image_model.add(MaxPooling2D(pool_size=(2, 2))) 91 | image_model.add(Dropout(0.25)) 92 | image_model.add(Flatten()) 93 | # Note: Keras does automatic shape inference. 94 | image_model.add(Dense(128)) 95 | image_model.add(RepeatVector(1)) 96 | #model = AttentionSeq2Seq(input_dim=128, input_length=1, hidden_dim=128, output_length=max_caption_len, output_dim=vocab_size) 97 | model = Seq2Seq(input_dim=128, input_length=1, hidden_dim=128, output_length=max_caption_len, 98 | output_dim=128, peek=True) 99 | image_model.add(model) 100 | image_model.add(TimeDistributed(Dense(vocab_size))) 101 | image_model.add(Activation('softmax')) 102 | return image_model --------------------------------------------------------------------------------