python3 pillow模块

pillow是一个和图片操作有关的python库,由于python2的PIL库年久失修,所以开发者们在PIL的基础上开发出了适合python3的pillow库。

下面简单介绍一下pillow库的用法。

1. 使用image类

PIL最重要的类是 Image 类, 你可以通过多种方法创建这个类的实例。
比如从文件中加载对象,使用open函数:

>>> from PIL import Image
>>> im = Image.open("test.jpg")

现在我们可以看看这个实例的一些属性:

>>> print(im.format, im.size, im.mode)
JPEG (512, 512) RGB

format 这个属性标识了图像来源。如果图像不是从文件读取它的值就是None。size属性是一个二元tuple,包含width和height(宽度和高度,单位都是px)。 mode 属性定义了图像bands的数量和名称,以及像素类型和深度。常见的modes 有 “L” (luminance) 表示灰度图像, “RGB” 表示真彩色图像, “CMYK” 表示出版图像。

如果文件打开错误,返回 IOError 错误。文件没找到则是FileNotFoundError错误。

只要你有了 Image 类的实例,你就可以通过类的方法处理图像。比如,下列方法可以显示图像:

>>> im.show()
但是上述show()方法的效率很低,它需要先保存图像到临时文件,再通过系统软件打开。

2. PIL中的的基本概念

使用Image可以获得一个图像实例,但是这个图像具有哪些方法和属性是我们需要了解的,了解这些之前我们先要清楚python中图像的一些基本概念。

2.1 mode (模式)

图像类的mode定义了图片的类型和像素的宽度。目前支持的mode有:

  • 1 (1位像素, 表示黑白, 每像素存储为8bit)
  • L (8位像素, 表示黑白)
  • P (8位像素, 使用调色板映射到任何其他模式)
  • RGB (3x8位像素, 真彩色)
  • RGBA (4x8位像素, 有透明通道的真彩色)
  • CMYK (4x8位像素, 色彩分离)
  • YCbCr (3x8位像素, 彩色视频格式)
  • I (32位整型像素)
  • F (32位浮点型像素)

PIL还为一些特殊模式提供了有限的支持,包括LA,RGBX和RGBa。

查看实例的模式:

>>> im.mode
'RGB'

2.2 size (大小)

你可以通过size属性读取图像大小。这是一个2元组,包含像素中的水平和垂直尺寸。

查看实例的大小:

>>> im.size
(272, 300)

2.3 Coordinate System (坐标系)

python图像库使用笛卡尔坐标系统,左上角代表(0,0),值得注意的是,坐标值代表的是隐含的像素角。坐标为(0,0)的像素的中心实际上位于(0.5,0.5)处。

坐标通常用的是二元组,矩形用的是四元组,左上方坐标在前。例如覆盖800x600图像全部像素的矩形写为(0,0,800,600)。

2.4 Palette (调色板)

调色板模式(P)使用调色板来定义每个像素的实际颜色。

下面我们可以来查看实例的调色板。如果图像的模式是“P”,则返回ImagePalette类的实例;否则,将为None。

>>> print(im.palette) 
None

2.5 Info (信息)

你可以使用info属性将辅助信息附加到图像。这是一个字典对象。加载和保存图像文件时如何处理这些信息取决于文件格式处理程序。大多数处理程序在加载图像时向info属性添加属性,但在保存图像时忽略它。

我们可以查看实力的信息:

>>> im.info
{'jfif': 257, 'jfif_version': (1, 1), 'jfif_unit': 0, 'jfif_density': (1, 1)}

2.6 Filters (滤波器)

对于可能将多个输入像素映射到单个输出像素的几何操作,PIL提供了四种不同的滤波器模式。具体如下:

  • NEAREST:最近滤波。从输入图像中选取最近的像素作为输出像素。它忽略了所有其他的像素。
  • BILINEAR:双线性滤波。在输入图像的2x2矩阵上进行线性插值。注意:PIL的当前版本,此滤波器在下采样时使用固定的输入环境。
  • BICUBIC:双立方滤波。在输入图像的4x4矩阵上进行立方插值。注意:PIL的当前版本,此滤波器在下采样时使用固定的输入环境。
  • ANTIALIAS:平滑滤波。此过滤器只能与调整大小和缩略图方法一起使用。

注意:在当前的PIL版本中,ANTIALIAS滤波器是下采样(例如,将一个大的图像转换为小图)时唯一正确的滤波器。BILIEAR和BICUBIC滤波器使用固定的输入模板,用于固定比例的几何变换和上采样是最好的。

3.一些常见的操作

下面将列举一些常见的图像操作方法,来快速入门。

3.1 剪切、粘贴、合并图像

Image类包含的方法允许你操作图像部分选区。

从图像中复制一个矩形选区

box = (100, 100, 400, 400)
region = im.crop(box)

矩形选区有一个4元元组定义,分别表示左、上、右、下的坐标。这个库以左上角为坐标原点,单位是px,所以上述代码复制了一个 300x300像素的矩形选区。这个选区现在可以被处理并且粘贴到原图。

处理复制的矩形选区并粘贴到原图

region = region.transpose(Image.ROTATE_180) # 旋转180度
im.paste(region, box)

当你粘贴矩形选区的时候必须保证尺寸一致。此外,矩形选区不能在图像外。然而你不必保证矩形选区和原图的颜色模式一致,因为矩形选区会被自动转换颜色。

完整代码

from PIL import Image
im=Image.open('test.jpg')
box=(50,100,150,200)
region=im.crop(box)
region = region.transpose(Image.ROTATE_90)
im.paste(region,box)
im.save('test2.jpg')

结果

3.2 几何变换

PIL的resize()rotate()支持大小变换和旋转变换。

from PIL import Image
im=Image.open('test.jpg')
out = im.resize((128, 128)) # 改变大小
out = im.rotate(45)  # 旋转45度
out.save('test3.jpg')

结果

旋转图像还可以使用transpose()方法。比如

out = im.transpose(Image.FLIP_LEFT_RIGHT) # 左右翻转
out = im.transpose(Image.FLIP_TOP_BOTTOM) # 上下翻转
out = im.transpose(Image.ROTATE_90)  # 逆时针旋转90度
out = im.transpose(Image.ROTATE_180) 
out = im.transpose(Image.ROTATE_270)

两个方法没有什么区别。但是transform()方法只支持旋转上述几个角度,换个角度就不行了。

3.3 颜色模式转换

允许你使用convert()方法在不同的像素表示之间转换图像。

im = Image.open("test.jpg").convert("L")

该库支持每种模式和“L”和“RGB”模式之间的转换。
要在其他模式之间转换,你可能必须使用中间图像(通常是“RGB”图像)。

3.4 颜色增强

PIL提供了许多可用于增强图像的方法和模块。

1.滤镜

ImageFilter模块包含许多可以与filter()方法一起使用的增强过滤器。

例如detail滤镜

from PIL import ImageFilter
out = im.filter(ImageFilter.DETAIL)

2.操作像素点

point()方法可用于平移图像的像素值(例如图像对比度操作)。在大多数情况下,需要一个参数的函数对象来传递给此方法。
每个像素都根据该功能进行处理。比如,将每个像素点的亮度增大20%

#注意这里用到一个匿名函数(那个可以把i的2倍返回的函数) 
out = im.point(lambda i: i * 2)

结果:

3.5 读取图像

一般来说我们可以使用open方法直接打开一个图像,如果成功的话我们就可以进行下一步的操作,但是当打开失败时会返回IOError

这时你可以使用文件类对象而不是文件名。该对象必须实现read(),seek()和tell()方法,并以二进制模式打开。

从文件读取

fp = open("test.jpg", "rb")
im = Image.open(fp)

从string读取

import StringIO

im = Image.open(StringIO.StringIO(buffer))

从压缩包读取

from PIL import TarIO

fp = TarIO.TarIO("Imaging.tar", "Imaging/test/test.jpg")
im = Image.open(fp)

4. pillow实战演练

这部分将进行一些图像操作的实战,也是一些常见的进阶操作。

4.1 将彩色图转换为灰度图

# coding:utf-8
# python3.6.6

import datetime
from PIL import Image

start = datetime.datetime.now()

im = Image.open('D:\\python\\py_pycharm\\1.jpg')
# 格式、大小、模式
print(im.format, im.size, im.mode)

# L = R * 299/1000 + G * 587/1000+ B * 114/1000

im1 = im.convert("L")
print(im1.format, im1.size, im1.mode)

im1.save('D:\\python\\py_pycharm\\result.bmp')
im1.save('D:\\python\\py_pycharm\\result.jpg')

end = datetime.datetime.now()
print (end-start)

4.2 压缩图片BMP->jpg

这里设置了不同的压缩比例,生成6张jpg图片

# coding:utf-8
# python3.6.6
import datetime
from PIL import Image

im = Image.open('D:\\python\\py_pycharm\\2.bmp')
# 格式、大小、模式
print(im.format, im.size, im.mode)

for i in (3000, 2500, 2000, 1500, 1000, 500):
    start = datetime.datetime.now()
    size=(i,i)
    im = Image.open('D:\\python\\py_pycharm\\2.bmp')
    im.thumbnail(size)
    print("the jpg of 2_%d:"%(7-i/500))
    print(im.format, im.size, im.mode)
    im.save("D:\\python\\py_pycharm\\2_%d.jpg"%(7-i/500))
    end = datetime.datetime.now()
    print("用时:" + str(end-start))
print("over")

4.3 将字符串隐藏在图片中

# coding:utf-8
# python 3.6.6

from PIL import Image
import time

# 将字符串转为二进制
def str_convert_bin(s):
    result = ''
    for c in s:
        b = bin(ord(c)).replace('0b', '')
        b = '0'*(7-len(b))+b
        result = result+b
    return result

# 将二进制转化为字符串
def bin_convert_str(b):
    str=''
    # 将二进制字符串每7位分割,成列表
    b1 = [b[i:i+7] for i in range(0, len(b), 7)]
    for i in range(len(b1)):
        b2 = chr(int(b1[i],2))
        str = str+b2
    return str


# 将二进制字符串嵌入图片像素B通道 im:Image()、bin1:要嵌入的二进制串
def insert(im,bin1):
    size = im.size
    length = len(bin1)
    k=0
    flag=0
    for i in range(size[0]):
        for j in range(size[1]):
            # im.getpixel((i,j))读取像素点(i,j)的像素值
            pixel_b=bin(im.getpixel((i,j))[2]).replace('0b', '')
            if pixel_b[-1:]<bin1[k]:
                # im.putpixel((i,j),(x,y,z))设置像素点(i,j)的RGB值为(x,y,z)
                im.putpixel((i,j),(im.getpixel((i,j))[0],im.getpixel((i,j))[1],im.getpixel((i,j))[2]+1))
            if pixel_b[-1:]>bin1[k]:
                im.putpixel((i,j),(im.getpixel((i,j))[0],im.getpixel((i,j))[1],im.getpixel((i,j))[2]-1))    
            k=k+1 
            if k==length:
                flag=1
                break
        if flag==1:
            break
    print("字符串嵌入完成\n\n")

# 提取字符串 im:Image()、length:二进制字符串长度
def extract(im,length):
    size = im.size
    k=0
    result=''
    flag=0
    for i in range(size[0]):
        for j in range(size[1]):
            pixel_b=bin(im.getpixel((i,j))[2]).replace('0b', '')
            result=result+pixel_b[-1:]
            k=k+1 
            if k==length:
                flag=1
                break
        if flag==1:
            break
    print("提取完成,二进制字符串为:\n%s"%result)
    str = bin_convert_str(result)
    print("转换完成,结果为:\n%s"%str)

def main():
    test_str=input("请输入字符串:\n")
    result = str_convert_bin(test_str)
    print("待嵌入字符串转化为二进制为:\n%s"%result)
    print("开始嵌入....")
    im = Image.open("2.bmp")
    insert(im, result)

    time.sleep(5)
    print("开始提取字符串:")
    extract(im, len(result))


if __name__=='__main__':
    main()

结果:

1 + 8 =
快来做第一个评论的人吧~