PIL:处理图像的好模块

时间:2019-07-01 00:49:52   收藏:0   阅读:315

介绍

PIL是一个专门用来处理图像的模块,可以对图象进行各种各样的变换

打开一张图片

from PIL import Image

# 调用Image下的open方法,即可打开一张图片
# 得到的im便是图片的字节流
# 我们便可以对im进行操作
im = Image.open("古明地觉.jpg")

查看图片的相关信息


im(图片字节流)的一些操作



图像过滤

可以使用ImageFilter来对图像进行细节上的增强、变换等等,当然还是对im进行操作,但是参数主要是ImageFilter这个模块提供的。这个模块提供很多的过滤器,通过指定不同的过滤器参数,来对图片进行不同的变换

from PIL import Image
from PIL import ImageFilter

im = Image.open("古明地觉.jpg")

图像过滤(高级用法)



Image模块下的api




图片的变换

这里的方法是通过im对象调用的

im.transform

图像可以有大小或者形状上的变化。
主要是用im.transform(size, method, data, filter)
size:新图像的尺寸
method:有EXTENT(裁剪一个矩形区域),AFFINE(仿射变换),QUAD等等,下面举例介绍
data:裁剪的区域,传入矩形的左上顶点和右下顶点
filter:滤波器,有NEAREST,BILINEAR,BICUBIC,ANTIALIAS。中间两个不常用,NEAREST速度最快,ANTIALIAS质量最高

im.transpose

可以对图像进行翻转

from PIL import Image

im = Image.open("古明地觉.jpg")


im1 = im.transpose(Image.FLIP_LEFT_RIGHT)  # 左右翻转
im2 = im.transpose(Image.FLIP_TOP_BOTTOM)  # 上下翻转
im1.show()
im2.show()

技术分享图片

技术分享图片



图像增强

PIL还提供了了一个专门用来增强图像的模块,叫ImageEnhance

操作也很简单
调用ImageEnhance下的某个方法,传入Image对象,也就是im,得到一个增强图像的对象,比如叫enh
然后调用enh.enhance方法,传入增强或者减弱的数值,变得到一个新的Image对象,可以直接调用show方法,或者Image.open()得到的对象的其他方法 
from PIL import Image, ImageEnhance

im = Image.open("古明地觉.jpg")

# 传入Image对象im,得到可以操作亮度的对象
enh = ImageEnhance.Brightness(im)

# 增强或者减弱亮度
enh.enhance(1.5).show()  # 增强为原来的1.5倍
enh.enhance(0.3).show()  # 减弱为原来的0.3倍

技术分享图片

技术分享图片

from PIL import Image, ImageEnhance

im = Image.open("古明地觉.jpg")

# 传入Image对象im,得到可以操作对比度的对象
enh = ImageEnhance.Contrast(im)

# 增强或者减弱对比度
enh.enhance(1.5).show()  # 增强为原来的1.5倍
enh.enhance(0.3).show()  # 减弱为原来的0.3倍

技术分享图片

技术分享图片

from PIL import Image, ImageEnhance

im = Image.open("古明地觉.jpg")

# 传入Image对象im,得到可以操作锐化的对象
enh = ImageEnhance.Sharpness(im)

# 增强或者减弱锐化
enh.enhance(20).show()  # 增强为原来的20倍
enh.enhance(0.5).show()  # 减弱为原来的0.5倍

技术分享图片

技术分享图片

from PIL import Image, ImageEnhance

im = Image.open("古明地觉.jpg")

# 传入Image对象im,得到可以操作颜色均衡的对象
enh = ImageEnhance.Color(im)

# 增强或者减弱颜色均衡
enh.enhance(5).show()  # 增强为原来的5倍
enh.enhance(0.5).show()  # 减弱为原来的0.5倍

技术分享图片

技术分享图片



图像加特技

PIL还为我们提供了ImageChops模块(channel operations),可以对图像进行一些特效上的操作。



图像处理

PIL还提供了一个ImageOps模块,包含一些图像处理操作



ImageDraw

ImageDraw是PIL里面的一个画笔,我们可以导入图像,也可以绘制图像。既然要绘制图像,那么肯定要有画笔啊。调用ImageDraw模块下的Draw方法,draw=ImageDraw(im),传入的im也就是Image对象,就可以生成一个画笔,那么画笔所画的内容就会显示在im(画板)上。那么这个画板可以是image.open创建的Image对象,也可以是自己创建的Image对象。下面我们就来自己创建


创建一个画板

from PIL import Image

"""
三个参数:
第一个参数:模式
第二个参数:画板的长和宽
第三个参数:画板的颜色,是一个三元组
"""
im = Image.new("RGB", (200, 80), (150, 155, 180))
im.show()

技术分享图片

创建画笔

from PIL import Image
from PIL import ImageDraw

im = Image.new("RGB", (200, 80), (150, 155, 180))
# 此时变得到一个画笔,画笔所画的内容都会显示在im上面
draw = ImageDraw.Draw(im)

绘制直线

from PIL import Image
from PIL import ImageDraw

im = Image.new("RGB", (200, 80), (150, 155, 180))
draw = ImageDraw.Draw(im)
"""
line方法表示画一条直线,参数如下:
参数一:起点和终点位置,[x1, y1, x2, y2]
参数二:fill,用什么颜色填充,是一个rgb三元组
参数三:width:线条宽度
"""
draw.line([10, 10, 150, 80], fill=(150, 140, 255), width=2)
draw.line([20, 10, 50, 180], fill=(250, 40, 155), width=2)

im.show()

技术分享图片

绘制曲线

from PIL import Image
from PIL import ImageDraw

im = Image.new("RGB", (800, 600), (100, 100, 100))
draw = ImageDraw.Draw(im)
"""
和绘制直线类似,但是坐标的意义不一样

参数一:起点和终点位置,和直线不一样,直线是两点确定一条直线,而曲线里的起点和终点,表示一个矩形的左上顶点和右下顶点
参数二:起始角度
参数三:终止角度

然后会根据参数一得到的矩形画出一个与矩形四边都想切的圆
然后根据起始角度和终止角度从满圆中截取相应的弧
并且规定相应矩形的水平中位线为零度角,然后顺时针变大

参数四:fill,填充颜色,rgb三元组,或者颜色名
参数五:width,线条宽度
"""
draw.arc([0, 0, 600, 400], 0, 360, fill="blue", width=2)
"""
如果矩形跑到了画板的外面,仍然会按照相应的规则画圆,截取
就假装有画板,只是显示的时候超出画板的部分不显示
有时候我们想画出一条曲线的时候,便可以采用这种办法
通过画圆的方式,然后截取一部分弧
"""
draw.arc([-100, -100, 1000, 200], 0, 360, fill="red", width=2)
im.show()

技术分享图片

绘制圆或者椭圆

这个和上面的arc类似,其实arc也算是用来画圆的

from PIL import Image
from PIL import ImageDraw

im = Image.new("RGB", (800, 600), (100, 100, 100))
draw = ImageDraw.Draw(im)

"""
参数一:和arc的第一个参数是一样的
此时不需要起始和终止角度了,因为画的就是圆或者椭圆

参数二:fill,填充色,圆或者椭圆内部的颜色
参数三:outline,轮廓的颜色
参数四:width,线条的宽度
"""
draw.ellipse([100, 100, 600, 600], fill="blue")
draw.ellipse([100, 100, 500, 300], outline="red", width=2)
im.show()

技术分享图片

draw.chord

用法和arc类似,只不过fill变成填充色,arc中的表示轮廓的fill在arc里面成为了outline

from PIL import Image
from PIL import ImageDraw

im = Image.new("RGB", (800, 600), (100, 100, 100))
draw = ImageDraw.Draw(im)

draw.chord([0, 0, 600, 400], 0, 360, fill="blue", width=2)

draw.arc([-100, -100, 1000, 200], 0, 360, fill="red", width=2)
im.show()

技术分享图片

绘制扇形

参数和chord一样

from PIL import Image
from PIL import ImageDraw

im = Image.new("RGB", (800, 600), (100, 100, 100))
draw = ImageDraw.Draw(im)

draw.pieslice([100, 100, 600, 600], 0, 90, fill="cyan", width=3)

im.show()

技术分享图片

绘制多边形

传入相应点的坐标,会将多个点依次相连

from PIL import Image
from PIL import ImageDraw

im = Image.new("RGB", (800, 600), (100, 100, 100))
draw = ImageDraw.Draw(im)

draw.polygon([(50, 60), (100, 100), (150, 50), (250, 600), (50, 60)], fill="cyan", outline="green")

im.show()

技术分享图片

绘制矩形

from PIL import Image
from PIL import ImageDraw

im = Image.new("RGB", (800, 600), (100, 100, 100))
draw = ImageDraw.Draw(im)

draw.rectangle([100, 100, 400, 400], outline="green")
draw.rectangle([200, 200, 300, 300], fill="blue")

im.show()

技术分享图片

字体对象

既然要画,肯定要有字体啊

from PIL import Image
from PIL import ImageFont

im = Image.new("RGB", (800, 600), (100, 100, 100))
"""
参数一:字体
参数二:大小,size
参数三:index,有的字体是一系列字体的组合index表示用第几个,一般不指定
"""
font = ImageFont.truetype(r"C:\Windows\Fonts\msth.ttc", 40)

绘制文字

from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw

im = Image.new("RGB", (800, 600), "cyan")
draw = ImageDraw.Draw(im)
font = ImageFont.truetype(r"C:\Windows\Fonts\simkai.ttf", 40)

"""
使用text方法绘制文字
参数一:起始位置
参数二:文字,text
参数三:font,字体对象,如果不指定,则使用默认字体
参数四:fill,字体的填充颜色
"""
draw.text([100, 200], text="古明地觉最可爱", font=font, fill=(100, 185, 179))
im.show()

技术分享图片


手动生成验证码

绘制噪点

from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
import random
import string

# 随机生成一个画板颜色
bg_color = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
# 定义画板的宽和高
width, height = 200, 80

# 创建画板
im = Image.new("RGB", (width, height), bg_color)

# 创建画笔对象,接收画板对象,这样一来,画笔所画的内容都会显示在画板上。
draw = ImageDraw.Draw(im)

# 绘制噪点,不要过多,一般为宽乘高再乘以0.1
for _ in range(int(width * height * 0.1)):
    # 噪点的横纵坐标
    x_y_point = random.randint(0, width), random.randint(0, height)
    # 填充色,尽量随机
    fill = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
    # 绘制
    draw.point(x_y_point, fill)
    
# 先来看看画板长啥样
im.show()

技术分享图片

可以看到噪点此刻绘制出来了,在为其绘制几条直线和曲线

from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
import random
import string

bg_color = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
width, height = 200, 80

im = Image.new("RGB", (width, height), bg_color)

draw = ImageDraw.Draw(im)

for _ in range(int(width * height * 0.1)):
    x_y_point = random.randint(0, width), random.randint(0, height)
    fill = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
    draw.point(x_y_point, fill)

for _ in range(5):
    """
    直线的长度要从画板的左边到,画板的右边
    因此左端点要在画板左侧上下变化
    因此右端点要在画板右侧上下变化
    """
    left_pos = 0, random.randint(0, height)
    right_pos = width, random.randint(0, height)
    fill = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
    draw.line([left_pos, right_pos], fill=fill)

# 再为其绘制几条曲线
for _ in range(5):
    """
    这里我们要超出画板
    这样最终在画板上显示的部分只是大圆的一条弧,看起来就像是一条曲线
    不然整个圆都显示的话,就不是我们想要的曲线了
    """
    left_pos = (-100, -100)
    right_pos = (width * 5, random.randint(0, height))
    fill = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
    draw.arc([left_pos, right_pos], 0, 360, fill=fill)

# 来看看长什么样
im.show()

技术分享图片

可以看到,背景的噪点和线段已经绘制完成,下面开始绘制文字

from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
import random
import string

bg_color = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
width, height = 200, 80

im = Image.new("RGB", (width, height), bg_color)

draw = ImageDraw.Draw(im)

for _ in range(int(width * height * 0.1)):
    x_y_point = random.randint(0, width), random.randint(0, height)
    fill = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
    draw.point(x_y_point, fill)

for _ in range(5):
    left_pos = 0, random.randint(0, height)
    right_pos = width, random.randint(0, height)
    fill = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
    draw.line([left_pos, right_pos], fill=fill)

for _ in range(5):
    left_pos = (-100, -100)
    right_pos = (width * 5, random.randint(0, height))
    fill = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
    draw.arc([left_pos, right_pos], 0, 360, fill=fill)

# 验证码是由文字和数字组成,先来获取所有的数字和字母
alpha_digit = string.ascii_letters + string.digits
# 验证码一般是四个字符,从里面随机选取4个
verify_code = random.sample(alpha_digit, 4)
# 生成字体对象
font = ImageFont.truetype(r"C:\Windows\Fonts\simkai.ttf", 40)
# 为四个字符创建四种颜色
color = [(random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)) for _ in range(4)]

# 绘制文字
# 注意:坐标加上字体的宽度不要超出画板,否则显示不全
draw.text([10, 10], verify_code[0], font=font, fill=color[0])
draw.text([60, 25], verify_code[1], font=font, fill=color[1])
draw.text([110, 15], verify_code[2], font=font, fill=color[2])
draw.text([150, 25], verify_code[3], font=font, fill=color[3])

# 释放画笔
del draw

# 再来查看一下
im.show()

技术分享图片

验证码便被成功的绘制出来了


保存图片

我们之前的所有案例,都是调用show方法,自动将图片打开。如果我想将图片保存起来呢?要怎么做呢?

from PIL import Image
import random

bg_color = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
width, height = 200, 80

im = Image.new("RGB", (width, height), bg_color)

# 如果我想将im保存起来的话,可以使用save方法
# 指定文件名和格式
im.save("本地路径.jpg", format="png")

这是一种方法,但是我们生成验证码是为了放在网站使用的,不可能先生成到本地,然后再读取。因此我们可以把字节流放到缓存里。

from PIL import Image
import random
from io import BytesIO

bg_color = random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)
width, height = 200, 80

im = Image.new("RGB", (width, height), bg_color)

buf = BytesIO()
# save方法除了传递本地路径,也可以传递一个缓存,这样字节流就都被放到了缓存里
im.save(buf, "png")

# 有了缓存,那么我们就可以直接在网站上渲染出验证码图片。比如Django
# 我们就可以使用return HttpResponse(buf.getvalue(), "image/png"),直接渲染到页面上

# 这里我们就用文件演示吧
with open("1.png", "wb") as f:
    f.write(buf.getvalue())

技术分享图片



图片转成字符画

from PIL import Image
def get_char(r, g, b, alpha=256):
    # 这些字符串是用来和图片的像素进行匹配的
    ascii_char = '''$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. '''
    if alpha == 0:
        return " "
    # 别问我为什么要这样进行映射,我也不知道
    length = len(ascii_char)
    gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b)
    unit = (256.0+1)/length
    return ascii_char[int(gray/unit)]
 
def get_img(img: str, n: int):
    im = Image.open(img)
    # 有的时候,图片太大了,生成的txt文件没办法全部显示,需要拖动滚动条
    # 显然这样不爽,因此我们可以适当的缩小
    # 具体缩小多少倍,看图片的情况
    height = int(im.size[1]/n)
    width = int(im.size[0]/n)
  
    im = im.resize((width, height), Image.NEAREST)
    txt = ""
    for h in range(height):
        for w in range(width):
            # 获取每一个像素的三原色,匹配相应的字符
            txt += get_char(*im.getpixel((w, h)))
        # 注意换行,不然每一行的像素都合在一行了
        txt += "\n"
    return txt
 
# 调用main函数,只需传入图片,和生成的txt文件名以及缩小的倍数即可
def main(image_name: str, art_name: str, n: int=1):
    txt = get_img(image_name, n)
    with open(art_name, "w", encoding="utf-8") as f:
        f.write(txt)
if __name__ == '__main__':
    main(r"C:\Users\Administrator\Desktop\kanade.jpg", r"C:\Users\Administrator\Desktop\kanade.txt")

技术分享图片



补充:图像的一些格式

在PIL中,彩色图片,打开之后返回的对象模式为RGB或者RGBA,灰度图像,返回对象的模式为L。
一般情况我们可以使用Image.open打开任意格式的图片,然后保存的时候保存为想要的格式。
但是Imgae对象可以直接转换格式,在PIL中共有1,L,P,RGB,RGBA,CMYK,YCbCr,I,F九种格式
from PIL import Image


im = Image.open("蕾姆.png")
im.show()

技术分享图片

from PIL import Image


im = Image.open("蕾姆.png")
im1 = im.convert("1")
print(im1.mode)  # 1
im1.show()

技术分享图片

from PIL import Image


im = Image.open("蕾姆.png")
im1 = im.convert("L")
"""
L = r * 299 / 1000 + g * 587 / 1000 + b * 114 / 1000
"""
print(im1.mode)  # L
im1.show()

技术分享图片

from PIL import Image


im = Image.open("蕾姆.png")
im1 = im.convert("P")
print(im1.mode)  # P
im1.show()

技术分享图片

from PIL import Image


im = Image.open("蕾姆.png")
im1 = im.convert("RGBA")
"""
模式rgba为32位彩色图像,它的每个像素用32个bit表示,
其中24bit表示红色、绿色、蓝色三个通道,另外8位表示alpha通道,即透明通道
"""
print(im1.mode)  # RGBA
im1.show()

技术分享图片

from PIL import Image


im = Image.open("蕾姆.png")
im1 = im.convert("CMYK")
"""
模式cmyk为32位彩色图像,它的每个像素用32位表示
模式cmyk就是印刷四分色模式,它是彩色印刷是所采用的一种套色模式
利用色料的三原色混色原理,加上黑色油墨,共计四种颜色混合叠加,形成所谓的'全彩印刷'
C = 255 - R
M = 255 - G
Y = 255 - B
K = 0
"""
print(im1.mode)  # CMYK
im1.show()

技术分享图片

from PIL import Image


im = Image.open("蕾姆.png")
im1 = im.convert("YCbCr")
"""
模式YCbCr为24位彩色图像,它的每个像素用24位表示
YCbCr其中Y是指亮度分量,Cb是指蓝色色度分量,而Cr是指红色色度分量
人的肉眼对Y分量更加敏感,因此在通过对色度分量进行子采样来减少色度分量后,肉眼将察觉不到图像的质量变化
Y = 0.257 * R + 0.504 * G + 0.098 * B + 16
Cb = -0.148 * R - 0.291 * G + 0.439 * B + 128
Cr = 0.439 * R - 0.368 * G - 0.071 * B + 128
"""
print(im1.mode)  # YCbCr
im1.show()

技术分享图片

from PIL import Image


im = Image.open("蕾姆.png")
im1 = im.convert("I")
"""
模式I为32位整型灰色图像,它的每个像素用32位表示
0表示灰,255表示白,(0, 255)之间的数字表示不同的灰度
I = R * 299 / 1000 + G * 587 / 1000 + B * 114 / 1000 
"""
print(im1.mode)  # I
im1.show()

技术分享图片

from PIL import Image


im = Image.open("蕾姆.png")
im1 = im.convert("F")
"""
模式I为32位浮点灰色图像,它的每个像素用32位表示
0表示灰,255表示白,(0, 255)之间的数字表示不同的灰度
F = R * 299 / 1000 + G * 587 / 1000 + B * 114 / 1000

模式F和模式L的转换公式是一样的,都是RGB转成灰色值的公式
但模式F会保留小数的部分 
"""
print(im1.mode)  # F
im1.show()

技术分享图片

PIL还有一些其他的模块,这里就不介绍了,因为不常用(其实是你懒),目前的这些对于基本的图像处理应该足够了,当然如果后续想到了,会继续添加

原文:https://www.cnblogs.com/traditional/p/11111770.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!