Python生成验证码图片

  1. 验证码图片

     """
     图片验证码
     """
     import os
     import random
    
     from io import BytesIO
    
     from PIL import Image
     from PIL import ImageFilter
     from PIL.ImageDraw import Draw
     from PIL.ImageFont import truetype
    
    
     class Bezier(object):
         """贝塞尔曲线"""
    
         def __init__(self):
             self.tsequence = tuple([t / 20.0 for t in range(21)])
             self.beziers = {}
    
         def make_bezier(self, n):
             """绘制贝塞尔曲线"""
             try:
                 return self.beziers[n]
             except KeyError:
                 combinations = pascal_row(n - 1)
                 result = []
                 for t in self.tsequence:
                     tpowers = (t ** i for i in range(n))
                     upowers = ((1 - t) ** i for i in range(n - 1, -1, -1))
                     coefs = [c * a * b for c, a, b in zip(combinations,
                                                           tpowers, upowers)]
                     result.append(coefs)
                 self.beziers[n] = result
                 return result
    
    
     class Captcha(object):
         """验证码"""
    
         def __init__(self, width, height, fonts=None, color=None):
             self._image = None
             self._fonts = fonts if fonts else \
                 [os.path.join(os.path.dirname(__file__), 'fonts', font)
                  for font in ['ArialRB.ttf', 'ArialNI.ttf', 'Georgia.ttf', 'Kongxin.ttf']]
             self._color = color if color else random_color(0, 200, random.randint(220, 255))
             self._width, self._height = width, height
    
         @classmethod
         def instance(cls, width=200, height=75):
             prop_name = f'_instance_{width}_{height}'
             if not hasattr(cls, prop_name):
                 setattr(cls, prop_name, cls(width, height))
             return getattr(cls, prop_name)
    
         def background(self):
             """绘制背景"""
             Draw(self._image).rectangle([(0, 0), self._image.size],
                                         fill=random_color(230, 255))
    
         def smooth(self):
             """平滑图像"""
             return self._image.filter(ImageFilter.SMOOTH)
    
         def curve(self, width=4, number=6, color=None):
             """绘制曲线"""
             dx, height = self._image.size
             dx /= number
             path = [(dx * i, random.randint(0, height))
                     for i in range(1, number)]
             bcoefs = Bezier().make_bezier(number - 1)
             points = []
             for coefs in bcoefs:
                 points.append(tuple(sum([coef * p for coef, p in zip(coefs, ps)])
                                     for ps in zip(*path)))
             Draw(self._image).line(points, fill=color if color else self._color, width=width)
    
         def noise(self, number=50, level=2, color=None):
             """绘制扰码"""
             width, height = self._image.size
             dx, dy = width / 10, height / 10
             width, height = width - dx, height - dy
             draw = Draw(self._image)
             for i in range(number):
                 x = int(random.uniform(dx, width))
                 y = int(random.uniform(dy, height))
                 draw.line(((x, y), (x + level, y)),
                           fill=color if color else self._color, width=level)
    
         def text(self, captcha_text, fonts, font_sizes=None, drawings=None, squeeze_factor=0.75, color=None):
             """绘制文本"""
             color = color if color else self._color
             fonts = tuple([truetype(name, size)
                            for name in fonts
                            for size in font_sizes or (65, 70, 75)])
             draw = Draw(self._image)
             char_images = []
             for c in captcha_text:
                 font = random.choice(fonts)
                 c_width, c_height = draw.textsize(c, font=font)
                 char_image = Image.new('RGB', (c_width, c_height), (0, 0, 0))
                 char_draw = Draw(char_image)
                 char_draw.text((0, 0), c, font=font, fill=color)
                 char_image = char_image.crop(char_image.getbbox())
                 for drawing in drawings:
                     d = getattr(self, drawing)
                     char_image = d(char_image)
                 char_images.append(char_image)
             width, height = self._image.size
             offset = int((width - sum(int(i.size[0] * squeeze_factor)
                                       for i in char_images[:-1]) -
                           char_images[-1].size[0]) / 2)
             for char_image in char_images:
                 c_width, c_height = char_image.size
                 mask = char_image.convert('L').point(lambda i: i * 1.97)
                 self._image.paste(char_image,
                             (offset, int((height - c_height) / 2)),
                             mask)
                 offset += int(c_width * squeeze_factor)
    
         @staticmethod
         def warp(image, dx_factor=0.3, dy_factor=0.3):
             """图像扭曲"""
             width, height = image.size
             dx = width * dx_factor
             dy = height * dy_factor
             x1 = int(random.uniform(-dx, dx))
             y1 = int(random.uniform(-dy, dy))
             x2 = int(random.uniform(-dx, dx))
             y2 = int(random.uniform(-dy, dy))
             warp_image = Image.new(
                 'RGB',
                 (width + abs(x1) + abs(x2), height + abs(y1) + abs(y2)))
             warp_image.paste(image, (abs(x1), abs(y1)))
             width2, height2 = warp_image.size
             return warp_image.transform(
                 (width, height),
                 Image.QUAD,
                 (x1, y1, -x1, height2 - y2, width2 + x2, height2 + y2, width2 - x2, -y1))
    
         @staticmethod
         def offset(image, dx_factor=0.1, dy_factor=0.2):
             """图像偏移"""
             width, height = image.size
             dx = int(random.random() * width * dx_factor)
             dy = int(random.random() * height * dy_factor)
             offset_image = Image.new('RGB', (width + dx, height + dy))
             offset_image.paste(image, (dx, dy))
             return offset_image
    
         @staticmethod
         def rotate(image, angle=25):
             """图像旋转"""
             return image.rotate(random.uniform(-angle, angle),
                                 Image.BILINEAR, expand=1)
    
         def generate(self, captcha_text='', fmt='PNG'):
             """生成验证码(文字和图片)"""
             self._image = Image.new('RGB', (self._width, self._height), (255, 255, 255))
             self.background()
             self.text(captcha_text, self._fonts,
                       drawings=['warp', 'rotate', 'offset'])
             self.curve()
             self.noise()
             self.smooth()
             image_bytes = BytesIO()
             self._image.save(image_bytes, format=fmt)
             return image_bytes.getvalue()
    
    
     def pascal_row(n=0):
         """生成Pascal三角第n行"""
         result = [1]
         x, numerator = 1, n
         for denominator in range(1, n // 2 + 1):
             x *= numerator
             x /= denominator
             result.append(x)
             numerator -= 1
         if n & 1 == 0:
             result.extend(reversed(result[:-1]))
         else:
             result.extend(reversed(result))
         return result
    
    
     def random_color(start=0, end=255, opacity=255):
         """获得随机颜色"""
         red = random.randint(start, end)
         green = random.randint(start, end)
         blue = random.randint(start, end)
         if opacity is None:
             return red, green, blue
         return red, green, blue, opacity

    说明:上面的代码在生成验证码图片时用到了三种字体文件,使用上面的代码时需要添加字体文件到应用目录下的fonts目录中。

    下面的视图函数用来生成验证码并通过HttpResponse对象输出到用户浏览器中。下面的视图函数用来生成验证码并通过HttpResponse对象输出到用户浏览器中。

     ALL_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    
    
     def get_captcha_text(length=4):
         selected_chars = random.choices(ALL_CHARS, k=length)
         return ''.join(selected_chars)
    
    
     def get_captcha(request):
         """获得验证码"""
         captcha_text = get_captcha_text()
         image = Captcha.instance().generate(captcha_text)
         return HttpResponse(image, content_type='image/png')

转载于:https://www.cnblogs.com/liuweiqc/p/11138828.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值