from io import BytesIO
from random import randint, sample
from string import ascii_letters, digits
from typing import Tuple
# 第三方包
from PIL.Image import new
from PIL.ImageDraw import Draw
from PIL.ImageFilter import EDGE_ENHANCE_MORE
from PIL.ImageFont import truetype
# 创建随机颜色
async def random_color():
"""
生成随机颜色
:return:
"""
return randint(150, 235), randint(150, 235), randint(150, 235)
# 生成验证吗
class CheckCode(object):
def __init__(
self,
image_width: int = 150,
image_height: int = 40,
character_length: int = 5,
font_size: int = 30,
mode: str = 'RGB',
color: Tuple[int] = (255, 255, 255),
font_file: str = '/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf',
) -> None:
self.image_width = image_width
self.image_height = image_height
self.character_length = character_length
self.image = new(mode=mode, size=(image_width, image_height), color=color)
self.draw = Draw(im=self.image, mode=mode)
self.create_font = truetype(font=font_file, size=font_size)
self.random_characters = sample(ascii_letters + digits, self.character_length)
# 开始创建
async def create_check_code(self):
# 将字符写入图框,并配上随机颜色
for _index, _character in enumerate(self.random_characters):
font_start_height = randint(-4, 4)
self.draw.text(
xy=[_index * self.image_width / self.character_length, font_start_height],
text=_character,
font=self.create_font,
fill=await random_color())
for _ in range(150):
# 写干扰点
self.draw.point([randint(0, self.image_width),
randint(0, self.image_height)],
fill=await random_color())
# 写干扰圆圈
self.draw.point([randint(0, self.image_width),
randint(0, self.image_height)],
fill=await random_color())
x = randint(0, self.image_width)
y = randint(0, self.image_height)
radius = randint(2, 4)
self.draw.arc(xy=(x - radius, y - radius, x + radius, y + radius),
start=0,
end=90,
fill=await random_color())
# 画干扰线
for _ in range(10):
x1 = randint(0, self.image_width)
y1 = randint(0, self.image_height)
x2 = randint(0, self.image_width)
y2 = randint(0, self.image_height)
self.draw.line((x1, y1, x2, y2), fill=await random_color())
# 给图框修改边模糊
self.image.filter(EDGE_ENHANCE_MORE)
# 将image对象转换成bytesIO
image_io = BytesIO()
self.image.save(image_io, 'png')
self.image.close()
# 这个很重要,将图片文件指针回到0,不然读取不到图片bytes
image_io.seek(0)
return image_io, ''.join(self.random_characters).lower()
async def main(int_index):
# 2. 写入文件
from app.system_module.method import CheckCode
init_image = CheckCode()
image, code = await init_image.create_check_code()
print(code)
with open(f'code{int_index}.png', 'wb') as f:
f.write(image.read())
if __name__ == '__main__':
from asyncio import run
# for i in range(5):
# run(main=main(int_index=0))
转化成 bytesIO是为了通用化返回,如果需要可以自行修改
我的项目是fastapi的转化对象可以直接作为StreamingResponse对象的接受参数
# 第三方包
from fastapi.responses import StreamingResponse
# 项目内包
from .method import CheckCode
# 获取验证码
async def query_check_code():
_image, _code = await CheckCode().create_check_code()
return StreamingResponse(content=_image, media_type="image/png")