Python 超简单生成 验证码图片 -pilow模块

预期效果:

我们要使用 Python下的Pillow实现以下功能
在这里插入图片描述
介绍一些 图片验证码:

图片验证码是比较传统的验证码形式,图片中除了经过平移、旋转、错切、缩放等基本变换的字母和数字之外,加一些随机线条或其他 干扰因素。

安装 pillow:

pip install pillow

开工:

  1. 既然要用到验证码,那就先准备好 随机选取的模块
  2. 导入 字符和数字的 集合(不是数据类型的集合)
  3. 准备图片需要的用到的工具、ImageDraw 和 ImageFont
  4. 将选取好的字符串返回
from random import choice, randint, randrange    # randrange和randint的区别就是 前者左闭右开
import string
from PIL import Image, ImageDraw, ImageFont

# 验证码图片文字的字符集
characters = string.ascii_letters + string.digits   # string下面的ascii_letters 字符和 digits 数字供咱们选择

def selectedCharacters(length):
	'''返回length个字符串'''
    result = ''.join(choice(characters) for _ in range(length))  # 这里用到的是 生成器表达式来进行随机选取 和 封装字符串、
    return result

  1. 准备好颜色返回
def getColor():
	'''准备好 文字干扰线要用到的颜色'''
    r = randint(0, 255)     # 随机选取颜色
    g = randint(0, 255)
    b = randint(0, 255)
    return (r, g, b)
  1. 定义主函数,生成图片,函数参数:图片大小,字符数量,背景颜色
  2. 创建白色底图,和生成在图片上绘画的对象
  3. 将生成的字符串计算大小(宽高),否则图片装不下
  4. 判断 空白图像是否能装得下 字符
def main(size=(200, 100), characterNumber=6, bgcolor=(255, 255, 255)):
    '''创建空白图像和绘图对象'''
    imageTemp = Image.new('RGB', size, bgcolor)
    draw01 = ImageDraw.Draw(imageTemp)      # 创建可绘图的 draw 对象

    # 生成并计算随机字符串的宽度和高度
    text = selectedCharacters(characterNumber)    # 生成随机的字符
    font = ImageFont.truetype(C:/Windows/Fonts/Dengl.ttf', 48)      # 使用系统的文字
    width, height = draw01.textsize(text, font)     # 计算图片中字符集的,宽高是多少
    
    if width + 2 * characterNumber > size[0] or height > size[1]:
        '''(如果生成字符的宽度 + 字符数量 x 2 > 空白图片的宽度) or (字符的高度 > 空白图片的高度)'''
        print('尺寸不合法')
        return
  1. 生成字符在图片中的坐标位置,求出每个字符的长度
  2. 对总字符数量遍历,让每个字符都有自己的坐标位置 x,y
  3. 开始在 图上绘画 字符
	# 绘制随机字符串中的字符
    startX = 0     # x坐标
    widthEachCharater = width // characterNumber     # 字符总宽 // 数量 = 每一个字符的宽度
    
    for i in range(characterNumber):      # 遍历 字符串数量,循环这么多次
        startX += widthEachCharater + 1         # 计算 每个字符 X轴的 坐标点
        
        '''让图片的Y轴 上下不定,Y轴 = ((图片的高度-字符的高度) // 2) 计算出能够活动的空间 + +- 10的这个区间,这里要注意 自己设定的区间不要超过图片的活动范围'''     
        position = (startX, (size[1] - height) // 2 + randint(-10, 10))      # x,y 轴的坐标都有,那么他就可以代表每个 字符的位置
        draw01.text(xy=position, text=text[i], font=font, fill=getColor())     # 在坐标上xy,使用 fill 颜色,font大小,来生成 text 文字
  1. 对生成的字符实现扭曲的效果,就是 向两边偏移一点点像素
  2. 加载 生成新图像(和之前图像一样大) 和 之前创建的白板图像
  3. 遍历图像的宽高,将像素位置向做偏移一些
  4. 最后在 for 的作用域内,老图像将x,y 一点点的赋值给 新图像
 	# 对像素位置进行微调,实现扭曲的效果
    imageFinal = Image.new('RGB', size, bgcolor)
    pixelsFinal = imageFinal.load()     # 加载新图像
    pixelsTemp = imageTemp.load()		# 加载老图像
    
    for y in range(size[1]):            # 遍历y轴 也就是图像的高度,从上到下
        offset = randint(-1, 0)			# 生成偏移的规格 -1到0,代表着向左
        for x in range(size[0]):        # 遍历x轴 也就是图像的宽度,从左到右
            newx = x + offset           # 在x轴的基础项 加上 刚才设置的偏移量,结果就是 不想左偏移像素 就是 原地不动
    
            if newx >= size[0]:         # 如果偏移后的值 大于 原图像的宽度了
                newx = size[0] - 1		# 就让他 -1个
            elif newx < 0:              # 小于 0,
                newx = 0				# 就让他等于零
            pixelsFinal[newx, y] = pixelsTemp[x, y]         # 将原图像的 宽高 赋值给 新图像
			'''这个的操作就相当于,将之前图像的宽高,通过for 一点点的遍历给 新图像'''
  1. 制作三中 随机的干扰线,彩色的小点,弧线,直线
  2. 将刚才扭曲的新图像 Draw,成为可绘画图片
  3. point 点,line 直线, arc 弧线
	# 绘制随机颜色随机位置的干扰像素
    draw02 = ImageDraw.Draw(imageFinal)
    for i in range(int(size[0] * size[1] * 0.07)):
    '''遍历次数 宽度x高度x0.07,这个0.07值得是密度,7%的干扰像素,设置多了就看不到内容了'''
    
        draw02.point((randrange(0, size[0]), randrange(0, size[1])), fill=getColor())
		'''point 画点。参数 x,y,颜色。画点的x,y轴都是取决于 图片的大小,然后randrange取随机。颜色也是取随机'''
    
		
    # 绘制 8条随机干扰直线
    for i in range(8):
    '''随机直线 值得就是 两端的位置不固定,但是 y轴的 长度都是一样的'''
        start = (0, randrange(size[1]))     # 开始随机位置 x=0,y=随机
        end = (size[0], randrange(size[1]))     # 结束随机位置
        draw02.line([start + end], fill = getColor(), width=1)     # 开始画线,x,y位置,随机的颜色,线条的宽度。

    # 绘制 8条随机弧线
    for i in range(8):
        start = (-50, -50)    # start为 x,y 的-50坐标
        end = (size[0] + 10, randint(0, size[1] + 10))     # end是 宽度 + 随机一个区间
        '''start + end,代表 end计算的值 分别 -50,来代表弧线的 x,y 轴'''
        draw02.arc(start + end, 0, 360, fill=getColor())    # 0起始位置,360结束位置
  1. 保存图片 并 展示
  2. 将主函数启动
    # 保存并显示图片
    imageFinal.save("result.jpg")
    imageFinal.show()

if __name__ == '__main__':
    main((200, 100), 6, (255, 255, 255))

效果展示

在这里插入图片描述


最后全部代码
from random import choice, randint, randrange    # randrange和randint的区别就是 前者左闭右开
import string
from PIL import Image, ImageDraw, ImageFont

# 验证码图片文字的字符集
characters = string.ascii_letters + string.digits   # string下面的ascii_letters 字符和 digits 数字供咱们选择

def selectedCharacters(length):
	'''返回length个字符串'''
    result = ''.join(choice(characters) for _ in range(length))  # 这里用到的是 生成器表达式来进行随机选取 和 封装字符串、
    return result

def getColor():
	'''准备好 文字干扰线要用到的颜色'''
    r = randint(0, 255)     # 随机选取颜色
    g = randint(0, 255)
    b = randint(0, 255)
    return (r, g, b)

def main(size=(200, 100), characterNumber=6, bgcolor=(255, 255, 255)):
    '''创建空白图像和绘图对象'''
    imageTemp = Image.new('RGB', size, bgcolor)
    draw01 = ImageDraw.Draw(imageTemp)      # 创建可绘图的 draw 对象

    # 生成并计算随机字符串的宽度和高度
    text = selectedCharacters(characterNumber)    # 生成随机的字符
    font = ImageFont.truetype('C:/Windows/Fonts/Dengl.ttf', 48)      # 使用系统的文字
    width, height = draw01.textsize(text, font)     # 计算图片中字符集的,宽高是多少
    
    if width + 2 * characterNumber > size[0] or height > size[1]:
        '''(如果生成字符的宽度 + 字符数量 x 2 > 空白图片的宽度) or (字符的高度 > 空白图片的高度)'''
        print('尺寸不合法')
        return

	# 绘制随机字符串中的字符
    startX = 0     # x坐标
    widthEachCharater = width // characterNumber     # 字符总宽 // 数量 = 每一个字符的宽度
    
    for i in range(characterNumber):      # 遍历 字符串数量,循环这么多次
        startX += widthEachCharater + 1         # 计算 每个字符 X轴的 坐标点
        
        '''让图片的Y轴 上下不定,Y轴 = ((图片的高度-字符的高度) // 2) 计算出能够活动的空间 + +- 10的这个区间,这里要注意 自己设定的区间不要超过图片的活动范围'''     
        position = (startX, (size[1] - height) // 2 + randint(-10, 10))      # x,y 轴的坐标都有,那么他就可以代表每个 字符的位置
        draw01.text(xy=position, text=text[i], font=font, fill=getColor())     # 在坐标上xy,使用 fill 颜色,font大小,来生成 text 文字

	 	# 对像素位置进行微调,实现扭曲的效果
    imageFinal = Image.new('RGB', size, bgcolor)
    pixelsFinal = imageFinal.load()     # 加载新图像
    pixelsTemp = imageTemp.load()		# 加载老图像
    
    for y in range(size[1]):            # 遍历y轴 也就是图像的高度,从上到下
        offset = randint(-1, 0)			# 生成偏移的规格 -1到0,代表着向左
        for x in range(size[0]):        # 遍历x轴 也就是图像的宽度,从左到右
            newx = x + offset           # 在x轴的基础项 加上 刚才设置的偏移量,结果就是 不想左偏移像素 就是 原地不动
    
            if newx >= size[0]:         # 如果偏移后的值 大于 原图像的宽度了
                newx = size[0] - 1		# 就让他 -1个
            elif newx < 0:              # 小于 0,
                newx = 0				# 就让他等于零
            pixelsFinal[newx, y] = pixelsTemp[x, y]         # 将原图像的 宽高 赋值给 新图像
			'''这个的操作就相当于,将之前图像的宽高,通过for 一点点的遍历给 新图像'''

	# 绘制随机颜色随机位置的干扰像素
    draw02 = ImageDraw.Draw(imageFinal)
    for i in range(int(size[0] * size[1] * 0.07)):
    '''遍历次数 宽度x高度x0.07,这个0.07值得是密度,7%的干扰像素,设置多了就看不到内容了'''
    
        draw02.point((randrange(0, size[0]), randrange(0, size[1])), fill=getColor())
		'''point 画点。参数 x,y,颜色。画点的x,y轴都是取决于 图片的大小,然后randrange取随机。颜色也是取随机'''
    
		
    # 绘制 8条随机干扰直线
    for i in range(8):
    '''随机直线 值得就是 两端的位置不固定,但是 y轴的 长度都是一样的'''
        start = (0, randrange(size[1]))     # 开始随机位置 x=0,y=随机
        end = (size[0], randrange(size[1]))     # 结束随机位置
        draw02.line(start + end, fill = getColor(), width=1)     # 开始画线,x,y位置,随机的颜色,线条的宽度。

    # 绘制 8条随机弧线
    for i in range(8):
        start = (-50, -50)    # start为 x,y 的-50坐标
        end = (size[0] + 10, randint(0, size[1] + 10))     # end是 宽度 + 随机一个区间
        '''start + end,代表 end计算的值 分别 -50,来代表弧线的 x,y 轴'''
        draw02.arc(start + end, 0, 360, fill=getColor())    # 0起始位置,360结束位置

    # 保存并显示图片
    imageFinal.save("result.jpg")
    imageFinal.show()

if __name__ == '__main__':
    main((200, 100), 6, (255, 255, 255))
  • 如果觉得 注释太多,影响学习

注释简易版本

from random import choice, randint, randrange
import string
from PIL import Image, ImageDraw, ImageFont

# 验证码图片文字的字符集
characters = string.ascii_letters + string.digits

def selectedCharacters(length):

    result = ''.join(choice(characters) for _ in range(length))
    return result


def getColor():

    r = randint(0, 255)
    g = randint(0, 255)
    b = randint(0, 255)
    return (r, g, b)

def main(size=(200, 100), characterNumber=6, bgcolor=(255, 255, 255)):
    # 创建空白图像和绘图对象
    imageTemp = Image.new('RGB', size, bgcolor)
    draw01 = ImageDraw.Draw(imageTemp)     

    # 生成并计算随机字符串的宽度和高度
    text = selectedCharacters(characterNumber)
    font = ImageFont.truetype('C:/Windows/Fonts/Dengl.ttf', 48)
    width, height = draw01.textsize(text, font)
    if width + 2 * characterNumber > size[0] or height > size[1]:
        print('尺寸不合法')
        return

    # 绘制随机字符串中的字符
    startX = 0
    widthEachCharater = width // characterNumber
    for i in range(characterNumber):
        startX += widthEachCharater + 1
        position = (startX, (size[1] - height) // 2 + randint(-10, 10))
        draw01.text(xy=position, text=text[i], font=font, fill=getColor())

    # 对像素位置进行微调,实现扭曲的效果
    imageFinal = Image.new('RGB', size, bgcolor)
    pixelsFinal = imageFinal.load()
    pixelsTemp = imageTemp.load()
    for y in range(size[1]):
        offset = randint(-1, 0)
        for x in range(size[0]):
            newx = x + offset
            if newx >= size[0]:
                newx = size[0] - 1
            elif newx < 0:
                newx = 0
            pixelsFinal[newx, y] = pixelsTemp[x, y]

    # 绘制随机颜色随机位置的干扰像素
    draw02 = ImageDraw.Draw(imageFinal)
    for i in range(int(size[0] * size[1] * 0.07)):
        draw02.point((randrange(0, size[0]), randrange(0, size[1])), fill=getColor())

    # 绘制8条随机干扰直线
    for i in range(8):
        start = (0, randrange(size[1]))
        end = (size[0], randrange(size[1]))
        draw02.line(start + end, fill = getColor(), width=1)

    # 绘制8条随机弧线
    for i in range(8):
        start = (-50, -50)
        end = (size[0] + 10, randint(0, size[1] + 10))
        draw02.arc(start + end, 0, 360, fill=getColor())

    # 保存并显示图片
    imageFinal.save("result.jpg")
    imageFinal.show()

if __name__ == '__main__':
    main((200, 100), 6, (255, 255, 255))
发布了91 篇原创文章 · 获赞 174 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_44685869/article/details/104839598