数据挖掘(八)

从图像中抽取信息很难,图像包含大量原始数据,图像的标准编码单元像素提供的信息量很少。图像特别是照片可能存在一系列问题,比如模糊不清、离目标太近、光线太亮或太暗、比例失真、残缺、扭曲等,这会增加计算机系统抽取有用信息的难度。本文介绍如何用神经网络识别图像中的字母,从而自动识别验证码CAPTCHA。

人工神经网络

  1. 神经网络由一些列相互连接的神经元组成,每个神经元都是一个简单的函数,接收一定输入,给出相应输出。神经元可以使用任何标准函数来处理数据比如线性函数,这些函数统称为激活函数。一般来说,神经网络学习算法要能正常工作,激活函数应当是可导和光滑的。常用的激活函数由逻辑斯蒂函数,x为神经元的输入,k、L通常为1,这时函数达到最大值。每个神经元接收几个输入,计算输出,这样的一个个神经元连接起来组成了神经网络。

f ( x ) = L 1 + e − k ( x − x 0 ) f(x) = \frac{L}{1 + e^-k(x-x0)} f(x)=1+ek(xx0)L

  1. 用于数据挖掘应用的神经网络,神经元按照层级进行排列。第一层是输入层,接收来子数据集的输入。第一层中的每个神经元对输入进行计算,把得到的结果传递给第二层的神经元,这种叫做前向神经网络。在神经网络中,上一层的输出作为下一层的输入,直到到达最后一层。输出结果表示的是神经网络分类器给出的分类结果。输入层和输出层之间的所有层称为隐含层。
  2. 我们优先考虑使用全连接层,也就是上一层中每个神经元的输出都输入到下一层的所有神经元。实际构建神经网络时,训练过程中,很多权重都会被设置为0,有效地减少边的数量。神经元激活函数通常使用逻辑斯蒂函数,每层神经元之间为全连接,创建和训练神经网络还需要用到其他几个参数。创建过程指定神经网络的规模需要用到两个参数,神经网络共有多少层和隐含层每层有多少个神经元。训练过程中还会用到神经元之间边的权重参数。一个神经元连接到另一个神经元,两者之间的边具有一定的权重,在计算输出时,用边的权重乘以信号的大小。如果边的权重为0.8,神经元激活后,输出值为1,那么下一个神经元从前面这个神经元得到的输入就是0.8。如果第一个神经元没有激活,值为0,那么输出到第二个神经元的值就是0。
  3. 神经网络大小合适并且权重经过充分训练,它的分类效果才能精确。通常开始时使用随机选取的权重,训练过程中再逐步更新。设置好网络大小后,再从训练集中训练得到边的权重参数后,就能构造分类器。然后我们可以使用分类器分类。

创建数据集

我们以破解网站4个字母的英文单词的验证码为例,编写程序还原图像中的单词。步骤是把大图像分成只包含一个字母的4张小图像,为每个字母分类,把字母重新组合为单词,用词典修正单词识别错误。假设验证码中的单词是一个完整的有效的英文单词,长度为4个字母,实际上我们使用同一个词典生成和破解验证码;单词全部字母均为大写形式,不适用符号、数字或空格。为了增加难度,再生成图像时对单词使用不同的错切变化效果。

绘制验证码
  1. 我们编写创建验证码的函数,绘制一张含有单词的图像,对单词使用错切变化效果。绘制图像使用PIL库,错切变化需要使用scikit-image库。
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from skimage import transform as tf
from matplotlib import pyplot as plt
# 生成验证码的基础函数,接收一个单词和错切值(通常在0到0.5之间),还可以指定图像大小
def create_captcha(text, shear=0, size=(100, 24)):
    # 使用字母L生成一张黑白图像,为ImageDraw类初始化一个实例
    im = Image.new('L', size, 'black')
    draw = ImageDraw.Draw(im)
    # 指定验证码文字所使用的字体
    font = ImageFont.truetype(r'C:\Windows\Fonts\Gabriola.ttf', 22)
    draw.text((2, 2), text, fill=1, font=font)
    # 把PIL图像转换为numpy数组
    image = np.array(im)
    # 应用错切变化效果,返回图像
    affine_tf = tf.AffineTransform(shear=shear)
    # 根据给定的坐标变换图像
    image = tf.warp(image, affine_tf)
    # 对图像特征进行归一化处理,确保特征值落在0~1之间
    return image/image.max()

image = create_captcha('JOHN', shear=0.5)
plt.imshow(image, cmap='gray')
plt.show()
  1. 虽然验证码是单词,但是我们可以把大问题转换为识别字母的小的问题,验证码识别算法的下一步是分割单词,找出其中的字母。创建函数寻找图像中连续的黑色像素,抽取他们作为新的小图像,这些小图像就是我们要找的字母。
from skimage.measure import label, regionprops
# 图像分割函数接收图像,返回小图像列表,每张小图像为单词的一个字母
def segment_iamge(image):
    # 检测每个字母的位置,使用label函数能找出图像中像素值相同且由连接在一起的像素块
    # label函数参数是图像数组,返回跟输入同型的数组,在返回的数组中图像连接在一起的区域用不同的值表示,在这些区域以外的像素用0表示
    labeled_image = label(image > 0)
    subimages = []
    # regionprops函数可以抽取连续区域,遍历这些区域分别处理,通过region对象获取到当前区域的相关信息
    for region in regionprops(labeled_image):
	# 用当前区域的起始位置坐标作为索引就能抽取到小图像
        start_x, start_y, end_x, end_y = region.bbox
        subimages.append(image[start_x:end_x, start_y:end_y])
    # 没有找到小图像就把原图像作为子图返回
    if len(subimages) == 0:
        return [image,

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部