zoukankan      html  css  js  c++  java
  • PIL应用之生成验证码图片

    PIL应用之生成验证码图片 - 残阳似血的博客

    PIL应用之生成验证码图片

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

    现在就给出用Python的PIL库实现验证码图片的代码。代码中有详细注释。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    #!/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 xrange(width):
                for h in xrange(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.save("validate.gif", "GIF")

    最后结果返回一个元组,第一个返回值是Image类的实例,第二个参数是图片中的字符串(比较是否正确的作用)。

    需要提醒的是,如果在生成ImageFont.truetype实例的时候抛出IOError异常,有可能是运行代码的电脑没有包含指定的字体,需要下载安装。

    生成的验证码图片效果:

    validate

    这时候,细心的同学可能要问,如果每次生成验证码,都要先保存生成的图片,再显示到页面。这么做让人太不能接受了。这个时候,我们需要使用python内置的StringIO模块,它有着类似file对象的行为,但是它操作的是内存文件。于是,我们可以这么写代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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:

    1
    2
    3
    4
    5
    from django.conf.urls.defaults import *
     
    urlpatterns = patterns('example.views',
        url(r'^validate/ , 'validate', name='validate'),
    )

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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")
  • 相关阅读:
    Java UDP通信简单实现
    为什么要阅读——兼分享《首先,打破一切常规》[中译文]:世界顶级管理者的成功秘诀/(美)马库斯·白金汉,(美)柯特·科夫曼 著
    怎样提高团队管理能力9
    POJ2777 Count Color 线段树区间更新
    Swift和Objective-C混合编程——Swift调用OC
    LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
    ZZUOJ-1195-OS Job Scheduling(郑州大学第七届ACM大学生程序设计竞赛E题)
    CentOS出错You don't have permission to access on this server
    string 和 vector 初探
    ICMP报文类型
  • 原文地址:https://www.cnblogs.com/lexus/p/2862073.html
Copyright © 2011-2022 走看看