Pillow速通教程

目录

  • 一、安装
  • 二、基础
    • 2.1 初识Image类
      • 2.1.1 Image.open()
      • 2.1.2 Image对象的各个属性
      • 2.1.3 Image.new()
      • 2.1.4 Image.save()
    • 2.2 图片格式的转换
    • 2.3 图片缩放
    • 2.4 图片的裁剪与粘贴
    • 2.5 图像的分离与合并
    • 2.6 图像的几何变换
  • 三、进阶
    • 3.1 图像增强
    • 3.2 `Image` 与 `ndarray` 的互相转化

一、安装

pip install pillow

二、基础

2.1 初识Image类

Pillow 中最重要的就是 Image 类了,导入方法如下:

from PIL import Image

2.1.1 Image.open()

假设当前工作目录下有一个 pics 文件夹,其中存放着 1.png 图片,若要打开它,只需

img = Image.open('pics/1.png')

如果打开成功,则上述语句将返回 Image 对象,我们可以使用 show() 方法来查看该图片

img.show()

2.1.2 Image对象的各个属性

我们还可以查看它的格式(后缀名)、图片尺寸(宽 × 高,单位是像素)和图片的模式:

img.format
# 'PNG'
img.size
# (605, 618)
img.mode
# 'RGBA'

以上结果说明,1.png 的格式是 PNG,宽 605,高 618,且图片模式为 RGBA


这里有必要介绍一下常见的四种图片模式:

模式 描述
1 1 位像素(取值范围 0 - 1),0表示黑,1 表示白,单色通道
L 8 位像素(取值范围 0 - 255),灰度图,单色通道
RGB 3 × 8 位像素,真彩色,三色通道,每个通道的取值范围 0 - 255
RGBA 4 × 8 位像素,四色通道(真彩色+透明通道)

2.1.3 Image.new()

除了打开现有的图片之外,我们还可以创建图片,格式如下:

Image.new(mode, size, color)

mode 代表图片模式,size 为图片尺寸,color 代表图片的颜色,默认值为 0 代表黑色。此外,color 还支持 (R, G, B) 三元组数字格式,颜色的十六进制值和颜色的英文单词。

具体示例如下:

img = Image.new(mode='RGB', size=(100, 100), color=(123, 71, 66))
img.show()

Pillow速通教程_第1张图片

img = Image.new(mode='RGB', size=(100, 100), color='#CD96CD')
img.show()

Pillow速通教程_第2张图片

img = Image.new(mode='RGB', size=(100, 100), color='cyan')
img.show()

Pillow速通教程_第3张图片

2.1.4 Image.save()

可能读者已经想到了,既然能创建图片,那又该如何保存呢?

保存图片的格式如下:

Image.save(fp, format=None)

其中 fp 为路径字符串,包含了要保存的文件名。format 为要保存的格式,当不指定文件格式时,它会以默认的图片格式来存储(根据提供的后缀名来自动选择格式);如果指定图片格式,则会以指定的格式存储图片。

例如,我们想将刚创建好的图片保存当当前工作目录下:

img = Image.new(mode='RGB', size=(100, 100), color='#CD96CD')
img.save('./mypic.png')

2.2 图片格式的转换

既然 Image.open() 可以用来打开图片,Image.save() 能够用来保存图片,那我们先打开一个 bmp 格式的图片,再将其保存为 png 格式的,不就可以实现格式转换了?

# 创建一个bmp图像用于转换
img = Image.new(mode='RGBA', size=(100, 100), color='#CD96CD')
img.save('./before.bmp')

# 转换格式
img = Image.open('before.bmp')
img.save('./after.png')

上述代码的确可以运行成功,即 bmp 成功转换成了 png。但需要注意的是,并非所有的图片格式都可以用 save() 进行转换,例如将 png 转换成 jpg:

img = Image.new(mode='RGBA', size=(100, 100), color='#CD96CD')
img.save('./before.png')

img = Image.open('before.png')
img.save('./after.jpg')

最终会报错:OSError: cannot write mode RGBA as JPEG'。这是因为 png 和 jpg 图像模式不一致导致的。其中 png 是四通道 RGBA 模式,而 jpg 是三通道 RGB 模式。因此要想实现图片格式的转换,就要先将 png 转变为三通道 RGB 模式。

img = Image.new(mode='RGBA', size=(100, 100), color='#CD96CD')
img.save('./before.png')

img = Image.open('before.png')
img.convert('RGB')  # 改变模式
img.save('./after.jpg')

常见图片后缀名,图片格式以及图片模式的整理:

extension name format mode
png PNG RGBA, RGB
jpg JPEG RGB
bmp BMP RGBA, RGB

2.3 图片缩放

这一小节我们会尝试对下方的图片进行缩放:

Pillow速通教程_第4张图片

首先读取图片并查看相应信息:

img = Image.open('./pics/1.jpg')
img.size
# (1024, 640)
img.mode
# 'RGB'

可以看出该图片是三通道的且尺寸为 1024 × 640 1024\times640 1024×640。接下来我们用 resize 对其进行缩放:

img = img.resize((500, 500))  # resize会返回一个新的Image对象
img.show()

Pillow速通教程_第5张图片
图片已经被 “压成” 了正方形。


基于此,我们还可以批量地修改图片尺寸。例如当前工作目录下有一个文件夹 pics,里面仅存放着待修改尺寸的图片,我们期望修改尺寸后的图片保存到 pics_new 文件夹中。

def resize_all(new_shape, path, new_path):
    if not os.path.exists(new_path):
        os.mkdir(new_path)
    for filename in os.listdir(path):
        img = Image.open(os.path.join(path, filename))
        img_new = img.resize(new_shape)
        img_new.save(os.path.join(new_path, filename))


new_shape = (500, 500)
path = './pics'
new_path = './pics_new'
resize_all(new_shape, path, new_path)

2.4 图片的裁剪与粘贴

我们可以将图片中的某一部分(区域)裁剪下来,做适当的处理后重新粘贴回去。

Pillow 中,区域的定位方式如下:

Pillow速通教程_第6张图片
对于 2.3 节中的图片,其左上角顶点的坐标为 ( 0 , 0 ) (0,0) (0,0),右下角顶点的坐标为 ( 1024 , 640 ) (1024,640) (1024,640)

如果我们要想裁剪它的左半部分,则对应区域的左上角的坐标为 ( 0 , 0 ) (0,0) (0,0),但右下角的坐标变成了 ( 512 , 640 ) (512,640) (512,640),我们把这两个元组按序排列成一个元组: ( 0 , 0 , 512 , 640 ) (0,0,512,640) (0,0,512,640),它对应了一个裁剪区域:

path = './pics/1.jpg'
img = Image.open(path)
box = (0, 0, 512, 640)
region = img.crop(box)
region.show()

Pillow速通教程_第7张图片
需要注意的是 crop 操作并不会影响到原来的 Image 对象。

我们将裁剪下来的这部分旋转180度后重新拼接回去:

region = region.transpose(Image.ROTATE_180)
img.paste(region, box)
img.show()

Pillow速通教程_第8张图片

2.5 图像的分离与合并

我们可以将一张彩图的R、G、B三个通道分离开来,分别产生三个 Image 对象:

path = './pics/1.jpg'
img = Image.open(path)
r, g, b = img.split()

其中 r, g, b 分别代表一个 Image 对象。我们可以重新组合这些颜色对象来达到不同的效果:

img = Image.merge('RGB', (b, g, r))
img.show()

Pillow速通教程_第9张图片


我们还可以合并两张图片,前提是它们的模式 mode 和尺寸 size 要相同。

假设 pics 文件夹下有两张待合并的图片 1.jpg2.jpg,则一个可能的例子如下:

img_1 = Image.open('./pics/1.jpg')
img_2 = Image.open('./pics/2.jpg')

# 确保尺寸和模式相同
img_2 = img_2.resize(img_1.size)
img_1 = img_1.convert('RGB')
img_2 = img_2.convert('RGB')

# 颜色通道分离
r1, g1, b1 = img_1.split()
r2, g2, b2 = img_2.split()

# 一种可能的组合
img_3 = Image.merge('RGB', (r2, g1, b2))
img_3.show()

除此之外,Image 类还提供了 blend() 方法用来混合 RGBA 模式的图片,这里不作介绍。

2.6 图像的几何变换

我们可以对 Image 对象调用 transpose() 方法来实现简单的翻转和旋转操作:

img = Image.open('./pics/1.jpg')
img = img.transpose(Image.FLIP_LEFT_RIGHT)  # 左右翻转
img = img.transpose(Image.FLIP_TOP_BOTTOM)  # 上下翻转
img = img.transpose(Image.ROTATE_90)  # 逆时针转90°
img = img.transpose(Image.ROTATE_180)  # 转180°
img = img.transpose(Image.ROTATE_270)  # 逆时针转270°
img = img.transpose(Image.TRANSPOSE)  # 转置(类似于矩阵转置)

有些时候,我们需要对图像旋转任意角度,这时候需要用到 rotate() 方法,例如逆时针旋转45°:

img = img.rotate(45)
img.show()

Pillow速通教程_第10张图片
如果不想让填充色为黑色,可以使用 fillcolor 参数进行更改:

img = img.rotate(45, fillcolor='green')

当然 rotate() 还有许多其他参数,这里不作过多介绍。

三、进阶

3.1 图像增强

PIL 中的 ImageFilter 模块包含了许多预定的增强过滤器。

from PIL import ImageFilter

我们可以对图像进行模糊处理:

img = Image.open('./pics/1.jpg')
img_blur = img.filter(ImageFilter.BLUR)
img_blur.show()

Pillow速通教程_第11张图片
绘制轮廓图:

img_cont = img.filter(ImageFilter.CONTOUR)
img_cont.show()


边缘检测:

img_edge = img.filter(ImageFilter.FIND_EDGES)
img_edge.show()


生成平滑图像:

img_smo = img.filter(ImageFilter.SMOOTH)
img_smo.show()

Pillow速通教程_第12张图片

3.2 Imagendarray 的互相转化

我们可以将 ndarray 转化为 Image 对象,其中 ndarray 的 size 必须满足 H × W × C H\times W\times C H×W×C H H H 是图像的高度, W W W 是图像的宽度, C C C 是通道个数,且数据类型须为 np.uint8

pic = np.zeros((600, 1200, 3), dtype=np.uint8)
pic[:, :600] = [255, 0, 0]
pic[:, 600:] = [0, 0, 255]

img = Image.fromarray(pic)
img.show()

Pillow速通教程_第13张图片

我们当然也可以将 Image 转化为 ndarray 后输出:

img = Image.open('./pics/1.jpg')
pic = np.array(img)
pic.shape
# (640, 1024, 3)

以下这段代码说明 pic[:, :, 0] 对应 R 通道,pic[:, :, 1] 对应 G 通道,pic[:, :, 2] 对应 B 通道:

img = Image.open('./pics/1.jpg')
pic = np.array(img)
pic_r = pic[:, :, 0]
pic_g = pic[:, :, 1]
pic_b = pic[:, :, 2]

r = Image.fromarray(pic_r)
g = Image.fromarray(pic_g)
b = Image.fromarray(pic_b)

img_new = Image.merge('RGB', (r, g, b))
img_new.show()  # 结果与原图相同

事实上, H × W × C H\times W\times C H×W×C 的数组观察起来不方便, C × H × W C\times H\times W C×H×W 形状的数组更加符合直觉,我们可以这样调整:

def transform(img):
    pic = np.array(img)
    new_pic = np.moveaxis(pic, -1, 0)
    return new_pic


img = Image.open('./pics/1.jpg')
pic = transform(img)
pic.shape
# (3, 640, 1024)
pic
# array([[[234, 234, 234, ..., 234, 234, 234],
#         [234, 234, 234, ..., 234, 234, 234],
#         [234, 234, 234, ..., 234, 234, 234],
#         ...,
#         [234, 234, 234, ..., 234, 234, 234],
#         [234, 234, 234, ..., 234, 234, 234],
#         [234, 234, 234, ..., 234, 234, 234]],
# 
#        [[227, 227, 227, ..., 227, 227, 227],
#         [227, 227, 227, ..., 227, 227, 227],
#         [227, 227, 227, ..., 227, 227, 227],
#         ...,
#         [227, 227, 227, ..., 227, 227, 227],
#         [227, 227, 227, ..., 227, 227, 227],
#         [227, 227, 227, ..., 227, 227, 227]],
# 
#        [[219, 219, 219, ..., 219, 219, 219],
#         [219, 219, 219, ..., 219, 219, 219],
#         [219, 219, 219, ..., 219, 219, 219],
#         ...,
#         [219, 219, 219, ..., 219, 219, 219],
#         [219, 219, 219, ..., 219, 219, 219],
#         [219, 219, 219, ..., 219, 219, 219]]], dtype=uint8)

你可能感兴趣的:(Computer,Vision,计算机视觉,python)