制作图形验证码

作者:    12345 管理员

标签: Python 《警示钟》 建议/贡献

创建日期:2024年12月3日 18:25

浏览量:22

制作图形验证码

import random
from PIL import Image, ImageDraw, ImageFont


class Captcha():

    def __init__(self, width, height, mode=None, color=None):

        """
        初始化--> 画文本--> (画干扰效果)--> 显示
        :param mode: mode-->默认RGBA
        :param width: 验证码的宽
        :param height: 验证码的高
        :param color: (num, num, num)
        """
        self.mode = mode
        if mode:    # 默认RGBA
            self.mode = mode

        else:
            self.mode = 'RGBA'
        self.width = width
        self.height = height
        self.color = None
        if color:   # 默认(255, 255, 255)
            self._color = color

        else:
            self._color = (255, 255, 255)

        # 定义使用Image类实例化一个图片
        self.img = Image.new(mode=self.mode, size=(self.width, self.height), color=self._color)

    def draw_out(self,_show=True,path="captcha.png"):
        # 把生成的图片保存为"pic.png"格式
        with open(path, 'wb') as f:
            self.img.save(f, format='png')
        if _show:
            # 显示图片
            self.img.show()

    # 画文本
    def draw_text(self, font_path, num, number=True, a_font=False, A_font=False):
        """
        若要添加字样,则设为true,数字默认开启
        :param font_path: 字样文件
        :param num: 验证码字符个数
        :param number: 是否开启数字
        :param a_font: 是否开启小写字母
        :param A_font: 是否开启大写字母
        :return: None
        """
        self.img = Image.new(mode=self.mode, size=(self.width, self.height), color=self._color)
        draw = ImageDraw.Draw(self.img, mode=self.mode)

        # 定义字体
        font1 = font_path
        font = ImageFont.truetype(font1, 50)    # 字体大小

        data = self.get_font(number=number, a_font=a_font, A_font=A_font)
        for i in range(num):
            # 数字,大写字母,小谢字母
            text = random.choice(data)
            color = self.get_color()
            draw.text([i * 50 + 12, -10], text, color, font=font)

    def draw_text_rotate(self, font_path, num, number=True, a_font=False, A_font=False):
        """
        若要添加字样,则设为true,数字默认开启
        :param font_path: 字样文件
        :param num: 验证码字符个数
        :param number: 是否开启数字
        :param a_font: 是否开启小写字母
        :param A_font: 是否开启大写字母
        :return: None
        """

        self.img = Image.new(mode=self.mode, size=(self.width, self.height), color=self._color)

        font1 = font_path
        font = ImageFont.truetype(font1, 60)

        data = self.get_font(number=number, a_font=a_font, A_font=A_font)
        text = ""
        for i in range(num):
            # 数字,大写字母,小谢字母
            char = random.choice(data)
            text += char
            color = self.get_color()

            global IMG
            img0 = Image.new(mode=self.mode, size=(self.width, self.height),
                             color=(255, 255, 255))
            draw0 = ImageDraw.Draw(img0, mode='RGBA')
            draw0.text((0, 0), char, color, font=font)

            # 获得一个旋转字
            img1 = img0.rotate(random.uniform(-35, 35), Image.BILINEAR,
                               translate=((i * 65) + 25, 0), center=(0, 0),
                               expand=0)
            if i == 0:  # 当前旋转字与上一个叠加
                IMG = img1
            else:
                IMG = Image.composite(img1, IMG, img1)

        blank = Image.new(mode='RGBA', size=IMG.size, color=(255,) * 3)    # 白背景
        self.img = Image.composite(IMG, blank, IMG)     # 补全旋转字的空缺

        return text

    # 画干扰点
    def draw_point(self, num):
        # 生成img该对象上的画笔
        draw = ImageDraw.Draw(self.img, mode=self.mode)

        for i in range(num):
            x = random.randint(0, self.width)
            y = random.randint(0, self.height)
            color = self.get_color()

            draw.point([x, y], fill=color)
        # self.draw_out()

    # 画干扰园
    def draw_arc(self, num):
        # 生成img该对象上的画笔
        draw = ImageDraw.Draw(self.img, mode=self.mode)

        for i in range(num):
            x = random.randint(0, self.width)
            y = random.randint(0, self.height)
            color = self.get_color()

            draw.arc([x, y, x + 15, y + 15], 0, 360, fill=color)
        # self.draw_out()

    # 画干扰线
    def draw_line(self, num):
        # 生成img该对象上的画笔
        draw = ImageDraw.Draw(self.img, mode=self.mode)

        for i in range(num):
            x1 = random.randint(0, self.width)
            y1 = random.randint(0, self.height)
            x2 = random.randint(0, self.width)
            y2 = random.randint(0, self.height)
            color = self.get_color()

            draw.line([x1, y1, x2, y2], fill=color)
            # self.draw_out()

    def get_font(self, number=False, a_font=False, A_font=False):
        data = []
        if number:
            for i in range(0, 10):
                data.append(str(i))

        if A_font:
            for i in range(65, 91):
                data.append(chr(i))

        if a_font:
            for i in range(97, 123):
                data.append(chr(i))

        return data

    def get_color(self):
        self.color = (random.randint(0, self._color[0]),
                      random.randint(0, self._color[1]), random.randint(0, self._color[2]))
        return self.color

    def make_captcha(self, _show=False, path="captcha.png"):
        text = self.draw_text_rotate(font_path='simkai.ttf', num=4, a_font=True, A_font=True)
        # pil.draw_text(font_path='UbuntuMono-B.ttf', num=4, a_font=True, A_font=True)
        self.draw_point(500)
        self.draw_arc(15)
        self.draw_line(10)
        self.draw_out(_show, path)
        return text


captcha = Captcha(width=280, height=70)

for i in range(10):
    print(captcha.make_captcha(path=f"captcha{i}.png"))

旧版

import string
import random
from PIL import Image, ImageFont, ImageDraw

class Captcha():
    '''
    captcha_size: 验证码图片尺寸
    font_size: 字体大小
    text_number: 验证码中字符个数
    line_number: 线条个数
    background_color: 验证码的背景颜色
    sources: 取样字符集。验证码中的字符就是随机从这个里面选取的
    save_format: 验证码保存格式
    '''
    def __init__(self, captcha_size=(150,100), font_size=30,text_number=4, line_number=10, background_color=(255, 255, 255), sources=None, save_format='png'):
        self.text_number = text_number
        self.line_number = line_number
        self.captcha_size = captcha_size
        self.background_color = background_color
        self.font_size = font_size
        self.format = save_format
        if sources:
            self.sources = sources
        else:
            self.sources = string.ascii_letters + string.digits

    def get_text(self):
        text = random.sample(self.sources,k=self.text_number)
        return ''.join(text)

    def get_font_color(self):
        font_color = (random.randint(0, 150), random.randint(0, 150), random.randint(0, 150))
        return font_color

    def get_line_color(self):
        line_color = (random.randint(0, 250), random.randint(0, 255), random.randint(0, 250))
        return line_color

    def draw_text(self,draw, text, font, captcha_width, captcha_height, spacing=20):
        '''
        在图片上绘制传入的字符
        :param draw: 画笔对象
        :param text: 绘制的所有字符
        :param font: 字体对象
        :param captcha_width: 验证码的宽度 
        :param captcha_height: 验证码的高度
        :param spacing: 每个字符的间隙
        :return: 
        '''
        # 得到这一窜字符的高度和宽度
        text_width, text_height = font.getsize(text)
        # 得到每个字体的大概宽度
        every_value_width = int(text_width / 4)
        # 这一窜字符的总长度
        text_length = len(text)
        # 每两个字符之间拥有间隙,获取总的间隙
        total_spacing = (text_length-1) * spacing
        if total_spacing + text_width >= captcha_width:
            raise ValueError("字体加中间空隙超过图片宽度!")
        # 获取第一个字符绘制位置
        start_width = int( (captcha_width - text_width - total_spacing) / 2 )
        start_height = int( (captcha_height - text_height) / 2 )
        # 依次绘制每个字符
        for value in text:
            position = start_width, start_height
            # print(position)
            # 绘制text
            draw.text(position, value, font=font, fill=self.get_font_color())
            # 改变下一个字符的开始绘制位置
            start_width = start_width + every_value_width + spacing

    def draw_line(self, draw, captcha_width, captcha_height):
        '''
        绘制线条
        :param draw: 画笔对象 
        :param captcha_width: 验证码的宽度 
        :param captcha_height: 验证码的高度
        :return: 
        '''
        # 随机获取开始位置的坐标
        begin = (random.randint(0,captcha_width/2), random.randint(0, captcha_height))
        # 随机获取结束位置的坐标
        end = (random.randint(captcha_width/2,captcha_width), random.randint(0, captcha_height))
        draw.line([begin, end], fill=self.get_line_color())

    def draw_point(self, draw, point_chance, width, height):
        '''
        绘制小圆点
        :param draw: 画笔对象
        :param point_chance: 绘制小圆点的几率 概率为 point_chance/100
        :param width: 验证码宽度
        :param height: 验证码高度
        :return:
        '''
        # 按照概率随机绘制小圆点
        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp < point_chance:
                    draw.point((w, h), fill=self.get_line_color())

    def make_captcha(self, name = 'captcha.png'):
        # 获取验证码的宽度, 高度
        width, height = self.captcha_size
        # 生成一张图片
        captcha = Image.new('RGB',self.captcha_size,self.background_color)
        # 获取字体对象
        font = ImageFont.truetype('simkai.ttf',self.font_size)
        # 获取画笔对象
        draw = ImageDraw.Draw(captcha)
        # 得到绘制的字符
        text = self.get_text()
        # 绘制字符
        self.draw_text(draw, text, font, width, height)
        # 绘制线条
        for i in range(self.line_number):
            self.draw_line(draw, width, height)
        # 绘制小圆点 10是概率 10/100, 10%的概率
        self.draw_point(draw,10,width,height)
        # 保存图片
        captcha.save(name, format=self.format)
        # 显示图片
        # captcha.show()
        return text

if __name__ == "__main__":
    captcha = Captcha()
    for i in range(10):
        text = captcha.make_captcha(f"captcha{i}.png")
        print("验证码文本:", text)

评论区

   Jimmy  创始人

发送时间:2024年12月7日 19:45

你的方法以被采纳,感谢你的支持与贡献