zoukankan      html  css  js  c++  java
  • django之图形验证码生成

    1、验证码生成代码(注意:代码中引用了三个字体文件,因为必须在该代码文件的同级路径下创建fonts目录,里面需要三个字体文件,路径如下:https://gitee.com/will-smith/dj32_test/tree/master/utils/verify/fonts),还需要安装pillow包。

    import random
    import string
    import os.path
    from io import BytesIO
    
    from PIL import Image
    from PIL import ImageFilter
    from PIL.ImageDraw import Draw
    from PIL.ImageFont import truetype
    
    
    class Bezier:
        def __init__(self):
            self.tsequence = tuple([t / 20.0 for t in range(21)])
            self.beziers = {}
    
        def pascal_row(self, n):
            """ Returns n-th row of Pascal's triangle
            """
            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 make_bezier(self, n):
            """ Bezier curves:
                http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Generalization
            """
            try:
                return self.beziers[n]
            except KeyError:
                combinations = self.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):
            self._bezier = Bezier()
            self._dir = os.path.dirname(__file__)
            # self._captcha_path = os.path.join(self._dir, '..', 'static', 'captcha')
    
        @staticmethod
        def instance():
            if not hasattr(Captcha, "_instance"):
                Captcha._instance = Captcha()
            return Captcha._instance
    
        def initialize(self, width=200, height=75, color=None, text=None, fonts=None):
            # self.image = Image.new('RGB', (width, height), (255, 255, 255))
            self._text = text if text else random.sample(string.ascii_uppercase + string.ascii_uppercase + '3456789', 4)
            self.fonts = fonts if fonts else 
                [os.path.join(self._dir, 'fonts', font) for font in ['Arial.ttf', 'Georgia.ttf', 'actionj.ttf']]
            self.width = width
            self.height = height
            self._color = color if color else self.random_color(0, 200, random.randint(220, 255))
    
        @staticmethod
        def random_color(start, end, opacity=None):
            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
    
        # draw image
    
        def background(self, image):
            Draw(image).rectangle([(0, 0), image.size], fill=self.random_color(238, 255))
            return image
    
        @staticmethod
        def smooth(image):
            return image.filter(ImageFilter.SMOOTH)
    
        def curve(self, image, width=4, number=6, color=None):
            dx, height = image.size
            dx /= number
            path = [(dx * i, random.randint(0, height))
                    for i in range(1, number)]
            bcoefs = self._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(image).line(points, fill=color if color else self._color, width=width)
            return image
    
        def noise(self, image, number=50, level=2, color=None):
            width, height = image.size
            dx = width / 10
            width -= dx
            dy = height / 10
            height -= dy
            draw = Draw(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)
            return image
    
        def text(self, image, 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(image)
            char_images = []
            for c in self._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 = 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)
                image.paste(char_image,
                            (offset, int((height - c_height) / 2)),
                            mask)
                offset += int(c_width * squeeze_factor)
            return image
    
        # draw text
        @staticmethod
        def warp(image, dx_factor=0.27, dy_factor=0.21):
            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))
            image2 = Image.new('RGB',
                               (width + abs(x1) + abs(x2),
                                height + abs(y1) + abs(y2)))
            image2.paste(image, (abs(x1), abs(y1)))
            width2, height2 = image2.size
            return image2.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)
            image2 = Image.new('RGB', (width + dx, height + dy))
            image2.paste(image, (dx, dy))
            return image2
    
        @staticmethod
        def rotate(image, angle=25):
            return image.rotate(
                random.uniform(-angle, angle), Image.BILINEAR, expand=1)
    
        def captcha(self, path=None, fmt='JPEG'):
            """Create a captcha.
    
            Args:
                path: save path, default None.
                fmt: image format, PNG / JPEG.
            Returns:
                A tuple, (text, StringIO.value).
                For example:
                    ('JGW9', 'x89PNG
    x1a
    x00x00x00
    ...')
    
            """
            image = Image.new('RGB', (self.width, self.height), (255, 255, 255))
            image = self.background(image)
            image = self.text(image, self.fonts, drawings=['warp', 'rotate', 'offset'])
            image = self.curve(image)
            image = self.noise(image)
            image = self.smooth(image)
            text = "".join(self._text)
            out = BytesIO()
            image.save(out, format=fmt)
            return text, out.getvalue()
    
        def generate_captcha(self):
            self.initialize()
            return self.captcha("")
    
    captcha = Captcha.instance()

    2、如何生成图片验证码

      如果需要的验证码的尺寸刚好是默认的200x75,那么就直接可以使用

    # 返回验证码和验证码二进制图片元组
    code, imgBytes = captcha.generate_captcha()

      如果需要的验证码的尺寸需要自己设定,那就直接重写generate_captcha方法,再引用上面的代码

    def generate_captcha(self, width, height, *args, **kwargs):
        self.initialize(width, height)
        return self.captcha("")

    3、如何使用图片验证码

    let $img = $(".form-item .captcha-graph-img img");
    generate();
    $img.click(generate);
            
    function generate() {
        let uuid = generateUUID();
        getUuid = uuid;
        let image_code_url = "/image_code/" + uuid + "/";
        $img.attr('src', image_code_url);
    }
    // 生成UUID

    function generateUUID() { let d = new Date().getTime(); if (window.performance && typeof window.performance.now === "function") { d += performance.now(); //use high-precision timer if available } let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { let r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16); }); return uuid; }
    from utils.verify.captcha import captcha
    from django_redis import get_redis_connection
    from django.http import HttpResponse
    
    
    def generate_image_code(request, image_id):
        text, imageBytes = captcha.generate_captcha()
        print(text, imageBytes)
        redis = get_redis_connection("image")
        redis.setex("image_{}".format(image_id), 60, text)
        return HttpResponse(content=imageBytes, content_type="image/jpeg", status=200)
  • 相关阅读:
    topcoder srm 681 div1
    topcoder srm 683 div1
    topcoder srm 684 div1
    topcoder srm 715 div1
    topcoder srm 685 div1
    topcoder srm 687 div1
    topcoder srm 688 div1
    topcoder srm 689 div1
    topcoder srm 686 div1
    topcoder srm 690 div1 -3
  • 原文地址:https://www.cnblogs.com/loveprogramme/p/13211142.html
Copyright © 2011-2022 走看看