在学习PIL中,我们可能会去生成随机字符验证码,添加直线、选字符这些都是比较简单的操作,但如何将我们随机生成的字符旋转起来,是相较复杂一点的。就我目前了解,可以使用numpy的数组操作,或是直接用opencv解决。本文主要通过PIL的模块对字符实现旋转,主要方法是Image.composite(),建议大家可以先了解一下这个方法。
目前,PIL中的rotate方法只能针对图片进行操作,而ImageDraw中的text()方法不能控制字符的方向。所以我的思路是,将字符旋转后的图片设为RGBA模式,将alpha通道设置为比较小的数,以此在合成图片时,字符以外的背景不会覆盖之前生成的字符。通过循环将旋转后的字符图片依次放入一张白色背景的图,每次生成的旋转图作为下一次合成的背景板,这样我们就得到了一张“RGBA”格式的旋转字符图。
之后,我们生成一张背景马赛克图。
将字符图转化为“RGB”模式,将马赛克背景图“覆盖到”字符图上,效果如下:
详细代码:
from PIL import Image,ImageDraw,ImageFont
import numpy as np
import random
def randomchar():
a = chr(random.randint(65,90))
b = chr(random.randint(97,122))
c = str(random.randint(0,9))
return random.choice([a,b,c])
def randomcolor():#随机设置某一背景像素点的颜色
return (random.randint(0,255),
random.randint(0,255),
random.randint(0,255))
def randomcolor2():#随机设置验证码字符或干扰直线的颜色
return (random.randint(80,255),
random.randint(80,255),
random.randint(80,255))
def randomcolor_light():#返回比较亮的颜色
return (random.randint(123,255),
random.randint(123,255),
random.randint(123,255))
def randomcolor_dark():#返回比较暗的颜色
return (random.randint(0,100),
random.randint(0,100),
random.randint(0,100))
def randomline(img,n):#在图上任意位置画n条线
draw = ImageDraw.Draw(img)
for i in range(n):
draw.line((random.randint(0,img.width-1),random.randint(0,img.height-1),
random.randint(0,img.width-1),random.randint(0,img.height-1)),
fill=randomcolor2(),width=2)
return img
font = ImageFont.truetype("msyh.ttc",40)#设置显示的字符字体,需要将对应的字体文件放到同级目录
mask2 = Image.fromarray(np.uint8(np.random.uniform(0,255,(214,214,3))))
distance = 0
mask = Image.new("RGBA",(240,240),(255,255,255,0))
for i in range(4):
s_background = Image.new("RGBA",(240,60),(255,255,255,0))#alpha通道设为0,保证透明度
s_draw = ImageDraw.Draw(s_background)
s_draw.text((5+distance,5),randomchar(),font=font,fill=randomcolor_dark())
s_rotate = s_background.rotate(random.randint(-60,60),expand=1)#图像会转动随机的角度
mask.resize(s_rotate.size)
out1 = Image.composite(s_rotate, mask, s_rotate)#第一次复合生成的图片是旋转后去黑色背景图片
mask = out1
distance = distance+random.randint(-30,30)+50#修改下次字符绘画的初始位置
out1 = out1.convert("RGB")
mask2.resize(out1.size)
r,g,b = mask2.split()
out2 = Image.composite(mask2,out1,r)#将我们生成的随机马赛克图片覆盖到旋转字符图片上去
out2 = randomline(out2,4)
out2.show()
总结:单用PIL可以,但是存在一些明显的缺点,1.在合成最终图片时,马赛克背景某一颜色通道会消失(存疑),导致最后背景效果不好。2.由于我们用的是composite方法,最后字符上会存留背景的像素点(机器更难识别?),而且该方法需要我们合成的两张图片大小一致,所以整张图的大小会随着循环的变化而变化,一般情况下,字符不会在同一水平位置,用numpy数组操作可以改进。3.当字符颜色较亮时,可能会很难识别出验证码。