验证码识别

转自https://blog.csdn.net/maliao1123/article/details/79415828

Tensorflow是目前最流行的深度学习框架,我们可以用它来搭建自己的卷积神经网络并训练自己的分类器,本文介绍怎样使用Tensorflow构建自己的CNN,怎样训练用于简单的验证码识别的分类器。本文假设你已经安装好了Tensorflow,了解过CNN的一些知识。

下面将分步介绍怎样获得训练数据,怎样使用tensorflow构建卷积神经网络,怎样训练,以及怎样测试训练出来的分类器

1. 准备训练样本

        使用Python的库captcha来生成我们需要的训练样本,代码如下:

 

[python] view plain copy

  1. import sys  

[python] view plain copy

  1. import os  
  2. import shutil  
  3. import random  
  4. import time  
  5. #captcha是用于生成验证码图片的库,可以 pip install captcha 来安装它  
  6. from captcha.image import ImageCaptcha  
  7.   
  8. #用于生成验证码的字符集  
  9. CHAR_SET = ['0','1','2','3','4','5','6','7','8','9']  
  10. #字符集的长度  
  11. CHAR_SET_LEN = 10  
  12. #验证码的长度,每个验证码由4个数字组成  
  13. CAPTCHA_LEN = 4  
  14.   
  15. #验证码图片的存放路径  
  16. CAPTCHA_IMAGE_PATH = 'E:/Tensorflow/captcha/images/'  
  17. #用于模型测试的验证码图片的存放路径,它里面的验证码图片作为测试集  
  18. TEST_IMAGE_PATH = 'E:/Tensorflow/captcha/test/'  
  19. #用于模型测试的验证码图片的个数,从生成的验证码图片中取出来放入测试集中  
  20. TEST_IMAGE_NUMBER = 50  
  21.   
  22. #生成验证码图片,4位的十进制数字可以有10000种验证码  
  23. def generate_captcha_image(charSet = CHAR_SET, charSetLen=CHAR_SET_LEN, captchaImgPath=CAPTCHA_IMAGE_PATH):     
  24.     k  = 0  
  25.     total = 1  
  26.     for i in range(CAPTCHA_LEN):  
  27.         total *= charSetLen  
  28.           
  29.     for i in range(charSetLen):  
  30.         for j in range(charSetLen):  
  31.             for m in range(charSetLen):  
  32.                 for n in range(charSetLen):  
  33.                     captcha_text = charSet[i] + charSet[j] + charSet[m] + charSet[n]  
  34.                     image = ImageCaptcha()  
  35.                     image.write(captcha_text, captchaImgPath + captcha_text + '.jpg')  
  36.                     k += 1  
  37.                     sys.stdout.write("\rCreating %d/%d" % (k, total))  
  38.                     sys.stdout.flush()  
  39.                       
  40. #从验证码的图片集中取出一部分作为测试集,这些图片不参加训练,只用于模型的测试                      
  41. def prepare_test_set():  
  42.     fileNameList = []      
  43.     for filePath in os.listdir(CAPTCHA_IMAGE_PATH):  
  44.         captcha_name = filePath.split('/')[-1]  
  45.         fileNameList.append(captcha_name)  
  46.     random.seed(time.time())  
  47.     random.shuffle(fileNameList)   
  48.     for i in range(TEST_IMAGE_NUMBER):  
  49.         name = fileNameList[i]  
  50.         shutil.move(CAPTCHA_IMAGE_PATH + name, TEST_IMAGE_PATH + name)  
  51.                           
  52. if __name__ == '__main__':  
  53.     generate_captcha_image(CHAR_SET, CHAR_SET_LEN, CAPTCHA_IMAGE_PATH)  
  54.     prepare_test_set()  
  55.     sys.stdout.write("\nFinished")  
  56.     sys.stdout.flush()    

 

运行上面的代码,可以生成验证码图片,

生成的验证码图片如下图所示:

 

2. 构建CNN,训练分类器

     代码如下:

 

[html] view plain copy

  1.   

[html] view plain copy

  1. import tensorflow as tf  
  2. import numpy as np  
  3. from PIL import Image  
  4. import os  
  5. import random  
  6. import time  
  7.   
  8. #验证码图片的存放路径  
  9. CAPTCHA_IMAGE_PATH = 'E:/Tensorflow/captcha/images/'  
  10. #验证码图片的宽度  
  11. CAPTCHA_IMAGE_WIDHT = 160  
  12. #验证码图片的高度  
  13. CAPTCHA_IMAGE_HEIGHT = 60  
  14.   
  15. CHAR_SET_LEN = 10  
  16. CAPTCHA_LEN = 4  
  17.   
  18. #60%的验证码图片放入训练集中  
  19. TRAIN_IMAGE_PERCENT = 0.6  
  20. #训练集,用于训练的验证码图片的文件名  
  21. TRAINING_IMAGE_NAME = []  
  22. #验证集,用于模型验证的验证码图片的文件名  

[html] view plain copy

  1. VALIDATION_IMAGE_NAME = []  

 

[html] view plain copy

  1. #存放训练好的模型的路径  
  2. MODEL_SAVE_PATH = 'E:/Tensorflow/captcha/models/'  
  3.   
  4. def get_image_file_name(imgPath=CAPTCHA_IMAGE_PATH):  
  5.     fileName = []  
  6.     total = 0  
  7.     for filePath in os.listdir(imgPath):  
  8.         captcha_name = filePath.split('/')[-1]  
  9.         fileName.append(captcha_name)  
  10.         total += 1  
  11.     return fileName, total  
  12.       
  13. #将验证码转换为训练时用的标签向量,维数是 40     
  14. #例如,如果验证码是 ‘0296’ ,则对应的标签是  
  15. # [1 0 0 0 0 0 0 0 0 0  
  16. #  0 0 1 0 0 0 0 0 0 0  
  17. #  0 0 0 0 0 0 0 0 0 1  
  18. #  0 0 0 0 0 0 1 0 0 0]  
  19. def name2label(name):  
  20.     label = np.zeros(CAPTCHA_LEN * CHAR_SET_LEN)  
  21.     for i, c in enumerate(name):  
  22.         idx = i*CHAR_SET_LEN + ord(c) - ord('0')  
  23.         label[idx] = 1  
  24.     return label  
  25.       
  26. #取得验证码图片的数据以及它的标签          
  27. def get_data_and_label(fileName, filePath=CAPTCHA_IMAGE_PATH):  
  28.     pathName = os.path.join(filePath, fileName)  
  29.     img = Image.open(pathName)  
  30.     #转为灰度图  
  31.     img = img.convert("L")         
  32.     image_array = np.array(img)      
  33.     image_data = image_array.flatten()/255  
  34.     image_label = name2label(fileName[0:CAPTCHA_LEN])  
  35.     return image_data, image_label  
  36.       
  37. #生成一个训练batch      
  38. def get_next_batch(batchSize=32, trainOrTest='train', step=0):  
  39.     batch_data = np.zeros([batchSize, CAPTCHA_IMAGE_WIDHT*CAPTCHA_IMAGE_HEIGHT])  
  40.     batch_label = np.zeros([batchSize, CAPTCHA_LEN * CHAR_SET_LEN])  
  41.     fileNameList = TRAINING_IMAGE_NAME  
  42.     if trainOrTest == 'validate':          
  43.         fileNameList = VALIDATION_IMAGE_NAME  
  44.           
  45.     totalNumber = len(fileNameList)   
  46.     indexStart = step*batchSize      
  47.     for i in range(batchSize):  
  48.         index = (i + indexStart) % totalNumber  
  49.         name = fileNameList[index]          
  50.         img_data, img_label = get_data_and_label(name)  
  51.         batch_data[i, : ] = img_data  
  52.         batch_label[i, : ] = img_label    
  53.   
  54.     return batch_data, batch_label  
  55.       
  56. #构建卷积神经网络并训练  
  57. def train_data_with_CNN():  
  58.     #初始化权值  
  59.     def weight_variable(shape, name='weight'):  
  60.         init = tf.truncated_normal(shape, stddev=0.1)  
  61.         var = tf.Variable(initial_value=init, name=name)  
  62.         return var  
  63.     #初始化偏置      
  64.     def bias_variable(shape, name='bias'):  
  65.         init = tf.constant(0.1, shape=shape)  
  66.         var = tf.Variable(init, name=name)  
  67.         return var  
  68.     #卷积      
  69.     def conv2d(x, W, name='conv2d'):  
  70.         return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME', name=name)  
  71.     #池化   
  72.     def max_pool_2X2(x, name='maxpool'):  
  73.         return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME', name=name)       
  74.      
  75.     #输入层  
  76.     #请注意 X 的 name,在测试model时会用到它  
  77.     X = tf.placeholder(tf.float32, [None, CAPTCHA_IMAGE_WIDHT * CAPTCHA_IMAGE_HEIGHT], name='data-input')  
  78.     Y = tf.placeholder(tf.float32, [None, CAPTCHA_LEN * CHAR_SET_LEN], name='label-input')      
  79.     x_input = tf.reshape(X, [-1, CAPTCHA_IMAGE_HEIGHT, CAPTCHA_IMAGE_WIDHT, 1], name='x-input')  
  80.     #dropout,防止过拟合  
  81.     #请注意 keep_prob 的 name,在测试model时会用到它  
  82.     keep_prob = tf.placeholder(tf.float32, name='keep-prob')  
  83.     #第一层卷积  
  84.     W_conv1 = weight_variable([5,5,1,32], 'W_conv1')  
  85.     B_conv1 = bias_variable([32], 'B_conv1')  
  86.     conv1 = tf.nn.relu(conv2d(x_input, W_conv1, 'conv1') + B_conv1)  
  87.     conv1 = max_pool_2X2(conv1, 'conv1-pool')  
  88.     conv1 = tf.nn.dropout(conv1, keep_prob)  
  89.     #第二层卷积  
  90.     W_conv2 = weight_variable([5,5,32,64], 'W_conv2')  
  91.     B_conv2 = bias_variable([64], 'B_conv2')  
  92.     conv2 = tf.nn.relu(conv2d(conv1, W_conv2,'conv2') + B_conv2)  
  93.     conv2 = max_pool_2X2(conv2, 'conv2-pool')  
  94.     conv2 = tf.nn.dropout(conv2, keep_prob)  
  95.     #第三层卷积  
  96.     W_conv3 = weight_variable([5,5,64,64], 'W_conv3')  
  97.     B_conv3 = bias_variable([64], 'B_conv3')  
  98.     conv3 = tf.nn.relu(conv2d(conv2, W_conv3, 'conv3') + B_conv3)  
  99.     conv3 = max_pool_2X2(conv3, 'conv3-pool')  
  100.     conv3 = tf.nn.dropout(conv3, keep_prob)  
  101.     #全链接层  
  102.     #每次池化后,图片的宽度和高度均缩小为原来的一半,进过上面的三次池化,宽度和高度均缩小8倍  
  103.     W_fc1 = weight_variable([20*8*64, 1024], 'W_fc1')  
  104.     B_fc1 = bias_variable([1024], 'B_fc1')  
  105.     fc1 = tf.reshape(conv3, [-1, 20*8*64])  
  106.     fc1 = tf.nn.relu(tf.add(tf.matmul(fc1, W_fc1), B_fc1))  
  107.     fc1 = tf.nn.dropout(fc1, keep_prob)  
  108.     #输出层  
  109.     W_fc2 = weight_variable([1024, CAPTCHA_LEN * CHAR_SET_LEN], 'W_fc2')  
  110.     B_fc2 = bias_variable([CAPTCHA_LEN * CHAR_SET_LEN], 'B_fc2')  
  111.     output = tf.add(tf.matmul(fc1, W_fc2), B_fc2, 'output')  
  112.       
  113.     loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=Y, logits=output))  
  114.     optimizer = tf.train.AdamOptimizer(0.001).minimize(loss)  
  115.       
  116.     predict = tf.reshape(output, [-1, CAPTCHA_LEN, CHAR_SET_LEN], name='predict')  
  117.     labels = tf.reshape(Y, [-1, CAPTCHA_LEN, CHAR_SET_LEN], name='labels')  
  118.     #预测结果  
  119.     #请注意 predict_max_idx 的 name,在测试model时会用到它  
  120.     predict_max_idx = tf.argmax(predict, axis=2, name='predict_max_idx')  
  121.     labels_max_idx = tf.argmax(labels, axis=2, name='labels_max_idx')  
  122.     predict_correct_vec = tf.equal(predict_max_idx, labels_max_idx)  
  123.     accuracy = tf.reduce_mean(tf.cast(predict_correct_vec, tf.float32))  
  124.       
  125.     saver = tf.train.Saver()  
  126.     with tf.Session() as sess:  
  127.         sess.run(tf.global_variables_initializer())  
  128.         steps = 0  
  129.         for epoch in range(6000):  
  130.             train_data, train_label = get_next_batch(64, 'train', steps)  
  131.             sess.run(optimizer, feed_dict={X : train_data, Y : train_label, keep_prob:0.75})  
  132.             if steps % 100 == 0:  
  133.                 test_data, test_label = get_next_batch(100, 'validate', steps)  
  134.                 acc = sess.run(accuracy, feed_dict={X : test_data, Y : test_label, keep_prob:1.0})  
  135.                 print("steps=%d, accuracy=%f" % (steps, acc))  
  136.                 if acc > 0.99:  
  137.                     saver.save(sess, MODEL_SAVE_PATH+"crack_captcha.model", global_step=steps)  
  138.                     break  
  139.             steps += 1  
  140.   
  141. if __name__ == '__main__':      
  142.     image_filename_list, total = get_image_file_name(CAPTCHA_IMAGE_PATH)  
  143.     random.seed(time.time())  
  144.     #打乱顺序  
  145.     random.shuffle(image_filename_list)  
  146.     trainImageNumber = int(total * TRAIN_IMAGE_PERCENT)  
  147.     #分成测试集  
  148.     TRAINING_IMAGE_NAME = image_filename_list[ : trainImageNumber]  
  149.     #和验证集  
  150.     VALIDATION_IMAGE_NAME = image_filename_list[trainImageNumber : ]  
  151.     train_data_with_CNN()      
  152.     print('Training finished')  

运行上面的代码,开始训练,训练要花些时间,如果没有GPU的话,会慢些,

训练完后,输出如下结果,经过4100次的迭代,训练出来的分类器模型在验证集上识别的准确率为99.5%

生成的模型文件如下,在模型测试时将用到这些文件


 

3. 测试模型

编写代码,对训练出来的模型进行测试

 

[python] view plain copy

  1. import tensorflow as tf  

[python] view plain copy

  1. import numpy as np  
  2. from PIL import Image  
  3. import os  
  4. import matplotlib.pyplot as plt   
  5.   
  6. CAPTCHA_LEN = 4  
  7.   
  8. MODEL_SAVE_PATH = 'E:/Tensorflow/captcha/models/'  
  9. TEST_IMAGE_PATH = 'E:/Tensorflow/captcha/test/'  
  10.   
  11. def get_image_data_and_name(fileName, filePath=TEST_IMAGE_PATH):  
  12.     pathName = os.path.join(filePath, fileName)  
  13.     img = Image.open(pathName)  
  14.     #转为灰度图  
  15.     img = img.convert("L")         
  16.     image_array = np.array(img)      
  17.     image_data = image_array.flatten()/255  
  18.     image_name = fileName[0:CAPTCHA_LEN]  
  19.     return image_data, image_name  
  20.   
  21. def digitalStr2Array(digitalStr):  
  22.     digitalList = []  
  23.     for c in digitalStr:  
  24.         digitalList.append(ord(c) - ord('0'))  
  25.     return np.array(digitalList)  
  26.   
  27. def model_test():  
  28.     nameList = []  
  29.     for pathName in os.listdir(TEST_IMAGE_PATH):  
  30.         nameList.append(pathName.split('/')[-1])  
  31.     totalNumber = len(nameList)  
  32.     #加载graph  
  33.     saver = tf.train.import_meta_graph(MODEL_SAVE_PATH+"crack_captcha.model-4100.meta")  
  34.     graph = tf.get_default_graph()  
  35.     #从graph取得 tensor,他们的name是在构建graph时定义的(查看上面第2步里的代码)  
  36.     input_holder = graph.get_tensor_by_name("data-input:0")  
  37.     keep_prob_holder = graph.get_tensor_by_name("keep-prob:0")  
  38.     predict_max_idx = graph.get_tensor_by_name("predict_max_idx:0")  
  39.     with tf.Session() as sess:  
  40.         saver.restore(sess, tf.train.latest_checkpoint(MODEL_SAVE_PATH))  
  41.         count = 0  
  42.         for fileName in nameList:  
  43.             img_data, img_name = get_image_data_and_name(fileName, TEST_IMAGE_PATH)  
  44.             predict = sess.run(predict_max_idx, feed_dict={input_holder:[img_data], keep_prob_holder : 1.0})              
  45.             filePathName = TEST_IMAGE_PATH + fileName  
  46.             print(filePathName)  
  47.             img = Image.open(filePathName)  
  48.             plt.imshow(img)  
  49.             plt.axis('off')  
  50.             plt.show()  
  51.             predictValue = np.squeeze(predict)  
  52.             rightValue = digitalStr2Array(img_name)  
  53.             if np.array_equal(predictValue, rightValue):  
  54.                 result = '正确'  
  55.                 count += 1  
  56.             else:   
  57.                 result = '错误'              
  58.             print('实际值:{}, 预测值:{},测试结果:{}'.format(rightValue, predictValue, result))  
  59.             print('\n')  
  60.               
  61.         print('正确率:%.2f%%(%d/%d)' % (count*100/totalNumber, count, totalNumber))  
  62.   
  63. if __name__ == '__main__':  
  64.     model_test()  

 

 

对模型的测试结果如下,在测试集上识别的准确率为 94%

下面是两个识别错误的验证码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值