编写自己的图像插件

Pillow使用了一个插件模型,允许你将自己的解码器添加到库中,而无需对库本身进行任何更改。这样的插件通常有这样的名字 XxxImagePlugin.py 在哪里 Xxx 是唯一的格式名(通常是缩写)。

警告

Pillow >=2.1.0不再自动导入python路径中名称以 ImagePlugin.py . 您需要手动导入图像插件。

Pillow分两个阶段解码文件:

  1. 它以加载的顺序循环访问可用的图像插件,并调用插件的 _accept 函数的前16个字节。如果 _accept 函数返回true,插件的 _open 方法来设置图像元数据和图像分片。这个 _open 方法不用于解码实际图像数据。

  2. 当请求图像数据时, ImageFile.load 方法,它为每个图块设置一个解码器并将数据提供给它。

插件应包含从图像处理程序派生的格式 PIL.ImageFile.ImageFile 基类。此类应提供 _open 方法,它读取文件头并至少设置 modesize 属性。为了能够加载该文件,该方法还必须创建一个 tile 描述符,其中包含解码器名称、块的范围和任何特定于解码器的数据。必须通过调用 Image 模块。

注解

出于性能原因,重要的是 _open 方法快速拒绝没有适当内容的文件。

例子

下面的插件支持一个简单的格式,它有一个128字节的头,由单词“SPAM”组成,后跟宽度、高度和像素大小(以位为单位)。标题字段用空格隔开。图像数据紧跟在标题之后,可以是二级、灰度级或24位真彩色。

spamimageplugin.py版: :

from PIL import Image, ImageFile


def _accept(prefix):
    return prefix[:4] == b"SPAM"


class SpamImageFile(ImageFile.ImageFile):

    format = "SPAM"
    format_description = "Spam raster image"

    def _open(self):

        header = self.fp.read(128).split()

        # size in pixels (width, height)
        self._size = int(header[1]), int(header[2])

        # mode setting
        bits = int(header[3])
        if bits == 1:
            self.mode = "1"
        elif bits == 8:
            self.mode = "L"
        elif bits == 24:
            self.mode = "RGB"
        else:
            raise SyntaxError("unknown number of bits")

        # data descriptor
        self.tile = [("raw", (0, 0) + self.size, 128, (self.mode, 0, 1))]


Image.register_open(SpamImageFile.format, SpamImageFile, _accept)

Image.register_extensions(SpamImageFile.format, [
    ".spam",
    ".spa",  # DOS version
])

格式处理程序必须始终设置 sizemode 属性。如果未设置,则无法打开文件。为了简化插件,调用代码考虑异常,例如 SyntaxErrorKeyErrorIndexErrorEOFErrorstruct.error 无法识别文件。

请注意,必须使用 PIL.Image.register_open() . 尽管不是必需的,但最好注册此格式使用的任何扩展名。

导入插件后,可以使用它:

from PIL import Image
import SpamImagePlugin
with Image.open("hopper.spam") as im:
    pass

这个 tile 属性

为了能够读取该文件以及识别它, tile 还必须设置属性。这个属性包含一个平铺描述符列表,其中每个描述符指定如何将数据加载到图像中的给定区域。在大多数情况下,只使用一个描述符,覆盖整个图像。

tile描述符是一个包含以下内容的4元组:

(decoder, region, offset, parameters)

字段使用如下:

decoder

指定要使用的解码器。这个 raw 这里使用的解码器支持各种像素格式的未压缩数据。有关此解码器的详细信息,请参阅下面的说明。

region

一个4元组,指定在映像中存储数据的位置。

offset

从文件开头到图像数据的字节偏移量。

parameters

解码器的参数。此字段的内容取决于平铺描述符元组中第一个字段指定的解码器。如果解码器不需要任何参数,则使用 None 对于这个领域。

请注意 tile 属性包含一个平铺描述符列表,而不仅仅是一个描述符。

译码器

原始解码器

这个 raw 解码器用于从图像文件中读取未压缩的数据。它可以用于大多数未压缩的文件格式,如PPM、BMP、未压缩的TIFF和许多其他格式。将原始解码器与 PIL.Image.frombytes() 函数,请使用以下语法:

image = Image.frombytes(
    mode, size, data, "raw",
    raw mode, stride, orientation
    )

在tile描述符中使用时,参数字段应如下所示:

(raw mode, stride, orientation)

字段使用如下:

原始模式

文件中使用的像素布局,用于将数据正确转换为PIL的内部布局。有关可用格式的摘要,请参阅下表。

stride

图像中两个连续行之间的距离(字节)。如果为0,则假定图像是压缩的(行与行之间没有填充)。如果忽略,步幅默认为0。

orientation

图像中的第一行是屏幕上的顶行(1),还是底行(-1)。如果忽略,则方向默认为1。

这个 原始模式 字段用于确定如何解包数据以匹配PIL的内部像素布局。PIL支持大量的原始模式;有关完整的列表,请参阅中的表 Unpack.c 模块。下表介绍了一些常用的 原始模式

模式

描述

1

1位双层,存储时最左边的像素最多
有效位。0表示黑色,1表示白色。

1;I

1位反转的两级,与最左边的像素一起存储在
最高有效位。0表示白色,1表示黑色。

1;R

1位反转两级,与最左边的像素一起存储在
最低有效位。0表示黑色,1表示白色。

L

8位灰度。0表示黑色,255表示白色。

L;I

8位反转灰度。0表示白色,255表示黑色。

P

8位调色板映射图像。

RGB

24位真彩色,存储为(红、绿、蓝)。

BGR

24位真彩色,存储为(蓝色、绿色、红色)。

RGBX

24位真彩色,存储为(红、绿、蓝、垫)。垫子
像素可能会有所不同。

RGB;L

24位真彩色,线条交错(首先全部为红色像素,然后
全部为绿色像素,最后为全部蓝色像素)。

请注意,对于最常见的情况,原始模式与模式完全相同。

python图像库支持许多其他解码器,包括jpeg、png和packbits。有关详细信息,请参见 decode.c 源文件,以及随库提供的标准插件实现。

解码浮点数据

PIL提供了一些特殊的机制,允许您将各种格式加载到一个模式中。 F (浮点)图像存储器。

你可以使用 raw 解码器,使用以下原始模式之一读取数据打包为任何标准机器数据类型的图像:

模式

描述

F

32位本机浮点。

F;8

8位无符号整数。

F;8S

8位有符号整数。

F;16

16位小尾数无符号整数。

F;16S

16位小端有符号整数。

F;16B

16位大尾数无符号整数。

F;16BS

16位大尾数带符号整数。

F;16N

16位本机无符号整数。

F;16NS

16位本机有符号整数。

F;32

32位小尾数无符号整数。

F;32S

32位小端有符号整数。

F;32B

32位大尾数无符号整数。

F;32BS

32位大尾数带符号整数。

F;32N

32位本机无符号整数。

F;32NS

32位本机有符号整数。

F;32F

32位小尾数浮点数。

F;32BF

32位大端浮点。

F;32NF

32位本机浮点。

F;64F

64位小尾数浮点数。

F;64BF

64位大端浮点。

F;64NF

64位本机浮点。

位译码器

如果原始解码器无法处理您的格式,PIL还提供了一个特殊的“位”解码器,可用于将各种打包格式读取到浮点图像内存中。

将位解码器与 PIL.Image.frombytes() 函数,请使用以下语法:

image = Image.frombytes(
    mode, size, data, "bit",
    bits, pad, fill, sign, orientation
    )

在tile描述符中使用时,参数字段应如下所示:

(bits, pad, fill, sign, orientation)

字段使用如下:

bits

每像素位数(2-32)。没有默认值。

pad

行间填充,以位为单位。如果没有填充,则为0;如果行被填充为完整字节,则为8。如果忽略,填充值默认为8。

fill

控制如何将数据添加到解码器位缓冲区并从中存储数据。

**填充=0 **

向解码器缓冲区的LSB端添加字节;从MSB端存储像素。

**填充=1 **

将字节添加到解码器缓冲区的MSB端;从MSB端存储像素。

**填充=2 **

向解码器缓冲区的LSB端添加字节;从LSB端存储像素。

**填充=3 **

向解码器缓冲区的msb端添加字节;从lsb端存储像素。

如果忽略,则填充顺序默认为0。

sign

如果非零,位字段将被符号扩展。如果为零或忽略,则位字段无符号。

orientation

图像中的第一行是屏幕上的顶行(1),还是底行(-1)。如果忽略,则方向默认为1。

用C语言编写自己的文件解码器

文件解码器的生命周期有三个阶段:

  1. 设置: Pillow 在解码器注册表中查找函数,返回到名为的函数 [decodername]_decoder 在内部核心图像对象上。该函数是用 args 元组从 tile 设置在 _open 方法。

  2. 解码:解码器的解码功能通过图像数据块重复调用。

  3. 清除:如果解码器注册了清除函数,则在解码过程结束时将调用该函数,即使出现异常。

安装程序

当前的惯例是解码器设置函数的名称 PyImaging_[Decodername]DecoderNew 定义在 decode.c . 它的python绑定名为 [decodername]_decoder 从内部设置 _imaging.c 函数数组的codecs部分中的文件。

设置函数需要调用 PyImaging_DecoderNew 至少,设置 decode 函数指针。此对象中感兴趣的字段是:

decode

指向解码函数的函数指针,该函数可以访问 imstate 以及要添加到图像中的数据缓冲区。

cleanup

指向清除函数的函数指针,可以访问 state .

im

目标图像将由Pillow设置。

state

ImagingCodeStateInstance将由枕头设置。这个 context 成员是一个不透明结构,解码器可以使用它来存储任何特定于格式的状态或选项。

pulls_fd

EXPERIMENTAL —— WARNING, 接口可能会改变。如果设置为1, state->fd 将是指向类似于python文件的对象的指针。解码器可以使用 codec_fd.c 直接从类似对象的文件中读取,而不是将数据推送到缓冲区中。请注意,在删除此警告之前,可以重构此实现。

3.3.0 新版功能.

译码

使用目标(核心)图像、解码器状态结构和要解码的数据缓冲区调用解码函数。

Experimental ——如果 pulls_fd 设置,然后使用空缓冲区调用一次decode函数。译码器的责任是在那个调用中解码整个图块。本节其余部分仅适用于以下情况: pulls_fd 未设置。

解码器负责从缓冲区中提取尽可能多的数据,并返回所消耗的字节数。对解码器的下一个调用将包括以前未使用的尾部。当从类似文件的对象中读取数据时,解码器函数将被多次调用。

如果发生错误,设置 state->errcode 然后返回- 1。

成功时返回-1,不设置错误代码。

清理

如果解码器返回负值或文件中存在读取错误,则调用cleanup函数。这个函数应该释放所有分配的内存,并从外部库释放任何资源。

用python编写自己的文件解码器

python文件解码器应派生自 PIL.ImageFile.PyDecoder 至少应该重写解码方法。文件解码器应使用 PIL.Image.register_decoder() . 与文件解码器的C实现一样,基于Python的文件解码器的生命周期中有三个阶段:

  1. 设置:Pillow在注册表中查找解码器,然后实例化类。

  2. 解码:解码器实例的 decode 方法通过要解释的数据缓冲区重复调用。

  3. 清理:解码器实例的 cleanup 方法被调用。