编写自己的图像插件#

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:
            msg = "unknown number of bits"
            raise SyntaxError(msg)

        # 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 属性也必须设置。该属性由平铺描述符列表组成,其中每个描述符指定应如何将数据加载到图像中的给定区域。

在大多数情况下,只使用单个描述符,覆盖整个图像。 PsdImagePlugin.PsdImageFile 如果通道逐个单独存储,则使用多个平铺来组合单个层中的通道。

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

(decoder, region, offset, parameters)

字段使用如下:

decoder

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

中的函数数组的codecs部分下可以看到C解码器的列表 _imaging.c 。在相关插件中注册了Python解码器。

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)

字段使用如下:

raw_mode

文件中使用的像素布局,用于将数据正确转换为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编写您自己的文件编解码器#

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

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

  2. 转型:编解码器的 decodeencode 函数通过图像数据块被重复调用。

  3. 清理:如果编解码器注册了清理函数,则会在转换过程结束时调用该函数,即使出现异常也是如此。

安装程序#

当前的约定是将编解码器设置函数命名为 PyImaging_[codecname]DecoderNewPyImaging_[codecname]EncoderNew 并在中定义 decode.cencode.c 。它的Python绑定名为 [codecname]_decoder[codecname]_encoder ,并从 _imaging.c 函数数组的编解码器部分中的文件。

Setup函数需要调用 PyImaging_DecoderNewPyImaging_EncoderNew 而且至少,要设置 decodeencode 函数指针。此对象中感兴趣的字段包括:

decode / encode

指向解码或编码函数的函数指针,该函数可以访问 imstate ,以及要转换的数据的缓冲区。

cleanup

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

im

目标图像将由Pillow设置。

state

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

pulls_fd/pushes_fd

如果解码器有 pulls_fd 或者编码器有 pushes_fd 设置为1, state->fd 将是指向类似于Python文件的对象的指针。编解码器可以使用中的函数 codec_fd.c 直接读取或写入类似对象的文件,而不是通过缓冲区推送数据。

在 3.3.0 版本加入.

转型#

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

编解码器负责从缓冲区中拉出尽可能多的数据,并返回消耗的字节数。对编解码器的下一次调用将包括先前未使用的尾部。在处理数据时,编解码器函数将被多次调用。

或者,如果 pulls_fdpushes_fd 则使用空缓冲区调用一次deode或encode函数。编解码器负责在一次调用中转换整个磁贴。使用这一点将为编解码器提供更多自由,但如果编解码器一次将整个瓦片保存在存储器中,则这种自由可能意味着增加存储器使用量。

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

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

清理#

在编解码器返回负值后,或者如果出现错误,则调用Cleanup函数。此函数应释放所有分配的内存,并从外部库释放任何资源。

用Python语言编写您自己的文件编解码器#

Python文件解码器和编码器应派生自 PIL.ImageFile.PyDecoderPIL.ImageFile.PyEncoder ,并且至少应重写Decode或Encode方法。它们应使用以下方式注册 PIL.Image.register_decoder()PIL.Image.register_encoder() 。与文件编解码器的C实现一样,基于Python的文件编解码器的生命周期有三个阶段:

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

  2. 变换:实例的 decode 方法使用要解释的数据缓冲区重复调用,或者 encode 方法被重复调用,其大小为要输出的数据。

    或者,如果解码器的 _pulls_fd 属性(或编码器的 _pushes_fd 属性)设置为 True ,那么 decodeencode 将只被调用一次。在解码器中, self.fd 可用于访问类似文件的对象。使用此选项将为编解码器提供更多自由,但如果编解码器一次将整个文件保存在内存中,则这种自由可能意味着增加内存使用量。

    在……里面 decode ,一旦数据被解释, set_as_raw 可用于填充图像。

  3. 清理:实例的 cleanup 方法在转换完成后被调用。这可用于清理编解码器使用的任何资源。

    如果您设置为 _pulls_fd_pushes_fdTrue 但是,您可能选择在结束时执行任何清理任务 decodeencode

举个例子 PIL.ImageFile.PyDecoder ,见 DdsImagePlugin 。对于同时使用两者的插件 PIL.ImageFile.PyDecoderPIL.ImageFile.PyEncoder ,见 BlpImagePlugin