在Python程序中生成验证码并不算特别复杂,但需要三方库Pillow的支持(PIL的分支),因为要对验证码图片进行旋转、扭曲、拉伸以及加入干扰信息来防范那些用OCR(光学文字识别)破解验证码的程序。
下面的代码封装了生成验证码图片的功能,大家可以直接用这些代码来生成图片验证码,不要“重复发明轮子”。
"""
图片验证码
"""
import os
import random
from io import BytesIO
from PIL import Image
from PIL import ImageFilter
from PIL. ImageDraw import Draw
from PIL. ImageFont import truetype
class Bezier ( object ):
"""贝塞尔曲线"""
def __init__ ( self ):
self. tsequence = tuple ( [t / 20.0 for t in range ( 21 ) ] )
self. beziers = { }
def make_bezier ( self , n ):
"""绘制贝塞尔曲线"""
try:
return self. beziers [n ]
except KeyError:
combinations = 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 , width , height , fonts = None , color = None ):
self._image = None
self._fonts = fonts if fonts else \
[ os. path. join ( os. path. dirname (__file__ ) , 'fonts' , font )
for font in [ 'ArialRB.ttf' , 'ArialNI.ttf' , 'Georgia.ttf' , 'Kongxin.ttf' ] ]
self._color = color if color else random_color ( 0 , 200 , random. randint ( 220 , 255 ) )
self._width , self._height = width , height
@ classmethod
def instance (cls , width = 200 , height = 75 ):
prop_name = f '_instance_{width}_{height}'
if not hasattr (cls , prop_name ):
setattr (cls , prop_name , cls (width , height ) )
return getattr (cls , prop_name )
def background ( self ):
"""绘制背景"""
Draw ( self._image ). rectangle ( [ ( 0 , 0 ) , self._image. size ] ,
fill =random_color ( 230 , 255 ) )
def smooth ( self ):
"""平滑图像"""
return self._image. filter (ImageFilter. SMOOTH )
def curve ( self , width = 4 , number = 6 , color = None ):
"""绘制曲线"""
dx , height = self._image. size
dx / = number
path = [ (dx * i , random. randint ( 0 , height ) )
for i in range ( 1 , number ) ]
bcoefs = 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 ( self._image ). line (points , fill =color if color else self._color , width =width )
def noise ( self , number = 50 , level = 2 , color = None ):
"""绘制扰码"""
width , height = self._image. size
dx , dy = width / 10 , height / 10
width , height = width - dx , height - dy
draw = Draw ( self._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 )
def text ( self , captcha_text , 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 ( self._image )
char_images = [ ]
for c in captcha_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 = self._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 )
self._image. paste (char_image ,
(offset , int ( (height - c_height ) / 2 ) ) ,
mask )
offset + = int (c_width * squeeze_factor )
@ staticmethod
def warp (image , dx_factor = 0.3 , dy_factor = 0.3 ):
"""图像扭曲"""
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 ) )
warp_image = Image. new (
'RGB' ,
(width + abs (x1 ) + abs (x2 ) , height + abs (y1 ) + abs (y2 ) ) )
warp_image. paste (image , ( abs (x1 ) , abs (y1 ) ) )
width2 , height2 = warp_image. size
return warp_image. 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 )
offset_image = Image. new ( 'RGB' , (width + dx , height + dy ) )
offset_image. paste (image , (dx , dy ) )
return offset_image
@ staticmethod
def rotate (image , angle = 25 ):
"""图像旋转"""
return image. rotate ( random. uniform (-angle , angle ) ,
Image. BILINEAR , expand = 1 )
def generate ( self , captcha_text = '' , fmt = 'PNG' ):
"""生成验证码(文字和图片)"""
self._image = Image. new ( 'RGB' , ( self._width , self._height ) , ( 255 , 255 , 255 ) )
self. background ( )
self. text (captcha_text , self._fonts ,
drawings = [ 'warp' , 'rotate' , 'offset' ] )
self. curve ( )
self. noise ( )
self. smooth ( )
image_bytes = BytesIO ( )
self._image. save (image_bytes , format =fmt )
return image_bytes. getvalue ( )
def pascal_row (n = 0 ):
"""生成Pascal三角第n行"""
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 random_color (start = 0 , end = 255 , opacity = 255 ):
"""获得随机颜色"""
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
说明:上面的代码在生成验证码图片时用到了三种字体文件,使用上面的代码时需要添加字体文件到应用目录下的fonts目录中。
行云博客 - 免责申明 本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我联系处理。敬请谅解!
本文链接:https://www.xy586.top/7617.html
转载请注明文章来源:行云博客 » Python验证码生成