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

前言

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

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

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

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

图像预处理的效果如下:

原始图像

灰度图

黑白图

按字符切分后的黑白图像

由于字符宽窄有差异,这里我们按字符切分后,有些字符会多出来一部分,有些字符会丢失一部分。比如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()方法验证我们的理论是否成立,识别效果如下:

获取到的验证码

识别结果

结语

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

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

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

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

(0)

相关推荐

  • python验证码识别教程之灰度处理、二值化、降噪与tesserocr识别

    前言 写爬虫有一个绕不过去的问题就是验证码,现在验证码分类大概有4种: 图像类 滑动类 点击类 语音类 今天先来看看图像类,这类验证码大多是数字.字母的组合,国内也有使用汉字的.在这个基础上增加噪点.干扰线.变形.重叠.不同字体颜色等方法来增加识别难度. 相应的,验证码识别大体可以分为下面几个步骤: 灰度处理 增加对比度(可选) 二值化 降噪 倾斜校正分割字符 建立训练库 识别 由于是实验性质的,文中用到的验证码均为程序生成而不是批量下载真实的网站验证码,这样做的好处就是可以有大量的知道明确结果

  • python验证码识别教程之利用投影法、连通域法分割图片

    前言 今天这篇文章主要记录一下如何切分验证码,用到的主要库就是Pillow和Linux下的图像处理工具GIMP.首先假设一个固定位置和宽度.无粘连.无干扰的例子学习一下如何使用Pillow来切割图片. 使用GIMP打开图片后,按 加号 放大图片,然后点击View->Show Grid来显示网格线: 其中,每个正方形边长为10像素,所以数字1切割坐标为左20.上20.右40.下70.以此类推可以知道剩下3个数字的切割位置. 代码如下: from PIL import Image p = Image

  • python使用tensorflow深度学习识别验证码

    本文介绍了python使用tensorflow深度学习识别验证码 ,分享给大家,具体如下: 除了传统的PIL包处理图片,然后用pytessert+OCR识别意外,还可以使用tessorflow训练来识别验证码. 此篇代码大部分是转载的,只改了很少地方. 代码是运行在linux环境,tessorflow没有支持windows的python 2.7. gen_captcha.py代码. #coding=utf-8 from captcha.image import ImageCaptcha # pi

  • 详解Python验证码识别

    以前写过一个刷校内网的人气的工具,Java的(以后再也不行Java程序了),里面用到了验证码识别,那段代码不是我自己写的:-) 校内的验证是完全单色没有任何干挠的验证码,识别起来比较容易,不过从那段代码中可以看到基本的验证码识别方式.这几天在写一个程序的时候需要识别验证码,因为程序是Python写的自然打算用Python进行验证码的识别. 以前没用Python处理过图像,不太了解PIL(Python Image Library)的用法,这几天看了看PIL,发现它太强大了,简直和ImageMagi

  • 用Python进行简单图像识别(验证码)

    这是一个最简单的图像识别,将图片加载后直接利用Python的一个识别引擎进行识别 将图片中的数字通过 pytesseract.image_to_string(image)识别后将结果存入到本地的txt文件中 #-*-encoding:utf-8-*- import pytesseract from PIL import Image class GetImageDate(object): def m(self): image = Image.open(u"C:\\a.png") text

  • python脚本实现验证码识别

    最近在折腾验证码识别.最终的脚本的识别率在92%左右,9000张验证码大概能识别出八千三四百张左右.好吧,其实是验证码太简单.下面就是要识别的验证码. 我主要用的是Python中的PIL库. 首先进行二值化处理.由于图片中的噪点颜色比较浅,所以可以设定一个阈值直接过滤掉.这里我设置的阈值是150,像素大于150的赋值为1,小于的赋为0. def set_table(a): table = [] for i in range(256): if i < a: table.append(0) else

  • Python验证码识别处理实例

    一.准备工作与代码实例 (1)安装PIL:下载后是一个exe,直接双击安装,它会自动安装到C:\Python27\Lib\site-packages中去, (2)pytesser:下载解压后直接放C:\Python27\Lib\site-packages(根据你安装的Python路径而不同),同时,新建一个pytheeer.pth,内容就写pytesser,注意这里的内容一定要和pytesser这个文件夹同名,意思就是pytesser文件夹,pytesser.pth,及内容都要一样! (3)Te

  • python+selenium识别验证码并登录的示例代码

    由于工作需要,登录网站需要用到验证码.最初是研究过验证码识别的,但是总是不能获取到我需要的那个验证码.直到这周五,才想起这事来,昨天顺利的解决了. 下面正题: python版本:3.4.3 所需要的代码库:PIL,selenium,tesseract 先上代码: #coding:utf-8 import subprocess from PIL import Image from PIL import ImageOps from selenium import webdriver import t

  • python下调用pytesseract识别某网站验证码的实现方法

    一.pytesseract介绍 1.pytesseract说明 pytesseract最新版本0.1.6,网址:https://pypi.python.org/pypi/pytesseract Python-tesseract is a wrapper for google's Tesseract-OCR ( http://code.google.com/p/tesseract-ocr/ ). It is also useful as a stand-alone invocation scrip

  • python验证码识别的实例详解

    其实关于验证码识别涉及很多方面的内容,入手难度大,但是入手后,可拓展性又非常广泛,可玩性极强,成就感也很足,对这感兴趣的朋友们下面跟着小编一起来学习学习吧. 依赖 sudo apt-get install python-imaging sudo apt-get install tesseract-ocr pip install pytesseract 利用google ocr来识别验证码 from PIL import Image import pytesseract image = Image

随机推荐