在本项目中,将会用Keras来搭建一个稍微复杂的CNN模型来破解以上的验证码。验证码如下:
利用Keras可以快速方便地搭建CNN模型,本项目搭建的CNN模型如下:
将数据集分为训练集和测试集,占比为8:2,该模型训练的代码如下:
# -*- coding: utf-8 -*- import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from matplotlib import pyplot as plt from keras.utils import np_utils, plot_model from keras.models import Sequential from keras.layers.core import Dense, Dropout, Activation, Flatten from keras.callbacks import EarlyStopping from keras.layers import Conv2D, MaxPooling2D # 读取数据 df = pd.read_csv('./data.csv') # 标签值 vals = range(31) keys = ['1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','J','K','L','N','P','Q','R','S','T','U','V','X','Y','Z'] label_dict = dict(zip(keys, vals)) x_data = df[['v'+str(i+1) for i in range(320)]] y_data = pd.DataFrame({'label':df['label']}) y_data['class'] = y_data['label'].apply(lambda x: label_dict[x]) # 将数据分为训练集和测试集 X_train, X_test, Y_train, Y_test = train_test_split(x_data, y_data['class'], test_size=0.3, random_state=42) x_train = np.array(X_train).reshape((1167, 20, 16, 1)) x_test = np.array(X_test).reshape((501, 20, 16, 1)) # 对标签值进行one-hot encoding n_classes = 31 y_train = np_utils.to_categorical(Y_train, n_classes) y_val = np_utils.to_categorical(Y_test, n_classes) input_shape = x_train[0].shape # CNN模型 model = Sequential() # 卷积层和池化层 model.add(Conv2D(32, kernel_size=(3, 3), input_shape=input_shape, padding='same')) model.add(Activation('relu')) model.add(Conv2D(32, kernel_size=(3, 3), padding='same')) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2), padding='same')) # Dropout层 model.add(Dropout(0.25)) model.add(Conv2D(64, kernel_size=(3, 3), padding='same')) model.add(Activation('relu')) model.add(Conv2D(64, kernel_size=(3, 3), padding='same')) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2), padding='same')) model.add(Dropout(0.25)) model.add(Conv2D(128, kernel_size=(3, 3), padding='same')) model.add(Activation('relu')) model.add(Conv2D(128, kernel_size=(3, 3), padding='same')) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2), padding='same')) model.add(Dropout(0.25)) model.add(Flatten()) # 全连接层 model.add(Dense(256, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(128, activation='relu')) model.add(Dense(n_classes, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) # plot model ##plot_model(model, to_file=r'./model.png', show_shapes=True) # 模型训练 callbacks = [EarlyStopping(monitor='val_acc', patience=5, verbose=1)] batch_size = 64 n_epochs = 100 history = model.fit(x_train, y_train, batch_size=batch_size, epochs=n_epochs, verbose=1, validation_data=(x_test, y_val), callbacks=callbacks) mp = './verifycode_Keras.h5' model.save(mp) # 绘制验证集上的准确率曲线 val_acc = history.history['val_acc'] plt.plot(range(len(val_acc)), val_acc, label='CNN model') plt.title('Validation accuracy on verifycode dataset') plt.xlabel('epochs') plt.ylabel('accuracy') plt.legend() plt.show()
在上述代码中,训练模型的时候采用了early stopping技巧。early stopping是用于提前停止训练的callbacks。具体地,可以达到当训练集上的loss不在减小(即减小的程度小于某个阈值)的时候停止继续训练。
运行上述模型训练代码,输出的结果如下:
......(忽略之前的输出) Epoch 22/100 64/1167 [>.............................] - ETA: 3s - loss: 0.0399 - acc: 1.0000 128/1167 [==>...........................] - ETA: 3s - loss: 0.1195 - acc: 0.9844 192/1167 [===>..........................] - ETA: 2s - loss: 0.1085 - acc: 0.9792 256/1167 [=====>........................] - ETA: 2s - loss: 0.1132 - acc: 0.9727 320/1167 [=======>......................] - ETA: 2s - loss: 0.1045 - acc: 0.9750 384/1167 [========>.....................] - ETA: 2s - loss: 0.1006 - acc: 0.9740 448/1167 [==========>...................] - ETA: 2s - loss: 0.1522 - acc: 0.9643 512/1167 [============>.................] - ETA: 1s - loss: 0.1450 - acc: 0.9648 576/1167 [=============>................] - ETA: 1s - loss: 0.1368 - acc: 0.9653 640/1167 [===============>..............] - ETA: 1s - loss: 0.1353 - acc: 0.9641 704/1167 [=================>............] - ETA: 1s - loss: 0.1280 - acc: 0.9659 768/1167 [==================>...........] - ETA: 1s - loss: 0.1243 - acc: 0.9674 832/1167 [====================>.........] - ETA: 0s - loss: 0.1577 - acc: 0.9639 896/1167 [======================>.......] - ETA: 0s - loss: 0.1488 - acc: 0.9665 960/1167 [=======================>......] - ETA: 0s - loss: 0.1488 - acc: 0.9656 1024/1167 [=========================>....] - ETA: 0s - loss: 0.1427 - acc: 0.9668 1088/1167 [==========================>...] - ETA: 0s - loss: 0.1435 - acc: 0.9669 1152/1167 [============================>.] - ETA: 0s - loss: 0.1383 - acc: 0.9688 1167/1167 [==============================] - 4s 3ms/step - loss: 0.1380 - acc: 0.9683 - val_loss: 0.0835 - val_acc: 0.9760 Epoch 00022: early stopping
可以看到,花费几分钟,一共训练了21次,最近一次的训练后,在测试集上的准确率为96.83%。在测试集的准确率曲线如下图:
模型训练完后,我们对新的验证码进行预测。新的100张验证码如下图:
使用训练好的CNN模型,对这些新的验证码进行预测,预测的Python代码如下:
# -*- coding: utf-8 -*- import os import cv2 import numpy as np def split_picture(imagepath): # 以灰度模式读取图片 gray = cv2.imread(imagepath, 0) # 将图片的边缘变为白色 height, width = gray.shape for i in range(width): gray[0, i] = 255 gray[height-1, i] = 255 for j in range(height): gray[j, 0] = 255 gray[j, width-1] = 255 # 中值滤波 blur = cv2.medianBlur(gray, 3) #模板大小3*3 # 二值化 ret,thresh1 = cv2.threshold(blur, 200, 255, cv2.THRESH_BINARY) # 提取单个字符 chars_list = [] image, contours, hierarchy = cv2.findContours(thresh1, 2, 2) for cnt in contours: # 最小的外接矩形 x, y, w, h = cv2.boundingRect(cnt) if x != 0 and y != 0 and w*h >= 100: chars_list.append((x,y,w,h)) sorted_chars_list = sorted(chars_list, key=lambda x:x[0]) for i,item in enumerate(sorted_chars_list): x, y, w, h = item cv2.imwrite('test_verifycode/%d.jpg'%(i+1), thresh1[y:y+h, x:x+w]) def remove_edge_picture(imagepath): image = cv2.imread(imagepath, 0) height, width = image.shape corner_list = [image[0,0] < 127, image[height-1, 0] < 127, image[0, width-1]<127, image[ height-1, width-1] < 127 ] if sum(corner_list) >= 3: os.remove(imagepath) def resplit_with_parts(imagepath, parts): image = cv2.imread(imagepath, 0) os.remove(imagepath) height, width = image.shape file_name = imagepath.split('/')[-1].split(r'.')[0] # 将图片重新分裂成parts部分 step = width//parts # 步长 start = 0 # 起始位置 for i in range(parts): cv2.imwrite('./test_verifycode/%s.jpg'%(file_name+'-'+str(i)), image[:, start:start+step]) start += step def resplit(imagepath): image = cv2.imread(imagepath, 0) height, width = image.shape if width >= 64: resplit_with_parts(imagepath, 4) elif width >= 48: resplit_with_parts(imagepath, 3) elif width >= 26: resplit_with_parts(imagepath, 2) # rename and convert to 16*20 size def convert(dir, file): imagepath = dir+'/'+file # 读取图片 image = cv2.imread(imagepath, 0) # 二值化 ret, thresh = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY) img = cv2.resize(thresh, (16, 20), interpolation=cv2.INTER_AREA) # 保存图片 cv2.imwrite('%s/%s' % (dir, file), img) # 读取图片的数据,并转化为0-1值 def Read_Data(dir, file): imagepath = dir+'/'+file # 读取图片 image = cv2.imread(imagepath, 0) # 二值化 ret, thresh = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY) # 显示图片 bin_values = [1 if pixel==255 else 0 for pixel in thresh.ravel()] return bin_values def predict(VerifyCodePath): dir = './test_verifycode' files = os.listdir(dir) # 清空原有的文件 if files: for file in files: os.remove(dir + '/' + file) split_picture(VerifyCodePath) files = os.listdir(dir) if not files: print('查看的文件夹为空!') else: # 去除噪声图片 for file in files: remove_edge_picture(dir + '/' + file) # 对黏连图片进行重分割 for file in os.listdir(dir): resplit(dir + '/' + file) # 将图片统一调整至16*20大小 for file in os.listdir(dir): convert(dir, file) # 图片中的字符代表的向量 files = sorted(os.listdir(dir), key=lambda x: x[0]) table = np.array([Read_Data(dir, file) for file in files]).reshape(-1,20,16,1) # 模型保存地址 mp = './verifycode_Keras.h5' # 载入模型 from keras.models import load_model cnn = load_model(mp) # 模型预测 y_pred = cnn.predict(table) predictions = np.argmax(y_pred, axis=1) # 标签字典 keys = range(31) vals = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'X', 'Y', 'Z'] label_dict = dict(zip(keys, vals)) return ''.join([label_dict[pred] for pred in predictions]) def main(): dir = './VerifyCode/' correct = 0 for i, file in enumerate(os.listdir(dir)): true_label = file.split('.')[0] VerifyCodePath = dir+file pred = predict(VerifyCodePath) if true_label == pred: correct += 1 print(i+1, (true_label, pred), true_label == pred, correct) total = len(os.listdir(dir)) print('\n总共图片:%d张\n识别正确:%d张\n识别准确率:%.2f%%.' %(total, correct, correct*100/total)) main()
以下是该CNN模型的预测结果:
Using TensorFlow backend.
2018-10-25 15:13:50.390130: I C: f_jenkinsworkspace
el-winMwindowsPY35 ensorflowcoreplatformcpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
1 ('ZK6N', 'ZK6N') True 1
2 ('4JPX', '4JPX') True 2
3 ('5GP5', '5GP5') True 3
4 ('5RQ8', '5RQ8') True 4
5 ('5TQP', '5TQP') True 5
6 ('7S62', '7S62') True 6
7 ('8R2Z', '8R2Z') True 7
8 ('8RFV', '8RFV') True 8
9 ('9BBT', '9BBT') True 9
10 ('9LNE', '9LNE') True 10
11 ('67UH', '67UH') True 11
12 ('74UK', '74UK') True 12
13 ('A5T2', 'A5T2') True 13
14 ('AHYV', 'AHYV') True 14
15 ('ASEY', 'ASEY') True 15
16 ('B371', 'B371') True 16
17 ('CCQL', 'CCQL') True 17
18 ('CFD5', 'GFD5') False 17
19 ('CJLJ', 'CJLJ') True 18
20 ('D4QV', 'D4QV') True 19
21 ('DFQ8', 'DFQ8') True 20
22 ('DP18', 'DP18') True 21
23 ('E3HC', 'E3HC') True 22
24 ('E8VB', 'E8VB') True 23
25 ('DE1U', 'DE1U') True 24
26 ('FK1R', 'FK1R') True 25
27 ('FK91', 'FK91') True 26
28 ('FSKP', 'FSKP') True 27
29 ('FVZP', 'FVZP') True 28
30 ('GC6H', 'GC6H') True 29
31 ('GH62', 'GH62') True 30
32 ('H9FQ', 'H9FQ') True 31
33 ('H67Q', 'H67Q') True 32
34 ('HEKC', 'HEKC') True 33
35 ('HV2B', 'HV2B') True 34
36 ('J65Z', 'J65Z') True 35
37 ('JZCX', 'JZCX') True 36
38 ('KH5D', 'KH5D') True 37
39 ('KXD2', 'KXD2') True 38
40 ('1GDH', '1GDH') True 39
41 ('LCL3', 'LCL3') True 40
42 ('LNZR', 'LNZR') True 41
43 ('LZU5', 'LZU5') True 42
44 ('N5AK', 'N5AK') True 43
45 ('N5Q3', 'N5Q3') True 44
46 ('N96Z', 'N96Z') True 45
47 ('NCDG', 'NCDG') True 46
48 ('NELS', 'NELS') True 47
49 ('P96U', 'P96U') True 48
50 ('PD42', 'PD42') True 49
51 ('PECG', 'PEQG') False 49
52 ('PPZF', 'PPZF') True 50
53 ('PUUL', 'PUUL') True 51
54 ('Q2DN', 'D2DN') False 51
55 ('QCQ9', 'QCQ9') True 52
56 ('QDB1', 'QDBJ') False 52
57 ('QZUD', 'QZUD') True 53
58 ('R3T5', 'R3T5') True 54
59 ('S1YT', 'S1YT') True 55
60 ('SP7L', 'SP7L') True 56
61 ('SR2K', 'SR2K') True 57
62 ('SUP5', 'SVP5') False 57
63 ('T2SP', 'T2SP') True 58
64 ('U6V9', 'U6V9') True 59
65 ('UC9P', 'UC9P') True 60
66 ('UFYD', 'UFYD') True 61
67 ('V9NJ', 'V9NH') False 61
68 ('V35X', 'V35X') True 62
69 ('V98F', 'V98F') True 63
70 ('VD28', 'VD28') True 64
71 ('YGHE', 'YGHE') True 65
72 ('YNKD', 'YNKD') True 66
73 ('YVXV', 'YVXV') True 67
74 ('ZFBS', 'ZFBS') True 68
75 ('ET6X', 'ET6X') True 69
76 ('TKVC', 'TKVC') True 70
77 ('2UCU', '2UCU') True 71
78 ('HNBK', 'HNBK') True 72
79 ('X8FD', 'X8FD') True 73
80 ('ZGNX', 'ZGNX') True 74
81 ('LQCU', 'LQCU') True 75
82 ('JNZY', 'JNZVY') False 75
83 ('RX34', 'RX34') True 76
84 ('811E', '811E') True 77
85 ('ETDX', 'ETDX') True 78
86 ('4CPR', '4CPR') True 79
87 ('FE91', 'FE91') True 80
88 ('B7XH', 'B7XH') True 81
89 ('1RUA', '1RUA') True 82
90 ('UBCX', 'UBCX') True 83
91 ('KVT5', 'KVT5') True 84
92 ('HZ3A', 'HZ3A') True 85
93 ('3XLR', '3XLR') True 86
94 ('VC7T', 'VC7T') True 87
95 ('7PG1', '7PQ1') False 87
96 ('4F21', '4F21') True 88
97 ('3HLJ', '3HLJ') True 89
98 ('1KT7', '1KT7') True 90
99 ('1RHE', '1RHE') True 91
100 ('1TTA', '1TTA') True 92
总共图片:100张
识别正确:92张
识别准确率:92.00%.
可以看到,该训练后的CNN模型,其预测新验证的准确率在90%以上。
Demo及数据集下载网站:CNN_4_Verifycode_jb51.rar
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]