[机器视觉]使用python自动识别验证码详解


Posted in Python onMay 16, 2019

前言

CAPTCHA全称Completely Automated Public Turing Test to Tell Computers and Humans Apart,即全自动区分人机的图灵测试。这也是验证码诞生的主要任务。但是随着近年来大数据运算和机器视觉的发展,用机器视觉识别图像已经变得非常容易,过去用于区分人机的验证码也开始变得不再安全。

接下来就让我们从零开始,深入图像处理和算法构建,来看看使用机器视觉来识别过时的验证码( 如下所示 )究竟可以有多简单。

[机器视觉]使用python自动识别验证码详解

载入需要的程序包 & 设置全局变量

import requests
import time
from io import BytesIO
from PIL import Image
import os
import numpy as np

# 获取验证码的网址
CAPT_URL = "http://xxxxxxxxxxxx.cn/servlet/ImageServlet"

# 验证码的保存路径
CAPT_PATH = "capt/"
if not os.path.exists(CAPT_PATH):
  os.mkdir(CAPT_PATH)

# 将验证码转为灰度图时用到的"lookup table"
THRESHOLD = 165
LUT = [0]*THRESHOLD + [1]*(256 - THRESHOLD)

从网站获取验证码

capt_fetch()方法非常简单,我们直接从网站获取验证码,将其转换为Image对象,等待被训练和测试等环节调用。

def capt_fetch():
  """
  从网站获取验证码,将验证码转为Image对象

  :require requests: import requests
  :require time: import time
  :require BytesIO: from io import BytesIO
  :require Image: from PIL import Image

  :param:
  :return capt: 一个Image对象
  """
  # 从网站获取验证码
  capt_raw = requests.get(CAPT_URL)

  # 将二进制的验证码图片写入IO流
  f = BytesIO(capt_raw.content)

  # 将验证码转换为Image对象
  capt = Image.open(f)

  return capt

保存验证码到本地

  1. 一个强大的机器学习模型,是离不开强大的训练集作支持的。这里我们也必须先有一个预先打好标签(预分类)的验证码图片集,才能开始训练模型。
  2. capt_download()方法就是我们用来建立训练图像集的方法。它会调用capt_fetch()方法,将获得的Image对象展示给用户,等待用户输入验证码中的字符,然后将图片命名为用户输入的字符存储起来。
  3. 当然,为了避免文件名重复(比如获取到了两张字符完全相同的验证码),capt_download()方法将系统时间也加入到了文件名中。
def capt_download():
  """
  将Image类型的验证码对象保存到本地

  :require Image: from PIL import Image
  :require os: import os

  :require capt_fetch(): 从nbsc网站获取验证码
  :require CAPT_PATH: 验证码保存路径

  :param:
  :return: 
  """
  capt = capt_fetch()
  capt.show()

  text = raw_input("请输入验证码中的字符:")
  suffix = str(int(time.time() * 1e3))

  capt.save(CAPT_PATH + text + "_" + suffix + ".jpg")

 图像预处理

  1. capt_process()方法会先将验证码转为灰度图,然后再根据全局变量中定义的LUT将灰度图转化为黑白图片。并按照验证码中四个字符所在的位置进行切割。
  2. 从彩色图片到灰度图,再到黑白图,看似验证码中的信息损失了很多,实际上这样做的目的是为了使字符的特征更加明显。
  3. 其实我们最终得到的黑白图像会有一些噪点存在,这主要是由于前景色与背景色不存在严格的区分度,我们可以使用滤波器过滤掉这些噪点,但少量的噪点会被训练模型当作误差处理,并不影响我们分类。至于过滤噪点的方法,我会专门写一篇帖子。
def capt_process(capt):
  """
  图像预处理:将验证码图片转为二值型图片,按字符切割

  :require Image: from PIL import Image
  :require LUT: A lookup table, 包含256个值

  :param capt: 验证码Image对象
  :return capt_per_char_list: 一个数组包含四个元素,每个元素是一张包含单个字符的二值型图片
  """
  capt_gray = capt.convert("L")
  capt_bw = capt_gray.point(LUT, "1")

  capt_per_char_list = []
  for i in range(4):
    x = 5 + i * 15
    y = 2
    capt_per_char = capt_bw.crop((x, y, x + 15, y + 18))
    capt_per_char_list.append(capt_per_char)

  return capt_per_char_list

图像预处理的效果如下:

[机器视觉]使用python自动识别验证码详解

原始图像

[机器视觉]使用python自动识别验证码详解

灰度图

[机器视觉]使用python自动识别验证码详解

黑白图

[机器视觉]使用python自动识别验证码详解

按字符切分后的黑白图像

由于字符宽窄有差异,这里我们按字符切分后,有些字符会多出来一部分,有些字符会丢失一部分。比如7多了一笔看起来像个三角形,M少了一竖看起来像N。但只要符号之间有区分度,依然能够准确分类。

提取图像中的特征值

  1. 到了这一步,我们得到的图像都是由单个字符组成的黑白图片(0为黑色像素点,1为白色像素点)。此时,如果我们把图片转为数组,就会得到一个由0,1组成的矩阵,其长宽恰与图片的大小相同, 每一个数字代表一个像素点。
  2. 接下来我们需要考虑如何提取能够区分不同字符的特征值。我们可以直接用图像中的每一个像素点作为一个特征值,也可以汇总图像中共有多少黑色像素点(当然,这样每张图片只能提取一个特征值),还可以按区域汇总图像中的像素点,比如先将图片四等分,汇总四张“子图片”中的像素点,如果觉得特征不够多,还可以继续下分,直至精确到每一个像素点。
  3. 为了使代码更加简洁,我们这里直接汇总、分行列汇总了图像像素点的个数,共提取了1张图片 + 15列 + 18行 ==> 34个特征值。至于按区域汇总的方法,还是等我们有空了单独写一篇帖子。
def capt_inference(capt_per_char):
  """
  提取图像特征

  :require numpy: import numpy as np

  :param capt_per_char: 由单个字符组成的二值型图片
  :return char_features:一个数组,包含 capt_per_char中字符的特征
  """
  char_array = np.array(capt_per_char)

  total_pixels = np.sum(char_array)
  cols_pixels = np.sum(char_array, 0)
  rows_pixels = np.sum(char_array, 1)

  char_features = np.append(cols_pixels, rows_pixels)
  char_features = np.append(total_pixels, char_features)

  return char_features.tolist()

生成训练集

这里我们会将预分类的每张验证码分别读入内存,从它们的图像中提取特征值,从它们的名称中提取验证码所对应的文字(标签),并将特征值与标签分别汇总成列表,注意train_labels中的每个元素是与train_table中的每一行相对应的。

def train():
  """
  将预分类的验证码图片集转化为字符特征训练集

  :require Image: from PIL import Image
  :require os: import os

  :require capt_process(): 图像预处理
  :require capt_inference(): 提取图像特征

  :param:
  :return train_table: 验证码字符特征训练集
  :return train_labels: 验证码字符预分类结果
  """
  files = os.listdir(CAPT_PATH)

  train_table = []
  train_labels = []

  for f in files:
    train_labels += list(f.split("_")[0])

    capt = Image.open(CAPT_PATH + f)
    capt_per_char_list = capt_process(capt)
    for capt_per_char in capt_per_char_list:
      char_features = capt_inference(capt_per_char)
      train_table.append(char_features)

  return train_table, train_labels

定义分类模型

  1. 只要我们提取的特征值具有足够的区分度(能够区分不同字符),理论上我们可以使用任何机器学习的模型建立特征值与标签的相关。
  2. 我尝试过使用knn,svm,Decision Tree,ANN等各种主流机器学习模型对提取的训练数据进行分类,尽管不同模型表现各异,但识别准确率都可以很轻松的达到90%以上。
  3. 这里我们不打算使用复杂的第三方模型完成学习过程,所以我们在这里自己写了一个分类模型。模型实现的nnc算法是knn的一种,通过比对未知分类的一组特征值测试集与那一组已知分类的特征值训练集最接近,判定测试集的分类情况应该和与其最接近的训练集的分类情况训练集标签相同。
  4. 当然,我们也可以稍加改造直接实现knn算法,找到3组、5组或k组与未知分类的特征向量最接近的训练集中的特征向量,并通过票选与其对应的标签,预测未知分类的特征向量在大概率上应该属于哪一类。
def nnc(train_table, test_vec, train_labels):
  """
  Nearest Neighbour Classification(近邻分类法),
  根据已知特征矩阵的分类情况,预测未分类的特征向量所属类别

  :require numpy: import numpy as np

  :param train_table: 预分类的特征矩阵
  :param test_vec: 特征向量, 长度必须与矩阵的列数相等
  :param labels: 特征矩阵的类别向量
  :return : 预测特征向量所属的类别 
  """

  dist_mat = np.square(np.subtract(train_table, test_vec))
  dist_vec = np.sum(dist_mat, axis = 1)
  pos = np.argmin(dist_vec)

  return train_labels[pos]

 测试模型分类效果

最后,我们需要测试我们的理论是否有效,通过调用test()方法,我们会先从网站获取验证码图像,对图像进行处理、特征提取,然后调用nnc()方法对提取到的四组特征值做近邻分类,分别得到验证码中的四个字符。最后将验证码图像和识别到的字符传出,方便我们比对识别结果。

def test():
  """
  测试模型分类效果

  :require Image: from PIL import Image

  :require capt_fetch(): 从nbsc网站获取验证码
  :require capt_process(): 图像预处理
  :require capt_inference(): 提取图像特征
  :train_table, train_labels: train_table, train_labels = train()

  :param:
  :return capt: 验证码图片
  :return test_labels: 验证码识别结果
  """

  test_labels = []

  capt = capt_fetch()
  capt_per_char_list = capt_process(capt)
  for capt_per_char in capt_per_char_list:
    char_features = capt_inference(capt_per_char)
    label = nnc(train_table, char_features, train_labels)
    test_labels.append(label)

  test_labels = "".join(test_labels)

  return capt, test_labels

训练数据,识别验证码

  1. 方法具备,接下来就是我们实践的环节了。
  2. 首先,我们需要建立一个机器学习库,即一个预分类的验证码图片集。这里我们仅仅获取了120张验证码作为训练集,相比tensorflow动辄成千上万次的迭代,我们建立模型所需的样本量非常之少。当然,这也要感谢我们使用的nnc算法并不需要十分庞大的训练集支持,才使得我们能够节省很多预分类时人工识别验证码的精力。
  3. 接下来,我们会调用train()方法生成训练集和训练集标签,这两个数组会被test()方法用到,但我们把这两个数组存储在全局变量里,所以不需要特意传递给test()方法。
# 下载120张图片到本地
for i in range(120):
  capt_download()

# 模型的训练与测试
train_table, train_labels = train()
test_capt, test_labels = test()

最后我们调用test()方法验证我们的理论是否成立,识别效果如下:

获取到的验证码

[机器视觉]使用python自动识别验证码详解

识别结果

[机器视觉]使用python自动识别验证码详解

结语

至此,我们通过机器视觉识别验证码的任务算是完成了。至于正确率,大概每10张验证码,40各字符中会预测失误一个字符。这已经比较接近我们人类的识别准确率了,当然,我们还可以通过建立起更庞大的学习库,使用knn或更复杂的模型,使用卷积核处理图片等方式,使识别准确率更高。

当然,我们这里所用的方法,只适用于识别比较简单的验证码。对于更加复杂的验证码(如下图),以上方法是不起作用的,但这并不代表这样的验证码不能通过机器视觉进行识别。

[机器视觉]使用python自动识别验证码详解

我们已经看到,随着机器视觉的发展,通过传统的验证码来区分人机已经越来越难了。当然,为了网络的安全性,我们也可使用更复杂的验证码,或者新型的验证方式,比如拖动滑块、短信验证、扫码登陆等。

以上所述是小编给大家介绍的python自动识别验证码详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
跟老齐学Python之编写类之一创建实例
Oct 11 Python
github配置使用指南
Nov 18 Python
Python实现获取某天是某个月中的第几周
Feb 11 Python
Python进程通信之匿名管道实例讲解
Apr 11 Python
Python的净值数据接口调用示例分享
Mar 15 Python
Python算术运算符实例详解
May 31 Python
利用python解决mysql视图导入导出依赖的问题
Dec 17 Python
Django实现表单验证
Sep 08 Python
selenium设置proxy、headers的方法(phantomjs、Chrome、Firefox)
Nov 29 Python
Django中的用户身份验证示例详解
Aug 07 Python
100行Python代码实现每天不同时间段定时给女友发消息
Sep 27 Python
Python小程序之在图片上加入数字的代码
Nov 26 Python
Python redis操作实例分析【连接、管道、发布和订阅等】
May 16 #Python
Python操作redis实例小结【String、Hash、List、Set等】
May 16 #Python
Python 实现数据结构中的的栈队列
May 16 #Python
Python 一键制作微信好友图片墙的方法
May 16 #Python
Python实现京东秒杀功能代码
May 16 #Python
使用Python进行体育竞技分析(预测球队成绩)
May 16 #Python
Python异步操作MySQL示例【使用aiomysql】
May 16 #Python
You might like
PHP实现广度优先搜索算法(BFS,Broad First Search)详解
2017/09/16 PHP
PHP实现打包zip并下载功能
2018/06/12 PHP
js精度溢出解决方案
2012/12/02 Javascript
DIV始终居中的js代码
2014/02/17 Javascript
jquery css 设置table的奇偶行背景色示例
2014/06/03 Javascript
jQuery实现点击该行即可删除HTML表格行
2014/10/17 Javascript
JS模拟bootstrap下拉菜单效果实例
2016/06/17 Javascript
AngularJS中directive指令使用之事件绑定与指令交互用法示例
2016/11/22 Javascript
jQuery简介_动力节点Java学院整理
2017/07/04 jQuery
webpack+react+antd脚手架优化的方法
2018/04/02 Javascript
opencv 识别微信登录验证滑动块位置
2018/08/07 Javascript
解决Vue中引入swiper,在数据渲染的时候,发生不滑动的问题
2018/09/27 Javascript
JavaScript对象拷贝与赋值操作实例分析
2018/12/10 Javascript
基于Vue插入视频的2种方法小结
2019/04/02 Javascript
微信小程序收货地址API兼容低版本解决方法
2019/05/18 Javascript
基于vue的tab-list类目切换商品列表组件的示例代码
2020/02/14 Javascript
Python 用户登录验证的小例子
2013/03/06 Python
使用httplib模块来制作Python下HTTP客户端的方法
2015/06/19 Python
wxpython中自定义事件的实现与使用方法分析
2016/07/21 Python
Python制作简易注册登录系统
2016/12/15 Python
mac下给python3安装requests库和scrapy库的实例
2018/06/13 Python
在Pycharm中项目解释器与环境变量的设置方法
2018/10/29 Python
python实现图片转字符小工具
2019/04/30 Python
python连接mongodb集群方法详解
2020/02/13 Python
Pandas时间序列基础详解(转换,索引,切片)
2020/02/26 Python
Python爬虫实现vip电影下载的示例代码
2020/04/20 Python
python中pdb模块实例用法
2021/01/15 Python
FC-Moto瑞典:欧洲最大的摩托车服装和头盔商店之一
2018/11/27 全球购物
MAC Cosmetics巴西官方网站:M·A·C彩妆
2019/04/18 全球购物
Fanatics法国官网:美国体育电商
2019/08/27 全球购物
学习党的群众路线教育实践活动心得体会范文
2014/11/03 职场文书
单位综合评价意见
2015/06/05 职场文书
九年级语文教学反思
2016/03/03 职场文书
PySwarms(Python粒子群优化工具包)的使用:GlobalBestPSO例子解析
2021/04/05 Python
MySQL中存储时间的最佳实践指南
2021/07/01 MySQL
Mysql数据库group by原理详解
2022/07/07 MySQL