深度神经网络实现验证码识别
前段时间接到了一个小项目,要做一个验证码的识别,验证码包含数字和英文字母,实现识别的过程用到了CNN网络,最后单个字符的准确率达到90%以上。
准备数据集
登录界面有一个验证码的网址,直接用代码向服务器请求了一万张二维码下来。
图像处理
先把图片二值化,然后切割成单个的字符。
#将验证码切割保存到每个数据标签文件夹
t=2
n=13
for i in xs_o:
photo=np.split(i[1], [t,t+n,t+2*n,t+3*n,t+4*n],axis=1)[1:5]
sp=0
while sp<4:
if i[0][sp]==' ':
st='X'
else:
st=i[0][sp]
if not os.path.exists('yzm_sets/'+st):
os.mkdir('yzm_sets/'+st)
matplotlib.image.imsave('yzm_sets/'+st+'/'+st+'_'+str(time.time())+'.png',photo[sp])
sp+=1
切完了再二值化处理一下
#验证码进行二值化处理
# 图片灰化
CAPTCHA_IMAGE_PATH='yzm_sets_k_2'
from PIL import Image
for j in os.listdir(CAPTCHA_IMAGE_PATH):
img = Image.open('yzm_sets_k_2/'+j)
# 模式L”为灰色图像,它的每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度。
Img = img.convert('L')
# 自定义灰度界限,大于这个值为黑色,小于这个值为白色
threshold =50
table = []
for i in range(256):
if i < threshold:
table.append(1)
else:
table.append(0)
# 图片二值化
photo = Img.point(table, '1')
photo.save("yzm_sets_kk_2/"+j)
标注训练数据
数据量太大了,我想到一个偷懒的方法,就是用百度的人工智能服务,每天可以免费调用api好几万次吧,够用了,但是识别率不怎么样,我就人工筛选了标注错的。
#调用百度识别API制作数据标签
def get_access_token():
url='https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=I3Nc86z2j2MIedoZcGZeSP7P&client_secret=cD8W3vOCcv7kFOdawbK0baoADuVjZSOU'
html=requests.get(url)
access_token=json.loads(html.text)['access_token']
return access_token
def ocr(base64_data,access_token):
url='https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token='+access_token
data={
'image':base64_data
}
html=requests.post(url,data=data)
return json.loads(html.text)
access_token=get_access_token()
IMAGE_PATH='yzm_'
for i in os.listdir(IMAGE_PATH):
try:
with open("yzm_/"+i,"rb") as f:#转为二进制格式
base64_data = base64.b64encode(f.read())#使用base64进行加密
time.sleep(0.2)
st=ocr(base64_data ,access_token)
if len(st['words_result']):
words=st['words_result'][0]['words']
if len(words)==4:
copyfile("yzm_/"+i, 'yzm_c/'+str(time.time())+'_'+words+'.png')#正确识别
else:
copyfile("yzm_/"+i, 'yzm_w/'+str(time.time())+'_'+words+'.png')#错误识别
#print(ocr(base64_data ,access_token))
except:
print('Error',i)
搭建模型
采用的是主流的CNN架构
- 独热码映射字典
char='0123456789abcdefghijklmnopqrstuvwxyz'
char2onehot={}
s=0
for i in char:
y=[0.]*36
y[s]=1.
char2onehot[i]=y
s+=1
- 定义数据形状
xs=tf.placeholder(tf.float32,[None,27*13])
ys=tf.placeholder(tf.float32,[None,36])
x_image=tf.reshape(xs,shape=[-1,27,13,1])
- 权重
def w_v(shape):
return tf.Variable(tf.truncated_normal(shape,stddev=0.5),name='w')
- 偏置
def b_v(shape):
b=tf.constant(0.0001,shape=shape)
return tf.Variable(b,name='b')
- 卷积
def conv2d(x,W):
return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME',name='c')
- 池化
def max_pooling(x):
return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME',name='p')
- `conv1
w1=w_v([5,5,1,4])
b1=b_v([4])
c1=conv2d(x_image,w1)+b1#shape=27,13,1
p1=max_pooling(c1)#shape=,30,4
- conv2
w2=w_v([5,5,4,8])
b2=b_v([8])
c2=conv2d(p1,w2)+b2#shape=30,30,4
p2=max_pooling(c2)#shape=15,15,8
- f1
w3=w_v([7*4*8,128])
b3=b_v([128])
f1=tf.nn.relu(tf.matmul(tf.reshape(p2,[-1,7*4*8]),w3)+b3)
- f2
w4=w_v([128,36])
b4=b_v([36])
f2=tf.matmul(f1,w4)+b4
prediction=tf.nn.softmax(f2)
- 定义损失函数和优化器
#交叉熵代价损失函数
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=ys, logits=prediction))
train_step=tf.train.AdamOptimizer(le-4).minimize(loss)
完整代码
import tensorflow as tf
import numpy as np
# 定义字符集
char='0123456789abcdefghijklmnopqrstuvwxyz'
char2onehot={}
s=0
for i in char:
y=[0.]*36
y[s]=1.
char2onehot[i]=y
s+=1
# 定义输入和输出
xs=tf.placeholder(tf.float32,[None,27*13])
ys=tf.placeholder(tf.float32,[None,36])
x_image=tf.reshape(xs,shape=[-1,27,13,1])
# 定义权重和偏置变量
def w_v(shape):
return tf.Variable(tf.truncated_normal(shape,stddev=0.5),name='w')
def b_v(shape):
b=tf.constant(0.0001,shape=shape)
return tf.Variable(b,name='b')
# 定义卷积和池化函数
def conv2d(x,W):
return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME',name='c')
def max_pooling(x):
return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME',name='p')
# 定义神经网络的结构
w1=w_v([5,5,1,4])
b1=b_v([4])
c1=conv2d(x_image,w1)+b1#shape=27,13,1
p1=max_pooling(c1)#shape=,30,4
w2=w_v([5,5,4,8])
b2=b_v([8])
c2=conv2d(p1,w2)+b2#shape=30,30,4
p2=max_pooling(c2)#shape=15,15,8
w3=w_v([7*4*8,128])
b3=b_v([128])
f1=tf.nn.relu(tf.matmul(tf.reshape(p2,[-1,7*4*8]),w3)+b3)
w4=w_v([128,36])
b4=b_v([36])
f2=tf.matmul(f1,w4)+b4
prediction=tf.nn.softmax(f2)
# 定义损失函数和优化器
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=ys, logits=prediction))
train_step=tf.train.AdamOptimizer(1e-4).minimize(loss)
# 准备训练数据集和标签
# ...
# 创建会话并初始化变量
sess = tf.Session()
sess.run(tf.global_variables_initializer())
# 训练过程
for i in range(num_epochs):
# 从数据集中取出一个batch的数据和标签
# ...
# 将batch数据和标签输入神经网络,计算损失函数并更新参数
_, train_loss = sess.run([train_step, loss], feed_dict={xs: batch_xs, ys: batch_ys})
# 计算训练集和测试集的准确率
# ...
# 输出训练集和测试集的准确率和损失函数
# ...
# 关闭会话
sess.close()
训练结束后保存模型及加载使用
import tensorflow as tf
# 创建 Saver 对象
saver = tf.train.Saver()
# 训练过程
# ...
# 保存模型
saver.save(sess, "models/model.ckpt")
# 加载模型
saver.restore(sess, "models/model.ckpt")