python快速生成验证码(随机生成验证码Python)

发布时间:2022-11-16

本文目录一览:

  1. python怎么生成随机图形验证码
  2. python怎么批量提交验证码
  3. 如何利用Python做简单的验证码识别
  4. 请教生成如图验证码的python算法

python怎么生成随机图形验证码

  1. 安装pillow模块
    pip install pillow
    
  2. pillow模块的基本使用
    1. 创建图片
      from PIL import Image
      # 定义使用Image类实例化一个长为400px,宽为400px,基于RGB的(255,255,255)颜色的图片
      img1 = Image.new(mode="RGB", size=(400, 400), color=(255, 255, 255))
      # 把生成的图片保存为"pic.png"格式
      with open("pic.png", "wb") as f:
          img1.save(f, format="png")
      # 显示图片
      img1.show()
      
    2. 创建画笔
      # 创建画笔,用于在图片上生成内容
      draw1 = ImageDraw.Draw(img1, mode="RGB")
      
    3. 在图片上生成点
      # 在(100,100)坐标上生成一个红点,指定的坐标不能超过图片的尺寸
      draw1.point([100, 100], pill="red")
      # 在(80,80)坐标上生成一个黑点,指定的坐标不能超过图片的尺寸
      draw1.point([80, 80], fill=(0, 0, 0))
      
    4. 在图片上画线
      # 第一个括号里面的参数是坐标,前两个数为开始坐标,后两个数为结束坐标
      # 括号里的第二个参数指定颜色,可以直接指定,也可以用RGB来表示颜色
      draw1.line((100, 100, 100, 300), fill="red")
      draw1.line((100, 200, 200, 100), fill="blue")
      
    5. 在图片在画圆
      # 括号里的第一个参数是坐标,前两个数为起始坐标,后两个为结束坐标
      # 用这两个坐标之间的正方形区域生成一个圆,大括号里的第二个参数为圆的开始角度
      # 第三个参数为圆的结束角度,0到360表示所画的是一个完整的圆形,
      # 也可以指定的数字来生成一段为圆弧,最后一个参数表示颜色,也可以用RGB来表示想要的颜色
      draw1.arc((100, 100, 300, 300), 0, 360, fill="red")
      draw1.arc((0, 0, 300, 300), 0, 90, fill="blue")
      
    6. 在图片在写文本
      # 使用画笔的text方法在图片上生成文本
      # 第一个参数为坐标,第二个参数为所有生成的文本的内容
      # 第三个参数为文本的颜色
      draw1.text([0, 0], "python", "blue")
      
    7. 在图片在生成指定字体的文本
      # 先实例化一个字体对象,第一个参数表示字体的路径,第二个参数表示字体大小
      font1 = ImageFont.truetype("One Chance.ttf", 28)
      # 在图片上生成字体
      # 第一个括号里的参数表示坐标,第二个参数表示写入的内容
      # 第三个参数表示颜色,第四个参数表示使用的字体对象
      draw1.text([200, 200], "linux", "red", font=font1)
      
  3. 图片验证码的实例
    # 导入random模块
    import random
    # 导入Image,ImageDraw,ImageFont模块
    from PIL import Image, ImageDraw, ImageFont
    # 定义使用Image类实例化一个长为120px,宽为30px,基于RGB的(255,255,255)颜色的图片
    img1 = Image.new(mode="RGB", size=(120, 30), color=(255, 255, 255))
    # 实例化一支画笔
    draw1 = ImageDraw.Draw(img1, mode="RGB")
    # 定义要使用的字体
    font1 = ImageFont.truetype("One Chance.ttf", 28)
    for i in range(5):
        # 每循环一次,从a到z中随机生成一个字母或数字
        # 65到90为字母的ASCII码,使用chr把生成的ASCII码转换成字符
        # str把生成的数字转换成字符串
        char1 = random.choice([chr(random.randint(65, 90)), str(random.randint(0, 9))])
        # 每循环一次重新生成随机颜色
        color1 = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        # 把生成的字母或数字添加到图片上
        # 图片长度为120px,要生成5个数字或字母则每添加一个,其位置就要向后移动24px
        draw1.text([i * 24, 0], char1, color1, font=font1)
    # 把生成的图片保存为"pic.png"格式
    with open("pic.png", "wb") as f:
        img1.save(f, format="png")
    

python怎么批量提交验证码

现在的网页中,为了防止机器人提交表单,图片验证码是很常见的应对手段之一。这里就不详细介绍了,相信大家都遇到过。 现在就给出用Python的PIL库实现验证码图片的代码。代码中有详细注释。

#!/usr/bin/env python
# coding=utf-8
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大写字母
_numbers = ''.join(map(str, range(3, 10)))  # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
def create_validate_code(size=(120, 30),
                        chars=init_chars,
                        img_type="GIF",
                        mode="RGB",
                        bg_color=(255, 255, 255),
                        fg_color=(0, 0, 255),
                        font_size=18,
                        font_type="ae_AlArabiya.ttf",
                        length=4,
                        draw_lines=True,
                        n_line=(1, 2),
                        draw_points=True,
                        point_chance=2):
    """
    @todo: 生成验证码图片
    @param size: 图片的大小,格式(宽,高),默认为(120, 30)
    @param chars: 允许的字符集合,格式字符串
    @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
    @param mode: 图片模式,默认为RGB
    @param bg_color: 背景颜色,默认为白色
    @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
    @param font_size: 验证码字体大小
    @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
    @param length: 验证码字符个数
    @param draw_lines: 是否划干扰线
    @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
    @param draw_points: 是否画干扰点
    @param point_chance: 干扰点出现的概率,大小范围[0, 100]
    @return: [0]: PIL Image实例
    @return: [1]: 验证码图片中的字符串
    """
    width, height = size  # 宽, 高
    img = Image.new(mode, size, bg_color)  # 创建图形
    draw = ImageDraw.Draw(img)  # 创建画笔
    def get_chars():
        """生成给定长度的字符串,返回列表格式"""
        return random.sample(chars, length)
    def create_lines():
        """绘制干扰线"""
        line_num = random.randint(*n_line)  # 干扰线条数
        for i in range(line_num):
            # 起始点
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            # 结束点
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))
    def create_points():
        """绘制干扰点"""
        chance = min(100, max(0, int(point_chance)))  # 大小限制在[0, 100]
        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))
    def create_strs():
        """绘制验证码字符"""
        c_chars = get_chars()
        strs = ' %s ' % ' '.join(c_chars)  # 每个字符前后以空格隔开
        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)
        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                  strs, font=font, fill=fg_color)
        return ''.join(c_chars)
    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()
    # 图形扭曲参数
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params)  # 创建扭曲
    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 滤镜,边界加强(阈值更大)
    return img, strs
if __name__ == "__main__":
    code_img = create_validate_code()
    code_img[0].save("validate.gif", "GIF")

最后结果返回一个元组,第一个返回值是Image类的实例,第二个参数是图片中的字符串(比较是否正确的作用)。 需要提醒的是,如果在生成ImageFont.truetype实例的时候抛出IOError异常,有可能是运行代码的电脑没有包含指定的字体,需要下载安装。 生成的验证码图片效果: 这时候,细心的同学可能要问,如果每次生成验证码,都要先保存生成的图片,再显示到页面。这么做让人太不能接受了。这个时候,我们需要使用python内置的StringIO模块,它有着类似file对象的行为,但是它操作的是内存文件。于是,我们可以这么写代码:

try:
    import cStringIO as StringIO
except ImportError:
    import StringIO
mstream = StringIO.StringIO()
img = create_validate_code()[0]
img.save(mstream, "GIF")

这样,我们需要输出的图片的时候只要使用“mstream.getvalue()”即可。比如在Django里,我们首先定义这样的url:

from django.conf.urls.defaults import *
urlpatterns = patterns('example.views',
    url(r'^validate/$', 'validate', name='validate'),
)

在views中,我们把正确的字符串保存在session中,这样当用户提交表单的时候,就可以和session中的正确字符串进行比较。

from django.shortcuts import HttpResponse
from validate import create_validate_code
def validate(request):
    mstream = StringIO.StringIO()
    validate_code = create_validate_code()
    img = validate_code[0]
    img.save(mstream, "GIF")
    request.session['validate'] = validate_code[1]
    return HttpResponse(mstream.getvalue(), "image/gif")

如何利用Python做简单的验证码识别

1 摘要

验证码是目前互联网上非常常见也是非常重要的一个事物,充当着很多系统的防火墙功能,但是随着OCR技术的发展,验证码暴露出来的安全问题也越来越严峻。本文介绍了一套字符验证码识别的完整流程,对于验证码安全和OCR识别技术都有一定的借鉴意义。 然后经过了一年的时间,笔者又研究和get到了一种更强大的基于CNN卷积神经网络的直接端到端的验证识别技术(文章不是我的,然后我把源码整理了下,介绍和源码在这里面): 基于python语言的tensorflow的‘端到端’的字符型验证码识别源码整理(github源码分享)

2 关键词

关键词:安全, 字符图片, 验证码识别, OCR, Python, SVM, PIL

3 免责声明

本文研究所用素材来自于某旧Web框架的网站完全对外公开的公共图片资源。 本文只做了该网站对外公开的公共图片资源进行了爬取,并未越权做任何多余操作。 本文在书写相关报告的时候已经隐去漏洞网站的身份信息。 本文作者已经通知网站相关人员此系统漏洞,并积极向新系统转移。 本报告的主要目的也仅是用于OCR交流学习和引起大家对验证安全的警觉。

4 引言

关于验证码的非技术部分的介绍,可以参考以前写的一篇科普类的文章: 互联网安全防火墙(1)--网络验证码的科普 里面对验证码的种类,使用场景,作用,主要的识别技术等等进行了讲解,然而并没有涉及到任何技术内容。本章内容则作为它的技术补充来给出相应的识别的解决方案,让读者对验证码的功能及安全性问题有更深刻的认识。

5 基本工具

要达到本文的目的,只需要简单的编程知识即可,因为现在的机器学习领域的蓬勃发展,已经有很多封装好的开源解决方案来进行机器学习。普通程序员已经不需要了解复杂的数学原理,即可以实现对这些工具的应用了。 主要开发环境:

  • python3.5
  • python SDK版本
  • PIL:图片处理库
  • libsvm:开源的svm机器学习库 关于环境的安装,不是本文的重点,故略去。

6 基本流程

一般情况下,对于字符型验证码的识别流程如下:

  1. 准备原始图片素材
  2. 图片预处理
  3. 图片字符切割
  4. 图片尺寸归一化
  5. 图片字符标记
  6. 字符图片特征提取
  7. 生成特征和标记对应的训练数据集
  8. 训练特征标记数据生成识别模型
  9. 使用识别模型预测新的未知图片集
  10. 达到根据“图片”就能返回识别正确的字符集的目标

7 素材准备

7.1 素材选择

由于本文是以初级的学习研究目的为主,要求“有代表性,但又不会太难”,所以就直接在网上找个比较有代表性的简单的字符型验证码(感觉像在找漏洞一样)。 最后在一个比较旧的网站(估计是几十年前的网站框架)找到了这个验证码图片。 原始图: 放大清晰图: 此图片能满足要求,仔细观察其具有如下特点。 有利识别的特点:

  • 由纯阿拉伯数字组成
  • 字数为4位
  • 字符排列有规律
  • 字体是用的统一字体 以上就是本文所说的此验证码简单的重要原因,后续代码实现中会用到 不利识别的特点:
  • 图片背景有干扰噪点 这虽然是不利特点,但是这个干扰门槛太低,只需要简单的方法就可以除去

7.2 素材获取

由于在做训练的时候,需要大量的素材,所以不可能用手工的方式一张张在浏览器中保存,故建议写个自动化下载的程序。 主要步骤如下:

  1. 通过浏览器的抓包功能获取随机图片验证码生成接口
  2. 批量请求接口以获取图片
  3. 将图片保存到本地磁盘目录中 这些都是一些IT基本技能,本文就不再详细展开了。 关于网络请求和文件保存的代码,如下:
def downloads_pic(**kwargs):
    pic_name = kwargs.get('pic_name', None)
    url = 'httand_code_captcha/'
    res = requests.get(url, stream=True)
    with open(pic_path + pic_name + '.bmp', 'wb') as f:
        for chunk in res.iter_content(chunk_size=1024):
            if chunk:  # filter out keep-alive new chunks
                f.write(chunk)
                f.flush()
    f.close()

循环执行N次,即可保存N张验证素材了。 下面是收集的几十张素材库保存到本地文件的效果图:

8 图片预处理

虽然目前的机器学习算法已经相当先进了,但是为了减少后面训练时的复杂度,同时增加识别率,很有必要对图片进行预处理,使其对机器识别更友好。 针对以上原始素材的处理步骤如下:

  1. 读取原始图片素材
  2. 将彩色图片二值化为黑白图片
  3. 去除背景噪点

8.1 二值化图片

主要步骤如下:

  1. 将RGB彩图转为灰度图
  2. 将灰度图按照设定阈值转化为二值图
image = Image.open(img_path)
imgry = image.convert('L')  # 转化为灰度图
table = get_bin_table()
out = imgry.point(table, '1')

上面引用到的二值函数的定义如下:

def get_bin_table(threshold=140):
    """
    获取灰度转二值的映射table
    :param threshold:
    :return:
    """
    table = []
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)
    return table

由PIL转化后变成二值图片:0表示黑色,1表示白色。二值化后带噪点的 6937 的像素点输出后如下图:

1111000111111000111111100001111100000011
1110111011110111011111011110111100110111
1001110011110111101011011010101101110111
1101111111110110101111110101111111101111
1101000111110111001111110011111111101111
1100111011111000001111111001011111011111
1101110001111111101011010110111111011111
1101111011111111101111011110111111011111
1101111011110111001111011110111111011100
1110000111111000011101100001110111011111

如果你是近视眼,然后离屏幕远一点,可以隐约看到 6937 的骨架了。

8.2 去除噪点

在转化为二值图片后,就需要清除噪点。本文选择的素材比较简单,大部分噪点也是最简单的那种孤立点,所以可以通过检测这些孤立点就能移除大量的噪点。 关于如何去除更复杂的噪点甚至干扰线和色块,有比较成熟的算法:洪水填充法 Flood Fill,后面有兴趣的时间可以继续研究一下。 本文为了问题简单化,干脆就用一种简单的自己想的简单办法来解决掉这个问题:

  1. 对某个黑点周边的九宫格里面的黑色点计数
  2. 如果黑色点少于2个则证明此点为孤立点,然后得到所有的孤立点
  3. 对所有孤立点一次批量移除。 下面将详细介绍关于具体的算法原理。 将所有的像素点如下图分成三大类:
  • 顶点A
  • 非顶点的边界点B
  • 内部点C 种类点示意图如下: 其中:
  • A类点计算周边相邻的3个点(如上图红框所示)
  • B类点计算周边相邻的5个点(如上图红框所示)
  • C类点计算周边相邻的8个点(如上图红框所示) 当然,由于基准点在计算区域的方向不同,A类点和B类点还会有细分:
  • A类点继续细分为:左上,左下,右上,右下
  • B类点继续细分为:上,下,左,右
  • C类点不用细分 然后这些细分点将成为后续坐标获取的准则。 主要算法的python实现如下:
def sum_9_region(img, x, y):
    """
    9邻域框,以当前点为中心的田字框,黑点个数
    :param x:
    :param y:
    :return:
    """
    # todo 判断图片的长宽度下限
    cur_pixel = img.getpixel((x, y))  # 当前像素点的值
    width = img.width
    height = img.height
    if cur_pixel == 1:  # 如果当前点为白色区域,则不统计邻域值
        return 0
    if y == 0:  # 第一行
        if x == 0:  # 左上顶点,4邻域
            # 中心点旁边3个点
            sum = cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x + 1, y + 1))
            return 4 - sum
        elif x == width - 1:  # 右上顶点
            sum = cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x - 1, y)) \
                  + img.getpixel((x - 1, y + 1))
            return 4 - sum
        else:  # 最上非顶点,6邻域
            sum = img.getpixel((x - 1, y)) \
                  + img.getpixel((x - 1, y + 1)) \
                  + cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x + 1, y + 1))
            return 6 - sum
    elif y == height - 1:  # 最下面一行
        if x == 0:  # 左下顶点
            # 中心点旁边3个点
            sum = cur_pixel \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x + 1, y - 1)) \
                  + img.getpixel((x, y - 1))
            return 4 - sum
        elif x == width - 1:  # 右下顶点
            sum = cur_pixel \
                  + img.getpixel((x, y - 1)) \
                  + img.getpixel((x - 1, y)) \
                  + img.getpixel((x - 1, y - 1))
            return 4 - sum
        else:  # 最下非顶点,6邻域
            sum = cur_pixel \
                  + img.getpixel((x - 1, y)) \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x, y - 1)) \
                  + img.getpixel((x - 1, y - 1)) \
                  + img.getpixel((x + 1, y - 1))
            return 6 - sum
    else:  # y不在边界
        if x == 0:  # 左边非顶点
            sum = img.getpixel((x, y - 1)) \
                  + cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x + 1, y - 1)) \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x + 1, y + 1))
            return 6 - sum
        elif x == width - 1:  # 右边非顶点
            # print('%s,%s' % (x, y))
            sum = img.getpixel((x, y - 1)) \
                  + cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x - 1, y - 1)) \
                  + img.getpixel((x - 1, y)) \
                  + img.getpixel((x - 1, y + 1))
            return 6 - sum
        else:  # 具备9领域条件的
            sum = img.getpixel((x - 1, y - 1)) \
                  + img.getpixel((x - 1, y)) \
                  + img.getpixel((x - 1, y + 1)) \
                  + img.getpixel((x, y - 1)) \
                  + cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x + 1, y - 1)) \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x + 1, y + 1))
            return 9 - sum

Tips: 这个地方是相当考验人的细心和耐心程度了,这个地方的工作量还是蛮大的,花了半个晚上的时间才完成的。 计算好每个像素点的周边像素黑点(注意:PIL转化的图片黑点的值为0)个数后,只需要筛选出个数为 1或者2 的点的坐标即为 孤立点。这个判断方法可能不太准确,但是基本上能够满足本文的需求了。 经过预处理后的图片如下所示: 对比文章开头的原始图片,那些孤立点都被移除掉,相对比较干净的验证码图片已经生成。

9 图片字符切割

由于字符型验证码图片本质就可以看着是由一系列的单个字符图片拼接而成,为了简化研究对象,我们也可以将这些图片分解到原子级,即:只包含单个字符的图片。 于是,我们的研究对象由“N种字串的组合对象”变成“10种阿拉伯数字”的处理,极大的简化和减少了处理对象。

9.1 分割算法

现实生活中的字符验证码的产生千奇百怪,有各种扭曲和变形。关于字符分割的算法,也没有很通用的方式。这个算法也是需要开发人员仔细研究所要识别的字符图片的特点来制定的。 当然,本文所选的研究对象尽量简化了这个步骤的难度,下文将慢慢进行介绍。 使用图像编辑软件(PhoneShop或者其它)打开验证码图片,放大到像素级别,观察其它一些参数特点: 可以得到如下参数:

  • 整个图片尺寸是 40*10
  • 单个字符尺寸是 6*10
  • 左右字符和左右边缘相距2个像素
  • 字符上下紧挨边缘(即相距0个像素) 这样就可以很容易就定位到每个字符在整个图片中占据的像素区域,然后就可以进行分割了,具体代码如下:
def get_crop_imgs(img):
    """
    按照图片的特点,进行切割,这个要根据具体的验证码来进行工作. # 见原理图
    :param img:
    :return:
    """
    child_img_list = []
    for i in range(4):
        x = 2 + i * (6 + 4)  # 见原理图
        y = 0
        child_img = img.crop((x, y, x + 6, y + 10))
        child_img_list.append(child_img)
    return child_img_list

然后就能得到被切割的原子级的图片元素了:

9.2 内容小结

基于本部分的内容的讨论,相信大家已经了解到了,如果验证码的干扰(扭曲,噪点,干扰色块,干扰线……)做得不够强的话,可以得到如下两个结论:

  • 4位字符和40000位字符的验证码区别不大
  • 纯字母
    • 不区分大小写。分类数为26
    • 区分大小写。分类数为52
  • 纯数字。分类数为10
  • 数字和区分大小写的字母组合。分类数为62
  • 纯数字 和 数字及字母组合 的验证码区别不大 在没有形成指数级或者几何级的难度增加,而只是线性有限级增加计算量时,意义不太大。

10 尺寸归一

本文所选择的研究对象本身尺寸就是统一状态:6*10的规格,所以此部分不需要额外处理。但是一些进行了扭曲和缩放的验证码,则此部分也会是一个图像处理的难点。

11 模型训练步骤

在前面的环节,已经完成了对单个图片的处理和分割了。后面就开始进行识别模型的训练了。 整个训练过程如下:

  1. 大量完成预处理并切割到原子级的图片素材准备
  2. 对素材图片进行人为分类,即:打标签
  3. 定义单张图片的识别特征
  4. 使用SVM训练模型对打了标签的特征文件进行训练,得到模型文件

12 素材准备

本文在训练阶段重新下载了同一模式的4数字的验证图片总计:3000张。然后对这3000张图片进行处理和切割,得到12000张原子级图片。 在这12000张图片中删除一些会影响训练和识别的强干扰的干扰素材,切割后的效果图如下:

13 素材标记

由于本文使用的这种识别方法中,机器在最开始是不具备任何数字的观念的。所以需要人为的对素材进行标识,告诉机器什么样的图片的内容是 1……。 这个过程叫做“标记”。 具体打标签的方法是:

  1. 为0~9每个数字建立一个目录,目录名称为相应数字(相当于标签)
  2. 人为判定图片内容,并将图片拖到指定数字目录中
  3. 每个目录中存放100张左右的素材 一般情况下,标记的素材越多,那么训练出的模型的分辨能力和预测能力越强。例如本文中,标记素材为十多张的时候,对新的测试图片识别率基本为零,但是到达100张时,则可以达到近乎100%的识别率

14 特征选择

对于切割后的单个字符图片,像素级放大图如下: 从宏观上看,不同的数字图片的本质就是将黑色按照一定规则填充在相应的像素点上,所以这些特征都是最后围绕像素点进行。 字符图片宽6个像素,高10个像素,理论上可以最简单粗暴地可以定义出60个特征:60个像素点上面的像素值。但是显然这样高维度必然会造成过大的计算量,可以适当的降维。 通过查阅相应的文献[2],给出另外一种简单粗暴的特征定义:

  • 每行上黑色像素的个数,可以得到10个特征
  • 每列上黑色像素的个数,可以得到6个特征 最后得到16维的一组特征,实现代码如下:
def get_feature(img):
    """
    获取指定图片的特征值,
    1. 按照每排的像素点,高度为10,则有10个维度,然后为6列,总共16个维度
    :param img_path:
    :return:一个维度为10(高度)的列表
    """
    width, height = img.size
    pixel_cnt_list = []
    height = 10
    for y in range(height):
        pix_cnt_x = 0
        for x in range(width):
            if img.getpixel((x, y)) == 0:  # 黑色点
                pix_cnt_x += 1
        pixel_cnt_list.append(pix_cnt_x)
    for x in range(width):
        pix_cnt_y = 0
        for y in range(height):
            if img.getpixel((x, y)) == 0:  # 黑色点
                pix_cnt_y += 1
        pixel_cnt_list.append(pix_cnt_y)
    return pixel_cnt_list

然后就将图片素材特征化,按照libSVM指定的格式生成一组带特征值和标记值的向量文

请教生成如图验证码的python算法

def gene_text():
    source = list(string.letters)
    for index in range(0, 10):
        source.append(str(index))
    return ''.join(random.sample(source, number))  # number是生成验证码的位数
def gene_code():
    width, height = size  # 宽和高
    image = Image.new('RGBA', (width, height), bgcolor)  # 创建图片
    font = ImageFont.truetype(font_path, 25)  # 验证码的字体和字体大小
    draw = ImageDraw.Draw(image)  # 创建画笔
    text = gene_text()  # 生成字符串
    font_width, font_height = font.getsize(text)
    draw.text(((width - font_width) / number, (height - font_height) / number), text,
              font=font, fill=fontcolor)  # 填充字符串
def gene_line(draw, width, height):
    begin = (random.randint(0, width), random.randint(0, height))
    end = (random.randint(0, width), random.randint(0, height))
    draw.line([begin, end], fill=linecolor)
image = image.transform((width + 20, height + 10), Image.AFFINE, (1, -0.3, 0, -0.1, 1, 0), Image.BILINEAR)  # 创建扭曲
image = image.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 滤镜,边界加强
image.save('idencode.png')  # 保存验证码图片