pillow常用功能,及验证码实现

一、创建图形:

1. Image.new(mode, size, color=0):创建一个空白图片

mode参数:

1:1位像素,表示黑和白,但是存储的时候每个像素存储为8bit。
L:8位像素,表示黑和白。
P:8位像素,使用调色板映射到其他模式。
RGB:3x8位像素,为真彩色。
RGBA:4x8位像素,有透明通道的真彩色。
CMYK:4x8位像素,颜色分离。
YCbCr:3x8位像素,彩色视频格式。
I:32位整型像素。
F:32位浮点型像素。
PIL也支持一些特殊的模式,包括RGBX(有padding的真彩色)和RGBa(有自左乘alpha的真彩色)。

size参数:

size属性定义或获取图片的尺寸。这是一个二元组,包含水平和垂直方向上的像素数。

color参数:

对于单通道图像,变量color只给定一个值;对于多通道图像,变量color给定一个元组(每个通道对应一个值)。

在版本1.1.4及其之后,用户也可以用颜色的名称,比如给变量color赋值为“red”。

如果没有对变量color赋值,图像内容将会被全部赋值为0(图像即为黑色)。

如果变量color是空,图像将不会被初始化,即图像的内容全为0。

from PIL import Image
im= Image.new("RGB", (128, 128), "#FF0000")
im.save("test1.png")   #图像im为128x128大小的红色图像。
im= Image.new("RGB", (128, 128))   #图像im为128x128大小的黑色图像,因为变量color不赋值的话,图像内容被设置为0,即黑色。
im.save("test2.png")
im= Image.new("RGB", (128, 128), "red")   #图像im为128x128大小的红色图像。
im.save("test3.png") #保存图片到本地

 

 显示图像:

im.show()

 

2. Image.frombuffer():使用标准的“raw”解码器,从字符串或者buffer对象中的像素数据创建一个图像

定义:Image.frombuffer(mode,size, data) ⇒ image

Image.frombuffer(mode, size,data, decoder, parameters) ⇒ image

对于一些模式,这个图像存储与原始的buffer(这意味着对原始buffer对象的改变体现在图像本身)共享内存。

并非所有的模式都可以共享内存;支持的模式有“L”,“RGBX”,“RGBA”和“CMYK”。对于其他模式,这个函数与fromstring()函数一致。

3.Image.fromstring():使用标准的“raw”解码器,从字符串的像素数据创建一个图像

定义:Image.fromstring(mode,size, data) ⇒ image

Image.fromstring(mode, size,data, decoder, parameters) ⇒ image

二、图像操作

 1.打开一个已经存在的图片

1).使用open(file, mode)打开图片,和打开文件一样,得到一个文件对象。略

2)使用Image.open(file)打开图片,得到一个PIL的Image类对象。

img = Image.open("test3.png")
print(img.mode)  # "RGB"
print(img.size)  # (670, 502)

 

 2.合并图片:blend

定义:Image.blend(image1,image2, alpha) ⇒ image

含义:

  • 使用给定的两张图像及透明度变量alpha,插值出一张新的图像。这两张图像必须有一样的尺寸和模式。
  • 合成公式为:out = image1 *(1.0 - alpha) + image2 * alpha
  • 如果变量alpha为0.0,将返回第一张图像的拷贝。如果变量alpha为1.0,将返回第二张图像的拷贝。对变量alpha的值没有限制。
from PIL import Image
im1 = Image.open("jing.jpg")
im2 = Image.open("wu.jpg")
im = Image.blend(im1,im2,0.5)
im.save("he.jpg")

 

3.合并图片:composite
定义:Image.composite(image1,image2, mask) ⇒ image
含义:使用给定的两张图像及mask图像作为透明度,插值出一张新的图像。变量mask图像的模式可以为“1”,“L”或者“RGBA”。所有图像必须有相同的尺寸。
from PIL import Image
im1 = Image.open("jing.jpg")
im2 = Image.open("wu.jpg")
r,g,b = im1.split()
print(g.mode)
im = Image.composite(im1,im2,b)
im.save("he.jpg")
b.save("he1.jpg")
from PIL import Image
im = Image.open("jing.jpg")
def deffun(c):
    return c*0.89      #改变了亮度
im_eval = Image.eval(im,deffun)  
im_eval.save("gai.jpg")
#注:图像im_eval与im01比较,其像素值均为im01的一半,则其亮度自然也会比im01暗一些。

 

 4.合并图片:merge

定义:Image.merge(mode,bands) ⇒ image

含义:使用一些单通道图像,创建一个新的图像。

变量bands为一个图像的元组或者列表,每个通道的模式由变量mode描述。

所有通道必须有相同的尺寸。

变量mode与变量bands的关系:
len(ImageMode.getmode(mode).bands)= len(bands)
from PIL import Image
im1 = Image.open("jing.jpg")
im2 = Image.open("wu.jpg")
r1,g1,b1 = im1.split()
r2,g2,b2 = im2.split()
imgs =[r1,g2,b2]
im_merge = Image.merge("RGB",imgs)
im_merge.save("he.jpg")

 

其它方法:convert、copy、draft、filter、fromstring、getbands、getbbox、getcolors、getdata、getextrema、getpixel、histogram、load、paste..........

 

三、ImageFont模块的函数

1.加载字体对象:ImageFont.load(file)⇒ Font instance

从指定的文件中加载一种字体,该函数返回对应的字体对象。如果该函数失败,将产生IOError异常。

2.加载字体对象:ImageFont.load(file)⇒ Font instance

和函数load()一样,但是如果没有指定当前路径的话,会从sys.path开始查找指定的字体文件。

3.创建字体对象:

定义1:ImageFont.truetype(file,size) ⇒ Font instance

加载一个TrueType或者OpenType字体文件,并且创建一个字体对象。

这个函数从指定的字体类型(对应字体文件)加载了一个字体对象,并且为指定大小的字体创建了字体对象。

定义2:ImageFont.truetype(file,size, encoding=value) ⇒ Font instance

通常的编码方式有“unic”(Unicode),“symb”(Microsoft Symbol),“ADOB”(Adobe Standard),“ADBE”(Adobe Expert)和“armn”(Apple Roman)。

4.ImageFont.load_default()⇒ Font instance

加载一个默认的字体

5.font.getsize(text)⇒ (width, height)  返回给定文本的宽度和高度,返回值为2元组。

6.font.getmask(text,mode=”“) ⇒ Image object  为给定的文本返回一个位图

 

四、图形扭曲:

定义1:Img.transform(size, method, data)

定义2:img.transform(size, method, data, filter) ⇒ image

参数说明:

size:一个二元组,变换后图片的宽度和高度

method:变换的方式

  • Image.EXTENT(从原图中裁剪出一个矩形区域),这个方法可以用于在当前图像中裁剪,放大,缩小或者镜像一个任意的长方形。此时data为指定输入图像中两个坐标点的4元组(x0,y0,x1,y1)
  • Image.AFFINE(仿射变换),这个方法用于原始图像的缩放、转换、旋转和裁剪。此时data为一个6元组(a,b,c,d,e,f),包含一个仿射变换矩阵的第一个两行。输出图像中的每一个像素(x,y),新值由输入图像的位置(ax+by+c, dx+ey+f)的像素产生,使用最接近的像素进行近似。
  • Image.QUAD(将正方形转换为矩形),输入图像的一个四边形(通过四个角定义的区域)映射到给定尺寸的长方形。data为一个8元组(x0,y0,x1,y1,x2,y2,x3,y3),它包括源四边形的左上,左下,右下和右上四个角。
  • Image.MESH(一个操作映射多个正方形),与QUAD类似,但是变量data是目标长方形和对应源四边形的list。
  • Image.PERSPECTIVE,对当前图像进行透视变换,产生给定尺寸的新图像。这个方法用于原始图像的2D透视。data为一个8元组(a,b,c,d,e,f,g,h),包括一个透视变换的系数。对于输出图像中的每个像素点,新的值来自于输入图像的位置的(a x + b y + c)/(g x + h y + 1), (d x+ e y + f)/(g x + h y + 1)像素,使用最接近的像素进行近似。

filter:定义了对原始图像中像素的滤波器。

NEAREST、BILINEAR、BICUBIC或者ANTIALIAS之一。

如果忽略,或者图像模式为“1”或者“P”,该变量设置为NEAREST。

methd:Image.EXTENT 示例

from PIL import Image
img01 = Image.open("timg.jpeg")
print(img01.mode)    # "RGB"
print(img01.size)   # (550,404)
trans1 = img01.transform((300,300),Image.EXTENT,(0,0,600,600))
print(trans1.size)  # (300,300)
trans1.show()

 

methd:Image.AFFINE 示例

trans2 = img01.transform((300,300), Image.AFFINE,(1,2,3,2,1,4))

 

 method:Image.QUAD示例

trans3 = img01.transform((300,300), Image.QUAD, (0,0,0,500,600,500,600,0))

 

method:Image.Mesh

method:Image.PERSPECTIVE示例

trans5 = img01.transform((300,300), Image.PERSPECTIVE, (1,2,3,2,1,6,1,2))

 

翻转或者旋转图形,返回新的图形:img.transpose(method) => image

method的取值为:FLIP_LEFT_RIGHT,FLIP_TOP_BOTTOM,ROTATE_90,ROTATE_180,或者ROTATE_270

t = img01.transpose(Image.FLIP_TOP_BOTTOM)

 

 

五、图像滤波:img.filter(imagFilter)

图像滤波:在图像处理中,经常需要对图像进行平滑、锐化、边界增强等滤波处理。

imageFilter参数:

• BLUR:模糊滤波
• CONTOUR:轮廓滤波
• DETAIL:细节滤波
• EDGE_ENHANCE:边界增强滤波
• EDGE_ENHANCE_MORE:边界增强滤波(程度更深)
• EMBOSS:浮雕滤波
• FIND_EDGES:寻找边界滤波
• SMOOTH:平滑滤波
• SMOOTH_MORE:平滑滤波(程度更深)
• SHARPEN:锐化滤波
• GaussianBlur(radius=2):高斯模糊 ,radius指定平滑半径

• UnsharpMask(radius=2, percent=150, threshold=3):反锐化掩码滤波

  • radius指定模糊半径;
  • percent指定反锐化强度(百分比);
  • threshold控制被锐化的最小亮度变化。

• Kernel(size, kernel, scale=None, offset=0):核滤波

当前版本只支持核大小为3x3和5x5的核大小,且图像格式为“L”和“RGB”的图像。

  • size指定核大小(width, height);
  • kernel指定核权值的序列;
  • scale指定缩放因子;
  • offset指定偏移量,如果使用,则将该值加到缩放后的结果上。

• RankFilter(size, rank):排序滤波

  • size指定滤波核的大小;
  • rank指定选取排在第rank位的像素,若大小为0,则为最小值滤波;若大小为size * size / 2则为中值滤波;若大小为size * size - 1则为最大值滤波。

• MedianFilter(size=3):中值滤波,size指定核的大小
• MinFilter(size=3):最小值滤波器,size指定核的大小
• MaxFilter(size=3):最大值滤波器,size指定核的大小
• ModeFilter(size=3):波形滤波器,size指定核的大小

选取核内出现频次最高的像素值作为该点像素值,仅出现一次或两次的像素将被忽略,若没有像素出现两次以上,则保留原像素值。

from PIL import ImageFilter
f1 = img01.filter(ImageFilter.BLUR)
f2 = img01.filter(ImageFilter.CONTOUR)
f3 = img01.filter(ImageFilter.MinFilter(3))
f3 = img01.filter(ImageFilter.EDGE_ENHANCE_MORE)

六、使用PIL生成验证码图片

draw.text((x0,y0), string, fill):写入字符串,第一个参数是一个二元组的起始坐标点,第二个参数是要写入的字符串

draw.text((x0,y0), string, fill,font=font):写入特殊字体的字符串;font字体可选,如选,则是特殊字体

draw.point((x0,y0), fill):画点,第一个参数是一个二元组坐标(1个点),第二个参数,填充颜色

draw.line((x0,y0,x1,y1), 开始角度,结束角度,fill):画线,第一个参数是一个四元组的坐标(2个点)

1.创建图形:

size = (240, 60)
width, height = size
img = Image.new("RGB", size, (238, 99, 99))

 

2.创建画笔:

from PIL import ImageDraw
draw = ImageDraw.Draw(img)

 

3.使用画笔,绘制干扰线,如果需要:

import random
line_num = random.randint(2, 4)  # 干扰线条数
for i in range(line_num):
    # 起始点
    begin = (random.randint(0, size[0]), random.randint(0, size[1]))
    # 结束点
    end = (random.randint(0, size[0]), random.randint(0, size[1]))
    draw.line([begin, end], fill=(0, 0, 0))

 

4.使用画笔,绘制干扰点,如果需要:

point_chance = 2 # 干扰点出现的概率
for w in range(width):
    for h in range(height):
        tmp = random.randint(0, 100)
        if tmp > 100 - point_chance:  # 概率2%
            draw.point((w, h), fill=(0, 0, 0))

 

5.使用画笔,绘制验证字符:

_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大写字母
_numbers = ''.join(map(str, range(3, 10)))  # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
length = 4 #验证码字符数
c_chars = random.sample(init_chars, length)  # 随机获取4个字符
strs = ' %s ' % ' '.join(c_chars)  # 每个字符前后以空格隔开
font = ImageFont.load_default()  # 获取字体对象
font_width, font_height = font.getsize(strs)  # 返回给定文本的宽度和高度
draw.text(((width - font_width) / 3, (height - font_height) / 3), strs, font=font, fill=(0,0,255))
# 绘制验证码:width、height图片的宽度和高度

 

 

6.图形扭曲:

    # 图形扭曲参数
    data = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, data)  # 扭曲图形,得到新的图形

 

7.滤波处理:

img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 滤镜,EDGE_ENHANCE_MORE:边界加强(阈值更大)

 

验证码,代码整合:

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大写字母
_numbers = ''.join(map(str, range(3, 10)))  # 数字,去除可能干扰的1,2
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))


def check_code(width=120, height=30, char_length=4, font_file='Ubuntu-BI.ttf', font_size=28):
    code = []
    img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
    draw = ImageDraw.Draw(img, mode='RGB')

    def rndChar():
        """
        生成随机字母
        :return:
        """
        # return chr(random.randint(65, 90))
        return random.sample(init_chars, 1)[0]

    def rndColor():
        """
        生成随机颜色
        :return:
        """
        return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))

    # 写文字
    font = ImageFont.truetype(font_file, font_size)
    # 使用默认字体
    font = ImageFont.load_default()
    for i in range(char_length):
        char = rndChar()
        code.append(char)
        h = random.randint(0, 4)
        draw.text([i * width / char_length, h], char, font=font, fill=rndColor())

    # 写干扰点
    point_chance = 20  # 干扰点出现的概率
    for i in range(40):
        if random.randint(0, 100) < point_chance:
            draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())

    # 写干扰圆圈
    arc_chance = 5  # 干扰圆圈出现的概率
    for i in range(40):
        if random.randint(0, 100) < arc_chance:
            draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
            x = random.randint(0, width)
            y = random.randint(0, height)
            draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())

    # 画干扰线
    for i in range(5):
        x1 = random.randint(0, width)
        y1 = random.randint(0, height)
        x2 = random.randint(0, width)
        y2 = random.randint(0, height)

        draw.line((x1, y1, x2, y2), fill=rndColor())
    #
    # # 图形扭曲参数
    # params = [1 - float(random.randint(1, 2)) / 100,
    #           0,
    #           0,
    #           0,
    #           1 - float(random.randint(1, 10)) / 100,
    #           float(random.randint(1, 2)) / 500,
    #           0.001,
    #           float(random.randint(1, 2)) / 500
    #           ]
    # # 扭曲图片
    # img = img.transform((width, height), Image.PERSPECTIVE, params)

    # 滤波
    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
    return img, ''.join(code)


if __name__ == '__main__':
    # # 1. 直接打开
    # img,code = check_code()
    # img.show()

    # 2. 写入文件
    # img,code = check_code()
    # with open('code.png','wb') as f:
    #     img.save(f,format='png')

    # 3. 写入内存(Python3)
    from io import BytesIO
    stream = BytesIO()
    img, code = check_code()
    img.save(stream, 'png')
    stream.getvalue()
    img.show()

    # 4. 写入内存(Python2)
    # import StringIO
    # stream = StringIO.StringIO()
    # img, code = check_code()
    # img.save(stream, 'png')
    # stream.getvalue()

 

posted on 2018-11-08 15:12  myworldworld  阅读(1017)  评论(0)    收藏  举报

导航