Tensorflow2.0+版本 实现图形验证码的自动识别
tensorflow
一个面向所有人的开源机器学习框架
项目地址:https://gitcode.com/gh_mirrors/te/tensorflow
免费下载资源
·
说明
代码只有两个文件,一个生成验证码,一个是训练和预测,最后面会把所有代码发出来。
二维码的制作
这里就大概过一下,使用的是PIL,生成验证码。
PIL:是python image library的缩写,图像处理的模块。
验证码制作,无非就是在一堆想要出现的数字字母里面,随机生成几个,但又为了不能轻意认识,加了干扰线干扰点。又觉得太单调了,好吧那就加上不同的颜色,稍微酷一点。
大概思路清楚,Just do it~~
准备全部数据
# 准备全部数据
# 这里先以数字识别试试
def getRandomChar():
random_num = str(random.randint(0, 9)) # 数字 0~9
random_lower = chr(random.randint(97, 122)) # 小写字母a~z
random_upper = chr(random.randint(65, 90)) # 大写字母A~Z
random_char = random.choice([random_num, random_upper])
return random_num
生成随机颜色
这里is_light留意一下,是为了生成浅色和深色用。具体后面讲为啥。
def getRandomColor(is_light = True):
"""
生成随机颜色
:param is_light: 以127分界线,为了设置浅色和深色
:return: (r, g, b)
"""
r = random.randint(0, 127) +int(is_light)* 128
g = random.randint(0, 127) +int(is_light)* 128
b = random.randint(0, 127) +int(is_light)* 128
return (r, g, b)
画干扰线和干扰点
干扰线和干扰点数量自己设置,看你心情
def drawLine(draw):
"""
随机生成4个干扰线,然后每个设置随机颜色
"""
for i in range(4):
x1 = random.randint(0, width)
x2 = random.randint(0, width)
y1 = random.randint(0, height)
y2 = random.randint(0, height)
draw.line((x1, y1, x2, y2), fill=getRandomColor(is_light=True))
def drawPoint(draw):
"""
随机生成80个干扰点,然后每个设置随机颜色
"""
for i in range(80):
x = random.randint(0, width)
y = random.randint(0, height)
draw.point((x,y), fill=getRandomColor(is_light=True))
画出验证码,保存
def createImg():
# 随机生成一个颜色
bg_color = getRandomColor(is_light=True)
# 创建一张随机背景色的图片
img = Image.new(mode="RGB", size=(width, height), color=bg_color)
# 获取图片画笔,用于描绘字
draw = ImageDraw.Draw(img)
# 修改字体
font = ImageFont.truetype(font="arial.ttf", size=18)
# 保存图片的名字
file_name = ''
# 这里生成4位数字,就循环4次
for i in range(4):
# 随机生成4种字符+4种颜色
random_txt = getRandomChar()
txt_color = getRandomColor(is_light=False)
# 避免文字颜色和背景色一致重合
while txt_color == bg_color:
txt_color = getRandomColor(is_light=False)
# 根据坐标填充文字
draw.text((15 + 15 * i, 0), text=random_txt, fill=txt_color, font=font)
file_name +=random_txt
# 画干扰线和点
drawLine(draw)
drawPoint(draw)
print(file_name)
# 打开图片操作,并保存在train文件夹下
with open("./train/{}.png".format(file_name), "wb") as f:
img.save(f, format="png")
效果就是这样的了
tensorflow识别
处理图片生成数据集
简单就是读取图片数据,生成训练集合测试集
def gen_train_data(filePath):
'''
生成数据集
:param filePath: 存filePath文件夹获取全部图片处理
:return: x_data:图片数据,shape=(num, 20, 80),y_data:标签信息, shape=(num, 4)
'''
#返回指定的文件夹包含的文件或文件夹的名字的列表。
train_file_name_list = os.listdir(filePath)
# 返回值
x_data = []
y_data = []
# 对每个图片单独处理
for selected_train_file_name in train_file_name_list:
if selected_train_file_name.endswith('.png'):
# 获取图片对象
captcha_image = Image.open(os.path.join(filePath, selected_train_file_name))
# 对图片去噪,后面对这个方法单独说明
captcha_image = denoising(captcha_image)
# captcha_image = captcha_image.convert('L') # 对于简单的不用去噪,灰度反而更有利
captcha_image_np = np.array(captcha_image)
# 下面这两个是tensorflow获取图片信息,这里我们还是以上面为例
# img = tf.io.read_file(os.path.join(filePath, selected_train_file_name))
# img_np = tf.image.decode_jpeg(img, channels=0)
img_np = np.array(captcha_image_np)
# 把每个处理后的数据,塞进x_data,y_data
x_data.append(img_np)
y_data.append(np.array(list(selected_train_file_name.split('.')[0])).astype(np.int))
x_data = np.array(x_data).astype(np.float)
y_data = np.array(y_data)
return x_data,y_data
# 生成训练集--train_data_dir(训练文件验证码路径)
(x,y) = gen_train_data(train_data_dir)
# 生成测试集--test_data_dir(测试文件验证码路径)
(x_test,y_test) = gen_train_data(test_data_dir)
打印一下x,y的shape可以看见
对图片优化
对图片去噪,去掉一些没有任何意义的东西比如干扰点干扰线,只看见我想要的值。处理完之后,把图片灰度。
可惜的是目前去噪算法并没有很好的解决方案,网上很多的去除噪点,切割验证码等等方法并没有很好的解决。我试了试不同的方法,很多都是这种样子
所以实在没法解决的话,直接灰度也可以。(目前NL-Means和BM3D可以说是目前效果最好的去噪算法,放在这有点大材小用)
这个时候还记得验证码生成随机颜色函数的这个参数is_light?利用浅色和深色进行过滤,把is_light=False的像素全部改成白色~~(只是提供一种想法,其实就算不去噪,只灰度也能很高效率的识别)
def denoising(image):
"""
处理图片,方便更好识别,学习
:param image:图片对象
:return: 处理之后的图片
"""
threshold = 128 # 通过设置阈值,去除不必要的干扰物
for i in range(image.width):
for j in range(image.height):
r,g,b = image.getpixel((i,j))
if (r > threshold or g >threshold or b > threshold):
r=255
g=255
b=255
image.putpixel((i,j),(r,g,b))
else:
r = 0
g = 0
b = 0
image.putpixel((i, j), (r, g, b))
# 灰度图片
image = image.convert('L')
return image
效果这样的。
接下来进行batch,对训练集每次10个来一次处理
def preprocess(x,y):
"""
对x,y进行数据处理,转成tensor张量,小范围缩小在-1~1之间
"""
x = 2*tf.cast(x,dtype=tf.float32)/255.-1
x = tf.expand_dims(x,-1)
y = tf.cast(y,dtype=tf.int32)
return x,y
batch_size = 10
train_db = tf.data.Dataset.from_tensor_slices((x,y))
train_db = train_db.map(preprocess).batch(batch_size)
建立模型
主要还是卷积神经网络经过池化,然后全连接层处理
model = Sequential([
# 第一个卷积层
layers.Conv2D(32, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# 第二个卷积层
layers.Conv2D(64, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
layers.Flatten(),
# 全连接
layers.Dense(128),
layers.Dense(40), # 因为这里我们4个数字,所以也就4*10可能性
layers.Reshape([4,10])
])
model.build(input_shape=[None, 20, 80, 1])
model.summary()
# 设置学习率
optimizer = optimizers.Adam(lr=1e-3)
进行训练
# 进行20次重复训练
for epoch in range(20):
for step, (x, y) in enumerate(train_db):
# 有的时候验证码不是这种格式,就没处理所以就不是的直接过滤
if x.shape == (10, 20, 80, 1):
with tf.GradientTape() as tape:
# logits
logits = model(x)
# 真实值就行one_hot编码来对比
y_onehot = tf.one_hot(y, depth=10)
# 设置loss
loss_ce = tf.losses.MSE(y_onehot, logits)
loss_ce = tf.reduce_mean(loss_ce)
# 不断更新梯度
grads = tape.gradient(loss_ce, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
if step % 10 == 0:
print(epoch, step, 'loss:', float(loss_ce))
可以看见从开始的0.14一直下降到0.006,大约准确率99%
对数据进行测试预测,发现确实挺高的,偶尔有那么一两个不对
全部代码
验证码文件
""" makeVerification.py 文件"""
import random
import os
from PIL import Image,ImageDraw,ImageFont,ImageFilter
# 设置图片宽高
width = 80
height = 20
"""
"""
def getRandomColor(is_light = True):
"""
生成随机颜色
:param is_light: 为了设置浅色和深色
:return: (r, g, b)
"""
r = random.randint(0, 127) +int(is_light)* 128
g = random.randint(0, 127) +int(is_light)* 128
b = random.randint(0, 127) +int(is_light)* 128
return (r, g, b)
# 这里全部数据
def getRandomChar():
random_num = str(random.randint(0, 9)) # 数字 0~9
random_lower = chr(random.randint(97, 122)) # 小写字母a~z
random_upper = chr(random.randint(65, 90)) # 大写字母A~Z
random_char = random.choice([random_num, random_upper])
return random_num
def drawLine(draw):
"""
随机生成4个干扰线,然后每个设置随机颜色
"""
for i in range(4):
x1 = random.randint(0, width)
x2 = random.randint(0, width)
y1 = random.randint(0, height)
y2 = random.randint(0, height)
draw.line((x1, y1, x2, y2), fill=getRandomColor(is_light=True))
def drawPoint(draw):
"""
随机生成80个干扰点,然后每个设置随机颜色
"""
for i in range(80):
x = random.randint(0, width)
y = random.randint(0, height)
draw.point((x,y), fill=getRandomColor(is_light=True))
def createImg(folder):
# 随机生成一个颜色为背景色颜色
bg_color = getRandomColor(is_light=True)
# 创建一张随机背景色的图片
img = Image.new(mode="RGB", size=(width, height), color=bg_color)
# 获取图片画笔,用于描绘字
draw = ImageDraw.Draw(img)
# 修改字体
font = ImageFont.truetype(font="arial.ttf", size=18)
# 保存图片的名字
file_name = ''
# 这里生成4位数字,就循环4次
for i in range(4):
# 随机生成4种字符+4种颜色
random_txt = getRandomChar()
txt_color = getRandomColor(is_light=False)
# 避免文字颜色和背景色一致重合
while txt_color == bg_color:
txt_color = getRandomColor(is_light=False)
# 根据坐标填充文字
draw.text((15 + 15 * i, 0), text=random_txt, fill=txt_color, font=font)
file_name +=random_txt
# 画干扰线和点
drawLine(draw)
drawPoint(draw)
print(file_name)
# 打开图片操作,并保存在当前文件夹下
with open("./{}/{}.png".format(folder,file_name), "wb") as f:
img.save(f, format="png")
if __name__ == '__main__':
# 创建num张验证码
num = 1000
# 创建train和test文件夹
os.path.exists('train') or os.makedirs('train')
os.path.exists('test') or os.makedirs('test')
# 每个文件夹创建num个
for i in range(num):
createImg('train')
createImg('test')
识别文件
import os
from PIL import Image
import numpy as np
import tensorflow as tf
from tensorflow.keras import datasets, layers, optimizers, Sequential, metrics
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
train_data_dir = r'D:\test\python\machinelearning\tensorFlow20\深度学习与TF-PPT和代码\深度学习与TensorFlow入门实战-源码和PPT\lesson32-Keras实战\验证码\train'
test_data_dir = r'D:\test\python\machinelearning\tensorFlow20\深度学习与TF-PPT和代码\深度学习与TensorFlow入门实战-源码和PPT\lesson32-Keras实战\验证码\test'
model_dir = r'D:\test\python\machinelearning\tensorFlow20\深度学习与TF-PPT和代码\深度学习与TensorFlow入门实战-源码和PPT\lesson32-Keras实战\验证码\model.h5'
def denoising(image):
"""
处理图片,方便更好识别,学习
:param image:图片对象
:return: 处理之后的图片
"""
threshold = 128 # 通过设置阈值,去除不必要的干扰物
for i in range(image.width):
for j in range(image.height):
r,g,b = image.getpixel((i,j))
if (r > threshold or g >threshold or b > threshold):
r=255
g=255
b=255
image.putpixel((i,j),(r,g,b))
else:
r = 0
g = 0
b = 0
image.putpixel((i, j), (r, g, b))
# 灰度图片
image = image.convert('L')
return image
def gen_train_data(filePath):
'''
生成数据集
:param filePath: 存filePath文件夹获取全部图片处理
:return: x_data:图片数据,shape=(num, 20, 80),y_data:标签信息, shape=(num, 4)
'''
#返回指定的文件夹包含的文件或文件夹的名字的列表。
train_file_name_list = os.listdir(filePath)
# 返回值
x_data = []
y_data = []
# 对每个图片单独处理
for selected_train_file_name in train_file_name_list:
if selected_train_file_name.endswith('.png'):
# 获取图片对象
captcha_image = Image.open(os.path.join(filePath, selected_train_file_name))
# 对图片去噪,后面对这个方法单独说明
captcha_image = denoising(captcha_image)
# captcha_image = captcha_image.convert('L') # 对于简单的不用去噪,灰度反而更有利
captcha_image_np = np.array(captcha_image)
# 下面这两个是tensorflow获取图片信息,这里我们还是以上面为例
# img = tf.io.read_file(os.path.join(filePath, selected_train_file_name))
# img_np = tf.image.decode_jpeg(img, channels=0)
img_np = np.array(captcha_image_np)
# 把每个处理后的数据,塞进x_data,y_data
x_data.append(img_np)
y_data.append(np.array(list(selected_train_file_name.split('.')[0])).astype(np.int))
x_data = np.array(x_data).astype(np.float)
y_data = np.array(y_data)
return x_data,y_data
# 生成训练集
(x,y) = gen_train_data(train_data_dir)
# 生成测试集
(x_test,y_test) = gen_train_data(test_data_dir)
print(x.shape,y.shape) #(num个图片验证码, 20宽, 80高) (955个图片验证码, 4)
def preprocess(x,y):
"""
对x,y进行数据处理,转成tensor张量,小范围缩小在-1~1之间
"""
x = 2*tf.cast(x,dtype=tf.float32)/255.-1
x = tf.expand_dims(x,-1)
y = tf.cast(y,dtype=tf.int32)
return x,y
batch_size = 10
train_db = tf.data.Dataset.from_tensor_slices((x,y))
train_db = train_db.map(preprocess).batch(batch_size)
test_db = tf.data.Dataset.from_tensor_slices((x_test,y_test))
test_db = test_db.map(preprocess).batch(1)
model = Sequential([
# 第一个卷积层
layers.Conv2D(32, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# layers.Dropout(0.25),
# 第二个卷积层
layers.Conv2D(64, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# layers.Dropout(0.25),
layers.Flatten(),
# 全连接
layers.Dense(128),
layers.Dense(40), # 因为这里我们4个数字,所以也就4*10可能性
layers.Reshape([4,10])
])
model.build(input_shape=[None, 20, 80, 1])
model.summary()
# 设置学习率
optimizer = optimizers.Adam(lr=1e-3)
def train():
global model
# 如果存在模型,就拿以前的继续训练,不用再从头开始
if os.path.exists(model_dir):
model = tf.keras.models.load_model('model.h5', compile=False)
# 进行20次重复训练
for epoch in range(20):
for step, (x, y) in enumerate(train_db):
# 有的时候验证码不是这种格式,就没处理所以就不是的直接过滤
if x.shape == (10, 20, 80, 1):
with tf.GradientTape() as tape:
# logits
logits = model(x)
# 真实值就行one_hot编码来对比
y_onehot = tf.one_hot(y, depth=10)
# 设置loss
loss_ce = tf.losses.MSE(y_onehot, logits)
loss_ce = tf.reduce_mean(loss_ce)
# 不断更新梯度
grads = tape.gradient(loss_ce, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
if step % 10 == 0:
print(epoch, step, 'loss:', float(loss_ce))
# 因为一次就已经很高了,所以直接保存模型
model.save('model.h5')
def test():
model = tf.keras.models.load_model('model.h5', compile=False)
for step, (x, y) in enumerate(test_db):
if x.shape == (1, 20, 80, 1):
logits = model(x)
logits = tf.nn.softmax(logits)
pred = tf.cast(tf.argmax(logits,axis=2),dtype=tf.int32)
print('预测值:',pred[0].numpy(),'真实值:',y[0].numpy(),'是否相同:',int(tf.reduce_sum(tf.cast(tf.equal(pred,y),dtype=tf.int32)))==4)
if __name__ == '__main__':
#判断是否存在模型文件,没有则训练生成
choice_flag = 1 # 0训练 1测试
if os.path.exists(model_dir) and choice_flag==1:
test()
else:
train()
GitHub 加速计划 / te / tensorflow
184.55 K
74.12 K
下载
一个面向所有人的开源机器学习框架
最近提交(Master分支:2 个月前 )
a49e66f2
PiperOrigin-RevId: 663726708
2 个月前
91dac11a
This test overrides disabled_backends, dropping the default
value in the process.
PiperOrigin-RevId: 663711155
2 个月前
更多推荐
已为社区贡献4条内容
所有评论(0)