PEP 368 – 标准图像协议和类
- 作者:
- Lino Mastrodomenico <l.mastrodomenico at gmail.com>
- 状态:
- 推迟
- 类型:
- 标准跟踪
- 创建日期:
- 2007 年 6 月 28 日
- Python 版本:
- 2.6, 3.0
- 发布历史:
摘要
Python 世界中图像存储和操作的现状极其分散:几乎每个使用图像对象的库都实现了自己的图像类,彼此不兼容,而且通常不太符合 Python 风格。标准库中存在一个基本的 RGB 图像类(Tkinter.PhotoImage
),但除了 Tkinter 编程之外,它几乎无法使用,也无人使用。
这种碎片化不仅占用了开发人员宝贵的思维空间,而且还使得不同库之间(在相对常见的用例中需要)的图像交换比实际需要更慢、更复杂。
本 PEP 提议通过定义一个简单且符合 Python 风格的图像协议/接口来改善现状,希望能被标准库内外现有的图像类接受和实现,同时不破坏与其现有用户群的向后兼容性。实际上,这是对一个最小的类图像对象应该如何构造和行为的定义(类似于类文件对象中的read()
和 write()
方法)。
还提议将一个提供基本图像操作功能并实现新协议的类包含在标准库中,同时提供一个mixin类,用于帮助向现有图像类添加对该协议的支持。
PEP 延期
对本 PEP 中涵盖概念的进一步探索已被推迟,因为目前缺乏一位有兴趣推广 PEP 目标、收集和整合反馈,并有足够可用时间有效完成此任务的倡导者。
基本原理
让高质量模块准备好包含在 Python 标准库中的一个好方法是,简单地等待竞争的外部库通过自然选择,提供一个具有有用功能和庞大用户群的明确赢家。然后,可以通过将其包含在标准库中,来正式批准这个事实上的标准。
不幸的是,这种方法未能很好地在 Python 世界中创建主导图像类:几乎每个需要图像对象的第三方库都创建了自己的类,与其他库的类不兼容。这是一个真正的问题,因为程序使用例如 PIL (Python Imaging Library) 创建和操作图像,然后使用 wxPython 或 pygame 显示它是完全合理的。但这些库的图像类不同且不兼容,通常的解决方案是手动将图像从源“导出”到 (width, height, bytes_string) 元组,然后“导入”它,在目标格式中创建一个新实例。这种方法可行,但既丑陋又比实际需要慢。
另一种有时被使用的“解决方案”是创建从一个类到另一个类的特定适配器和/或转换器(例如,PIL 提供了 ImageTk
模块,用于将 PIL 图像转换为与 Tkinter 兼容的类)。但是这种方法对于涉及的库数量来说扩展性不佳,而且对用户来说仍然很烦人:如果我有一个完美的图像对象,为什么我必须在将其传递给下一个方法之前进行转换,为什么它不能简单地接受我的图像原样呢?
这个问题绝不限于上述三个库,它可能有多种原因,其中两个我认为在解决它之前理解非常重要:
- 在当今的计算世界中,图像是一种基本类型,不严格绑定到特定领域。这就是为什么上述三个库(PIL、wxPython 和 pygame)的图像类之间永远不会有明确的赢家:它们涵盖不同的领域,彼此之间并没有真正的竞争;
- Python 标准库从未提供过一个可以被第三方模块采用或模仿的优秀图像类。
Tkinter.PhotoImage
提供了基本的 RGB 功能,但它是其中最慢、最丑陋的,并且只能在 Tkinter 根窗口创建后才能实例化。
本 PEP 试图通过四种方式改善这种状况:
- 它定义了一个简单且符合 Python 风格的图像协议/接口(包括 Python 端和 C 端),希望能被标准库内外现有的图像类接受和实现,同时不破坏与其现有用户群的向后兼容性。
- 它提议在标准库中包含三个新类:
ImageMixin
提供了实现新协议所需的一切;其主要目的是使现有库尽可能简单地支持此接口,在某些情况下,甚至像将其添加到基类列表并对构造函数进行少量添加一样简单。Image
是ImageMixin
的子类,并将添加一个构造函数,该构造函数可以在不同的像素格式之间调整图像大小和/或转换图像。这旨在提供新协议的快速高效的默认实现。ImageSize
是一个小的辅助类。详情见下文。
Tkinter.PhotoImage
将实现新的协议(主要通过ImageMixin
类),并且所有可以接收图像的 Tkinter 方法都将被修改,以接受任何实现该接口的对象。此外,本 PEP 的作者将与最常见的外部库的开发人员合作,以实现相同的目标(在它们的类中支持该协议并接受任何实现该协议的类)。- 新的
PyImage_*
函数将添加到 CPython C API 中:它们实现了协议的 C 端,并接受 任何 支持它的对象作为第一个参数,即使它不是Image
/ImageMixin
类的实例。
最终用户将看到的主要效果是不同库之间图像交换的简化(如果一切顺利,任何 Python 库都将接受来自任何其他库的图像)以及新 Image
类的开箱即用。新类旨在涵盖简单但常见的用例,例如裁剪和/或调整照片大小以适应所需尺寸并将其传递给适当的窗口小部件以在窗口上显示,或者使纹理变暗并将其传递给 3D 库。
Image
类无意替代或与 PIL、Pythonmagick 或 NumPy 竞争,即使它提供了这三个库功能的一个(非常小的)子集。特别是 PIL 提供了非常丰富的图像操作功能,拥有数十个类、滤镜、变换和文件格式。将 PIL(或类似的东西)包含在标准库中可能是一个值得追求的目标,也可能不是,但这完全超出了本 PEP 的范围。
规范
imageop
模块被用作新类和新对象的默认位置,因为它长期以来托管了提供类似功能的函数,但如果更喜欢,也可以创建一个新模块(例如,一个新的“image
”或“media
”模块;后者最终可能包含其他多媒体类)。
MODES
是一个新的模块级常量:它是 Image
类支持的像素格式集。任何实现新协议的图像对象都保证采用这些模式之一进行格式化,但接受图像的库允许只支持其中的一个子集。
这些模式也以模块级常量的形式提供(例如 imageop.RGB
)。
下表总结了目前支持的模式及其属性:
名称 | 组件名称 | 每组件位数 | 子采样 | 有效间隔 |
---|---|---|---|---|
L | l (小写 L) | 8 | 否 | 全范围 |
L16 | l | 16 | 否 | 全范围 |
L32 | l | 32 | 否 | 全范围 |
LA | l, a | 8 | 否 | 全范围 |
LA32 | l, a | 16 | 否 | 全范围 |
RGB | r, g, b | 8 | 否 | 全范围 |
RGB48 | r, g, b | 16 | 否 | 全范围 |
RGBA | r, g, b, a | 8 | 否 | 全范围 |
RGBA64 | r, g, b, a | 16 | 否 | 全范围 |
YV12 | y, cr, cb | 8 | 1, 2, 2 | 16-235, 16-240, 16-240 |
JPEG_YV12 | y, cr, cb | 8 | 1, 2, 2 | 全范围 |
CMYK | c, m, y, k | 8 | 否 | 全范围 |
CMYK64 | c, m, y, k | 16 | 否 | 全范围 |
当模式名称以数字结尾时,它表示每个像素的平均位数。所有其他模式只使用每个组件每个像素一个字节。
不支持调色板模式或每个组件少于 8 位的模式。欢迎来到 21 世纪。
以下是模式的快速描述及其包含理由;共有四组模式:
- 灰度(
L*
模式):它们在科学计算中大量使用(这些人可能还需要非常高的动态范围和精度,因此有了L32
,这是唯一每个组件 32 位的模式),有时将彩色图像的单个分量视为灰度图像也很有用(平面图像的各个平面就使用了这一点,见下文YV12
);分量名称('l'
,小写字母 L)代表亮度,第二个可选分量('a'
)是 alpha 值,代表像素的不透明度:alpha = 0 表示完全透明,alpha = 255/65535 表示完全不透明的像素; - RGB* 模式:常见的彩色图像。可选的 alpha 组件与灰度模式中的含义相同;
- YCbCr,又名 YUV(
*YV12
模式)。这些模式是平面的(即每个分量的所有像素值都存储在连续的内存区域中,而不是通常的安排,即像素的所有分量都位于连续的字节中),并且使用 1、2、2(又名 4:2:0)子采样(即每个像素都有自己的 Y 值,但 Cb 和 Cr 分量在 2x2 相邻像素组之间共享),因为这是 YCbCr 图像最常见的格式。请注意,V (Cr) 平面存储在 U (Cb) 平面之前。YV12
通常用于 MPEG2(包括 DVD)、MPEG4(包括 ASP/DivX 和 AVC/H.264)和 Theora 视频帧。Y 的有效值在范围(16, 236)(不包括 236)内,Cb 和 Cr 的有效值在范围(16, 241)内。JPEG_YV12
类似于YV12
,但三个分量可以具有 256 个值的全范围。它是几乎所有 JPEG/JFIF 文件和 MJPEG 视频帧使用的原生格式。这两个模式相对于所有其他支持模式的“怪异之处”源于它们被大量现有库和应用程序广泛使用;这也是它们被包含的原因(以及它们不能无损转换为 RGB 的事实,因为 YCbCr 是一个更大的色彩空间);像素值的奇怪 4:2:0 平面排列相对容易支持,因为在大多数情况下,这三个平面可以被视为三个独立的灰度图像; - CMYK* 模式(青色、品红色、黄色和黑色)是减色模式,用于在纸张上打印彩色图像。专业设计师喜欢假装没有它们就活不下去,所以它们就在这里了。
Python API
请参阅下面的 示例。
在 Python 2.x 中,此处定义的所有新类都是新式类。
模式对象
模式对象提供了许多属性和方法,可用于实现适用于不同类型图像的通用算法:
组件
每个像素的组件数量(例如,RGBA 图像为 4)。
component_names
一个字符串元组;请参阅上表中的“组件名称”列。
bits_per_component
8、16 或 32;请参阅上表中的“每组件位数”。
bytes_per_pixel
components * bits_per_component // 8
,仅适用于非平面模式(见下文)。
planar
布尔值;如果图像组件各自位于单独的平面中,则为True
。目前,当且仅当模式使用子采样时,才会发生这种情况。
subsampling
一个元组,其中每个模式中的组件都包含一个包含两个整数的元组,分别表示水平和垂直方向上的下采样量。实际上,对于YV12
和JPEG_YV12
,它为((1, 1), (2, 2), (2, 2))
,对于其他所有模式,则为((1, 1),) * components
。
x_divisor
max(x for x, y in subsampling)
;使用此模式的图像宽度必须能被此值整除。
y_divisor
max(y for x, y in subsampling)
;使用此模式的图像高度必须能被此值整除。
intervals
一个元组,其中每个模式中的组件都包含一个包含两个整数的元组:组件的最小值和最大有效值。对于YV12
,其值为((16, 235), (16, 240), (16, 240))
,对于所有其他模式,则为((0, 2 ** bits_per_component - 1),) * components
。
get_length(iterable[integer]) -> int
参数必须是一个包含两个整数(图像的宽度和高度)的可迭代对象;它返回存储这些尺寸和此模式的图像所需的字节数。
实现细节:模式是 str
子类的实例,并且其值与其名称相等(例如 imageop.RGB == 'RGB'
),除了 L32
的值为 'I'
。这仅用于与现有 PIL 用户保持向后兼容性;使用此处提出的图像协议的新代码不应依赖此细节。
图像协议
任何支持图像协议的对象都必须提供以下方法和属性:
众数
此图像中像素的格式和排列;它是MODES
集合中的一个常量。
size
buffer
一个介于 0 到 255 之间的整数序列;它们是用于存储图像数据的实际字节(即,修改它们的值会影响图像像素,反之亦然);数据采用行主序/C 连续顺序,无填充,无任何特殊内存对齐,即使每个组件超过 8 位也是如此。唯一支持的方法是__len__
、__getitem__
/__setitem__
(带整数和切片索引)和__iter__
;在 C 端,它实现了缓冲区协议。这是一个相当低级的图像接口,用户有责任为每个组件超过 8 位的模式使用正确的(原生)字节序,并为
YV12
图像使用正确的值范围。缓冲区可能保持也可能不保持对其图像的引用,但即使在相应的图像已被垃圾回收器销毁之后,使用缓冲区仍然是安全的(如果无用)(这将需要更改 wxPython 的图像类以及可能其他库的图像类)。实现细节:这可以是array('B')
、bytes()
对象或专门的固定长度类型。
info
一个dict
对象,可以包含与图像相关的任意元数据(例如 DPI、伽马、ICC 配置文件、曝光时间……);此数据的解释超出了本 PEP 的范围,可能取决于用于创建和/或保存图像的库;如果图像的一个方法返回一个新图像,它可以从其自己的info
属性复制或调整元数据(ImageMixin
实现总是创建一个带有空info
字典的新图像)。
bits_per_component
bytes_per_pixel
component_names
组件
intervals
planar
subsampling
mode.*
对应属性的快捷方式。
map(function[, function...]) -> None
对于图像中的每个像素,通过相应的函数映射每个分量。如果只传递一个函数,它会反复用于每个分量。此方法会就地修改图像,并且通常速度非常快(大多数情况下,函数只被调用少量次数,对于没有分支的简单函数可能只调用一次),但它对传递的函数施加了一些限制:
- 它必须接受一个整数参数并返回一个数字(如有必要,
map
会将结果四舍五入到最近的整数并将其裁剪到range(0, 2 ** bits_per_component)
);- 它不得尝试拦截任何
BaseException
、Exception
或任何在参数上的任何操作引发的未知Exception
子类(实现可能会通过传递奇怪的对象来优化速度,因此即使是简单的"if n == 10:"
也可能引发异常:只需忽略它,map
会处理它);捕获任何其他异常是可以的;- 它应该是无副作用的,并且其结果不应依赖于在单个
map
调用期间可能改变的值(除了参数)。
rotate90() -> image
rotate180() -> image
rotate270() -> image
返回图像的副本,以其中心逆时针旋转 90、180 或 270 度。
clip() -> None
将YV12
图像中的无效组件值饱和到允许的最小值或最大值(请参阅mode.intervals
),对于其他图像模式,此方法不执行任何操作,速度非常快;鼓励保存/导出YV12
图像的库始终调用此方法,因为中间操作(例如map
方法)可能会为像素分配超出有效间隔的值。
split() -> tuple[image]
返回一个L
、L16
或L32
图像的元组,对应于图像中的各个分量。
平面图像还支持与 component_names
中定义的同名属性:它们包含灰度(模式 L
)图像,提供对相应组件像素值的视图;对子图像的任何更改都会立即反映到父图像上,反之亦然(它们的缓冲区指向相同的内存位置)。
非平面图像提供以下附加方法:
pixels() -> iterator[pixel]
返回一个迭代器,它迭代图像中的所有像素,从顶部行开始,并从左到右扫描每一行。有关 像素对象 的描述,请参见下文。
__iter__() -> iterator[line]
返回一个迭代器,它迭代图像中的所有行,从上到下。有关 行对象 的描述,请参见下文。
__len__() -> int
返回图像中的行数(size.height
)。
__getitem__(integer) -> line
返回指定(y)位置的行。
__getitem__(tuple[integer]) -> pixel
参数必须是一个包含两个整数的元组;它们分别解释为图像中的 x 和 y 坐标(0, 0 是左上角),并返回一个像素对象。
__getitem__(slice | tuple[integer | slice]) -> image
参数必须是一个切片或一个包含两个切片或一个整数和一个切片的元组;图像的选定区域被复制并返回一个新图像;image[x:y:z]
等同于image[:, x:y:z]
。
__setitem__(tuple[integer], integer | iterable[integer]) -> None
修改指定位置的像素;对于只有一个分量的图像,image[x, y] = integer
是image[x, y] = (integer,)
的快捷方式。
__setitem__(slice | tuple[integer | slice], image) -> None
以与__getitem__
方法的相应形式相同的方式选择一个区域,并为其分配第二个参数中图像的像素副本,该图像必须与此图像具有完全相同的模式,并且与指定区域具有相同的尺寸;如果存在 alpha 分量,则仅复制它,并且不影响图像的其他分量(即,不执行 alpha 合成)。
图像创建后,mode
、size
和 buffer
(包括 buffer
的内存地址)绝不会改变。
预计,如果 PEP 3118 被接受,所有图像对象都将支持新的缓冲区协议,但这超出了本 PEP 的范围。
Image
和 ImageMixin
类
ImageMixin
类实现了上述所有方法和属性,除了 mode
、size
、buffer
和 info
。Image
是 ImageMixin
的子类,它增加了对这四个属性的支持,并提供了以下构造函数(请注意,构造函数不属于图像协议):
__init__(mode, size, color, source)
mode
必须是MODES
集合中的常量之一,size
是两个整数的序列(新图像的宽度和高度);color
是整数序列,图像的每个组件一个,用于将所有像素初始化为相同的值;source
可以是适当大小和格式的整数序列,按原样复制到新图像的缓冲区中,也可以是现有图像;在 Python 2.x 中,source
也可以是str
的实例,并被解释为字节序列。color
和source
互斥,如果两者都省略,图像将初始化为透明黑色(在YV12
模式下,缓冲区中的所有字节值为 16,在CMYK*
模式下为 255,其他所有模式下为 0)。如果source
存在且是图像,则可以省略mode
和/或size
;如果指定了它们并且与源模式和/或大小不同,则源图像将被转换。用于调整大小和进行色彩空间转换的精确算法可能因 Python 版本和实现而异,但它们总是能提供高质量的结果(例如:上采样可以使用三次样条插值,下采样图像可以使用抗锯齿滤镜);支持任何模式转换组合,但用于转换为和从
CMYK*
模式的算法相当简单:如果您有设备的精确颜色配置文件,您可能需要使用 LittleCMS 等良好的颜色管理工具。新图像有一个空的info
dict
。
行对象
行对象(例如,在迭代图像时返回)支持以下属性和方法:
众数
此行来自的图像模式。
__iter__() -> iterator[pixel]
返回一个迭代器,它从左到右迭代行中的所有像素。有关 像素对象 的描述,请参见下文。
__len__() -> int
返回行中的像素数(图像宽度)。
__getitem__(integer) -> pixel
返回指定(x)位置的像素。
__getitem__(slice) -> image
行的选定部分被复制并返回一个新图像;新图像的高度始终为 1。
__setitem__(integer, integer | iterable[integer]) -> None
修改指定位置的像素;对于只有一个分量的图像,line[x] = integer
是line[x] = (integer,)
的快捷方式。
__setitem__(slice, image) -> None
选择行的一部分,并将第二个参数中图像的像素副本分配给它,该图像的高度必须为 1,宽度等于指定的切片,并且与此行具有相同的模式;如果存在 alpha 分量,则仅复制它,并且不影响图像的其他分量(即,不执行 alpha 合成)。
像素对象
像素对象(例如,当迭代一行时返回)支持以下属性和方法:
众数
此像素来自的图像模式。
value
一个整数元组,每个组件一个。任何长度正确的迭代器都可以赋值给value
(它会自动转换为元组),但您不能为其赋值一个整数,即使模式只有一个组件:请改用例如pixel.l = 123
。
r, g, b, a, l, c, m, y, k
每个组件的整数值;只有适用于当前模式的(在mode.component_names
中)才会可用。
__iter__() -> iterator[int]
__len__() -> int
__getitem__(integer | slice) -> int | tuple[int]
__setitem__(integer | slice, integer | iterable[integer]) -> None
这四个方法模拟了一个固定长度的整数列表,每个像素组件一个。
ImageSize
类
ImageSize
是一个命名元组,一个与 tuple
相同的类,但有以下例外:
- 它的构造函数只接受两个整数:宽度和高度;它们在构造函数中使用它们的
__index__()
方法进行转换,因此所有ImageSize
对象都保证只包含int
(或者在 Python 2.x 中可能是long
)实例; - 它有一个
width
和一个height
属性,分别等同于元组中的第一个和第二个数字; - 其
__repr__
方法返回的字符串是'imageop.ImageSize(width=%d, height=%d)' % (width, height)
。
ImageSize
通常不由终端用户实例化,但可以在创建实现图像协议的新类时使用,因为 size
属性必须是 ImageSize
实例。
C API
可用的图像模式在 C 级别可见为 PyObject *
类型的 PyImage_*
常量(例如:PyImage_RGB
是 imageop.RGB
)。
以下函数提供了一个 C 友好的模式和图像对象接口(所有函数在失败时返回 NULL
或 -1):
int PyImageMode_Check(PyObject *obj)
如果对象obj
是有效的图像模式,则返回 true。
int PyImageMode_GetComponents(PyObject *mode)
PyObject* PyImageMode_GetComponentNames(PyObject *mode)
int PyImageMode_GetBitsPerComponent(PyObject *mode)
int PyImageMode_GetBytesPerPixel(PyObject *mode)
int PyImageMode_GetPlanar(PyObject *mode)
PyObject* PyImageMode_GetSubsampling(PyObject *mode)
int PyImageMode_GetXDivisor(PyObject *mode)
int PyImageMode_GetYDivisor(PyObject *mode)
Py_ssize_t PyImageMode_GetLength(PyObject *mode, Py_ssize_t width, Py_ssize_t height)
这些函数等同于它们对应的 Python 属性或方法。
int PyImage_Check(PyObject *obj)
如果对象obj
是Image
对象或Image
类型的子类型实例,则返回 true;另请参阅下面的PyObject_CheckImage
。
int PyImage_CheckExact(PyObject *obj)
如果对象obj
是Image
对象,但不是Image
类型的子类型实例,则返回 true。
PyObject* PyImage_New(PyObject *mode, Py_ssize_t width, Py_ssize_t height)
返回一个新的Image
实例,初始化为透明黑色(详细信息请参阅上面的Image.__init__
)。
PyObject* PyImage_FromImage(PyObject *image, PyObject *mode, Py_ssize_t width, Py_ssize_t height)
返回一个新的Image
实例,必要时,用image
对象的内容进行重新缩放并转换为指定的mode
后进行初始化。
PyObject* PyImage_FromBuffer(PyObject *buffer, PyObject *mode, Py_ssize_t width, Py_ssize_t height)
返回一个新的Image
实例,使用buffer
对象的内容进行初始化。
int PyObject_CheckImage(PyObject *obj)
如果对象obj
实现了图像协议的足够子集以被下面定义的函数接受,则返回 true,即使其类不是ImageMixin
和/或Image
的子类。目前,它只检查属性mode
、size
和buffer
的存在和正确性。
PyObject* PyImage_GetMode(PyObject *image)
Py_ssize_t PyImage_GetWidth(PyObject *image)
Py_ssize_t PyImage_GetHeight(PyObject *image)
int PyImage_Clip(PyObject *image)
PyObject* PyImage_Split(PyObject *image)
PyObject* PyImage_GetBuffer(PyObject *image)
int PyImage_AsBuffer(PyObject *image, const void **buffer, Py_ssize_t *buffer_len)
这些函数等效于它们对应的 Python 属性或方法;图像内存只能通过 GIL 和对图像或其缓冲区的引用来访问,并且对于每个组件超过 8 位的模式应格外小心:数据以原生字节顺序存储,并且可能不与 2 或 4 字节边界对齐。
示例
新 Image
类和协议的常见操作示例:
# create a new black RGB image of 6x9 pixels
rgb_image = imageop.Image(imageop.RGB, (6, 9))
# same as above, but initialize the image to bright red
rgb_image = imageop.Image(imageop.RGB, (6, 9), color=(255, 0, 0))
# convert the image to YCbCr
yuv_image = imageop.Image(imageop.JPEG_YV12, source=rgb_image)
# read the value of a pixel and split it into three ints
r, g, b = rgb_image[x, y]
# modify the magenta component of a pixel in a CMYK image
cmyk_image[x, y].m = 13
# modify the Y (luma) component of a pixel in a *YV12 image and
# its corresponding subsampled Cr (red chroma)
yuv_image.y[x, y] = 42
yuv_image.cr[x // 2, y // 2] = 54
# iterate over an image
for line in rgb_image:
for pixel in line:
# swap red and blue, and set green to 0
pixel.value = pixel.b, 0, pixel.r
# find the maximum value of the red component in the image
max_red = max(pixel.r for pixel in rgb_image.pixels())
# count the number of colors in the image
num_of_colors = len(set(tuple(pixel) for pixel in image.pixels()))
# copy a block of 4x2 pixels near the upper right corner of an
# image and paste it into the lower left corner of the same image
image[:4, -2:] = image[-6:-2, 1:3]
# create a copy of the image, except that the new image can have a
# different (usually empty) info dict
new_image = image[:]
# create a mirrored copy of the image, with the left and right
# sides flipped
flipped_image = image[::-1, :]
# downsample an image to half its original size using a fast, low
# quality operation and a slower, high quality one:
low_quality_image = image[::2, ::2]
new_size = image.size.width // 2, image.size.height // 2
high_quality_image = imageop.Image(size=new_size, source=image)
# direct buffer access
rgb_image[0, 0] = r, g, b
assert tuple(rgb_image.buffer[:3]) == (r, g, b)
向后兼容性
本 PEP 涉及三个需要考虑向后兼容性的领域:
- Python 2.6: 新类和对象被添加到
imageop
模块中,而不触及现有模块内容;新方法和属性将添加到Tkinter.PhotoImage
中,并且其__getitem__
和__setitem__
方法将被修改,以接受整数、元组和切片(目前它们只接受字符串)。所有更改都提供了现有功能的超集,因此预计不会出现重大的兼容性问题。 - Python 3.0:根据 PEP 3108,
imageop
模块的旧内容将被删除;此提案中定义的所有内容将像 Python 2.x 中一样工作,除了常见的 2.x/3.0 差异(例如,将取消对long
整数和将str
实例解释为字节序列的支持)。 - 外部库:标准图像方法和属性的名称和语义经过精心选择,以允许某些操作图像的外部库(至少包括 PIL、wxPython 和 pygame)在其图像类中实现新协议,而不会破坏与现有代码的兼容性。图像协议和 NumPy 数组之间唯一明显冲突的是
size
属性的值和image[x, y]
表达式中的坐标顺序。
参考实现
如果此 PEP 获得接受,作者将提供一个纯 Python 的新类参考实现(可在 CPython、PyPy、Jython 和 IronPython 中运行),以及第二个针对 Python 和 C 进行了速度优化的实现,适合包含在 CPython 标准库中。作者还将提交所需的 Tkinter 补丁。所有代码都将提供 Python 2.x 版本和 Python 3.0 版本(预计这两个版本将非常相似,并且 Python 3.0 版本可能几乎完全自动生成)。
致谢
如果此 PEP 获得接受,其实现将由 Google 通过 Google Summer of Code 计划赞助。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0368.rst