2007-06-30 15:07:03 -04:00
|
|
|
|
PEP: 368
|
|
|
|
|
Title: Standard image protocol and class
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Lino Mastrodomenico <l.mastrodomenico@gmail.com>
|
2013-05-18 03:50:40 -04:00
|
|
|
|
Status: Deferred
|
2007-06-30 15:07:03 -04:00
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 28-Jun-2007
|
|
|
|
|
Python-Version: 2.6, 3.0
|
|
|
|
|
Post-History:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
The current situation of image storage and manipulation in the Python
|
|
|
|
|
world is extremely fragmented: almost every library that uses image
|
|
|
|
|
objects has implemented its own image class, incompatible with
|
|
|
|
|
everyone else's and often not very pythonic. A basic RGB image class
|
|
|
|
|
exists in the standard library (``Tkinter.PhotoImage``), but is pretty
|
|
|
|
|
much unusable, and unused, for anything except Tkinter programming.
|
|
|
|
|
|
|
|
|
|
This fragmentation not only takes up valuable space in the developers
|
|
|
|
|
minds, but also makes the exchange of images between different
|
|
|
|
|
libraries (needed in relatively common use cases) slower and more
|
|
|
|
|
complex than it needs to be.
|
|
|
|
|
|
|
|
|
|
This PEP proposes to improve the situation by defining a simple and
|
|
|
|
|
pythonic image protocol/interface that can be hopefully accepted and
|
|
|
|
|
implemented by existing image classes inside and outside the standard
|
|
|
|
|
library *without breaking backward compatibility* with their existing
|
|
|
|
|
user bases. In practice this is a definition of how a minimal
|
|
|
|
|
*image-like* object should look and act (in a similar way to the
|
|
|
|
|
``read()`` and ``write()`` methods in *file-like* objects).
|
|
|
|
|
|
|
|
|
|
The inclusion in the standard library of a class that provides basic
|
|
|
|
|
image manipulation functionality and implements the new protocol is
|
|
|
|
|
also proposed, together with a mixin class that helps adding support
|
|
|
|
|
for the protocol to existing image classes.
|
|
|
|
|
|
2013-05-18 03:50:40 -04:00
|
|
|
|
PEP Deferral
|
|
|
|
|
============
|
|
|
|
|
|
|
|
|
|
Further exploration of the concepts covered in this PEP has been deferred
|
|
|
|
|
for lack of a current champion interested in promoting the goals of the PEP
|
|
|
|
|
and collecting and incorporating feedback, and with sufficient available
|
|
|
|
|
time to do so effectively.
|
2007-06-30 15:07:03 -04:00
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
A good way to have high quality modules ready for inclusion in the
|
|
|
|
|
Python standard library is to simply wait for natural selection among
|
|
|
|
|
competing external libraries to provide a clear winner with useful
|
2016-07-11 11:14:08 -04:00
|
|
|
|
functionality and a big user base. Then the de facto standard can be
|
2007-06-30 15:07:03 -04:00
|
|
|
|
officially sanctioned by including it in the standard library.
|
|
|
|
|
|
|
|
|
|
Unfortunately this approach hasn't worked well for the creation of a
|
|
|
|
|
dominant image class in the Python world: almost every third-party
|
|
|
|
|
library that requires an image object creates its own class
|
|
|
|
|
incompatible with the ones from other libraries. This is a real
|
|
|
|
|
problem because it's entirely reasonable for a program to create and
|
|
|
|
|
manipulate an image using, e.g., PIL (the Python Imaging Library) and
|
|
|
|
|
then display it using wxPython or pygame. But these libraries have
|
|
|
|
|
different and incompatible image classes, and the usual solution is to
|
|
|
|
|
manually "export" an image from the source to a (width, height,
|
|
|
|
|
bytes_string) tuple and "import" it creating a new instance in the
|
|
|
|
|
target format. This approach *works*, but is both uglier and slower
|
|
|
|
|
than it needs to be.
|
|
|
|
|
|
|
|
|
|
Another "solution" that has been sometimes used is the creation of
|
|
|
|
|
specific adapters and/or converters from a class to another (e.g. PIL
|
|
|
|
|
offers the ``ImageTk`` module for converting PIL images to a class
|
|
|
|
|
compatible with the Tkinter one). But this approach doesn't scale
|
|
|
|
|
well with the number of libraries involved and it's still annoying for
|
|
|
|
|
the user: if I have a perfectly good image object why should I convert
|
|
|
|
|
before passing it to the next method, why can't it simply accept my
|
|
|
|
|
image as-is?
|
|
|
|
|
|
|
|
|
|
The problem isn't by any stretch limited to the three mentioned
|
|
|
|
|
libraries and has probably multiple causes, including two that IMO are
|
|
|
|
|
very important to understand before solving it:
|
|
|
|
|
|
|
|
|
|
* in today's computing world an image is a basic type not strictly
|
|
|
|
|
tied to a specific domain. This is why there will never be a clear
|
|
|
|
|
winner between the image classes from the three libraries mentioned
|
|
|
|
|
above (PIL, wxPython and pygame): they cover different domains and
|
|
|
|
|
don't really compete with each other;
|
|
|
|
|
|
|
|
|
|
* the Python standard library has never provided a good image class
|
|
|
|
|
that can be adopted or imitated by third part modules.
|
|
|
|
|
``Tkinter.PhotoImage`` provides basic RGB functionality, but it's by
|
|
|
|
|
far the slowest and ugliest of the bunch and it can be instantiated
|
|
|
|
|
only after the Tkinter root window has been created.
|
|
|
|
|
|
|
|
|
|
This PEP tries to improve this situation in four ways:
|
|
|
|
|
|
|
|
|
|
1. It defines a simple and pythonic image protocol/interface (both on
|
|
|
|
|
the Python and the C side) that can be hopefully accepted and
|
|
|
|
|
implemented by existing image classes inside and outside the
|
|
|
|
|
standard library *without breaking backward compatibility* with
|
|
|
|
|
their existing user bases.
|
|
|
|
|
|
|
|
|
|
2. It proposes the inclusion in the standard library of three new
|
|
|
|
|
classes:
|
|
|
|
|
|
|
|
|
|
* ``ImageMixin`` provides almost everything necessary to implement
|
|
|
|
|
the new protocol; its main purpose is to make as simple as
|
|
|
|
|
possible to support this interface for existing libraries, in
|
|
|
|
|
some cases as simple as adding it to the list of base classes and
|
|
|
|
|
doing minor additions to the constructor.
|
|
|
|
|
|
|
|
|
|
* ``Image`` is a subclass of ``ImageMixin`` and will add a
|
|
|
|
|
constructor that can resize and/or convert an image between
|
|
|
|
|
different pixel formats. This is intended to provide a fast and
|
|
|
|
|
efficient default implementation of the new protocol.
|
|
|
|
|
|
|
|
|
|
* ``ImageSize`` is a minor helper class. See below for details.
|
|
|
|
|
|
|
|
|
|
3. ``Tkinter.PhotoImage`` will implement the new protocol (mostly
|
|
|
|
|
through the ``ImageMixin`` class) and all the Tkinter methods that
|
|
|
|
|
can receive an image will be modified the accept any object that
|
|
|
|
|
implements the interface. As an aside the author of this PEP will
|
|
|
|
|
collaborate with the developers of the most common external
|
|
|
|
|
libraries to achieve the same goal (supporting the protocol in
|
|
|
|
|
their classes and accepting any class that implements it).
|
|
|
|
|
|
|
|
|
|
4. New ``PyImage_*`` functions will be added to the CPython C API:
|
|
|
|
|
they implement the C side of the protocol and accept as first
|
|
|
|
|
parameter **any** object that supports it, even if it isn't an
|
|
|
|
|
instance of the ``Image``/``ImageMixin`` classes.
|
|
|
|
|
|
|
|
|
|
The main effects for the end user will be a simplification of the
|
|
|
|
|
interchange of images between different libraries (if everything goes
|
|
|
|
|
well, any Python library will accept images from any other library)
|
|
|
|
|
and the out-of-the-box availability of the new ``Image`` class. The
|
|
|
|
|
new class is intended to cover simple but common use cases like
|
|
|
|
|
cropping and/or resizing a photograph to the desired size and passing
|
|
|
|
|
it an appropriate widget for displaying it on a window, or darkening a
|
|
|
|
|
texture and passing it to a 3D library.
|
|
|
|
|
|
|
|
|
|
The ``Image`` class is not intended to replace or compete with PIL,
|
|
|
|
|
Pythonmagick or NumPy, even if it provides a (very small) subset of
|
|
|
|
|
the functionality of these three libraries. In particular PIL offers
|
|
|
|
|
very rich image manipulation features with *dozens* of classes,
|
|
|
|
|
filters, transformations and file formats. The inclusion of PIL (or
|
|
|
|
|
something similar) in the standard library may, or may not, be a
|
|
|
|
|
worthy goal but it's completely outside the scope of this PEP.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Specification
|
|
|
|
|
=============
|
|
|
|
|
|
|
|
|
|
The ``imageop`` module is used as the *default* location for the new
|
|
|
|
|
classes and objects because it has for a long time hosted functions
|
|
|
|
|
that provided a somewhat similar functionality, but a new module may
|
|
|
|
|
be created if preferred (e.g. a new "``image``" or "``media``" module;
|
|
|
|
|
the latter may eventually include other multimedia classes).
|
|
|
|
|
|
|
|
|
|
``MODES`` is a new module level constant: it is a set of the pixel
|
|
|
|
|
formats supported by the ``Image`` class. Any image object that
|
|
|
|
|
implements the new protocol is guaranteed to be formatted in one of
|
|
|
|
|
these modes, but libraries that accept images are allowed to support
|
|
|
|
|
only a subset of them.
|
|
|
|
|
|
|
|
|
|
These modes are in turn also available as module level constants (e.g.
|
|
|
|
|
``imageop.RGB``).
|
|
|
|
|
|
|
|
|
|
The following table is a summary of the modes currently supported and
|
|
|
|
|
their properties:
|
|
|
|
|
|
|
|
|
|
========= =============== ========= =========== ======================
|
|
|
|
|
Name Component Bits per Subsampling Valid
|
|
|
|
|
names component intervals
|
|
|
|
|
========= =============== ========= =========== ======================
|
|
|
|
|
L l (lowercase L) 8 no full range
|
|
|
|
|
L16 l 16 no full range
|
|
|
|
|
L32 l 32 no full range
|
|
|
|
|
LA l, a 8 no full range
|
|
|
|
|
LA32 l, a 16 no full range
|
|
|
|
|
RGB r, g, b 8 no full range
|
|
|
|
|
RGB48 r, g, b 16 no full range
|
|
|
|
|
RGBA r, g, b, a 8 no full range
|
|
|
|
|
RGBA64 r, g, b, a 16 no full range
|
|
|
|
|
YV12 y, cr, cb 8 1, 2, 2 16-235, 16-240, 16-240
|
|
|
|
|
JPEG_YV12 y, cr, cb 8 1, 2, 2 full range
|
|
|
|
|
CMYK c, m, y, k 8 no full range
|
|
|
|
|
CMYK64 c, m, y, k 16 no full range
|
|
|
|
|
========= =============== ========= =========== ======================
|
|
|
|
|
|
|
|
|
|
When the name of a mode ends with a number, it represents the average
|
|
|
|
|
number of bits per pixel. All the other modes simply use a byte per
|
|
|
|
|
component per pixel.
|
|
|
|
|
|
|
|
|
|
No palette modes or modes with less than 8 bits per component are
|
|
|
|
|
supported. Welcome to the 21st century.
|
|
|
|
|
|
|
|
|
|
Here's a quick description of the modes and the rationale for their
|
|
|
|
|
inclusion; there are four groups of modes:
|
|
|
|
|
|
|
|
|
|
1. **grayscale** (``L*`` modes): they are heavily used in scientific
|
|
|
|
|
computing (those people may also need a very high dynamic range and
|
|
|
|
|
precision, hence ``L32``, the only mode with 32 bits per component)
|
|
|
|
|
and sometimes it can be useful to consider a single component of a
|
|
|
|
|
color image as a grayscale image (this is used by the individual
|
|
|
|
|
planes of the planar images, see ``YV12`` below); the name of the
|
|
|
|
|
component (``'l'``, lowercase letter L) stands for luminance, the
|
|
|
|
|
second optional component (``'a'``) is the alpha value and
|
|
|
|
|
represents the opacity of the pixels: alpha = 0 means full
|
|
|
|
|
transparency, alpha = 255/65535 represents a fully opaque pixel;
|
|
|
|
|
|
|
|
|
|
2. **RGB\* modes**: the garden variety color images. The optional
|
|
|
|
|
alpha component has the same meaning as in grayscale modes;
|
|
|
|
|
|
|
|
|
|
3. **YCbCr**, a.k.a. YUV (``*YV12`` modes). These modes are planar
|
|
|
|
|
(i.e. the values of all the pixel for each component are stored in
|
|
|
|
|
a consecutive memory area, instead of the usual arrangement where
|
|
|
|
|
all the components of a pixel reside in consecutive bytes) and use
|
|
|
|
|
a 1, 2, 2 (a.k.a. 4:2:0) subsampling (i.e. each pixel has its own Y
|
|
|
|
|
value, but the Cb and Cr components are shared between groups of
|
|
|
|
|
2x2 adjacent pixels) because this is the format that's by far the
|
|
|
|
|
most common for YCbCr images. Please note that the V (Cr) plane is
|
|
|
|
|
stored before the U (Cb) plane.
|
|
|
|
|
|
|
|
|
|
``YV12`` is commonly used for MPEG2 (including DVDs), MPEG4 (both
|
|
|
|
|
ASP/DivX and AVC/H.264) and Theora video frames. Valid values for
|
|
|
|
|
Y are in range(16, 236) (excluding 236), and valid values for Cb
|
|
|
|
|
and Cr are in range(16, 241). ``JPEG_YV12`` is similar to
|
|
|
|
|
``YV12``, but the three components can have the full range of 256
|
|
|
|
|
values. It's the native format used by almost all JPEG/JFIF files
|
|
|
|
|
and by MJPEG video frames. The "strangeness" of these two wrt all
|
|
|
|
|
the other supported modes derives from the fact that they are
|
|
|
|
|
widely used that way by a lot of existing libraries and
|
|
|
|
|
applications; this is also the reason why they are included (and
|
|
|
|
|
the fact that they can't losslessly converted to RGB because YCbCr
|
|
|
|
|
is a bigger color space); the funny 4:2:0 planar arrangement of the
|
|
|
|
|
pixel values is relatively easy to support because in most cases
|
|
|
|
|
the three planes can be considered three separate grayscale images;
|
|
|
|
|
|
|
|
|
|
4. **CMYK\* modes** (cyan, magenta, yellow and black) are subtractive
|
|
|
|
|
color modes, used for printing color images on dead trees.
|
|
|
|
|
Professional designers love to pretend that they can't live without
|
|
|
|
|
them, so here they are.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Python API
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
See the examples_ below.
|
|
|
|
|
|
|
|
|
|
In Python 2.x, all the new classes defined here are new-style classes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Mode Objects
|
|
|
|
|
''''''''''''
|
|
|
|
|
|
|
|
|
|
The mode objects offer a number of attributes and methods that can be
|
|
|
|
|
used for implementing generic algorithms that work on different types
|
|
|
|
|
of images:
|
|
|
|
|
|
|
|
|
|
``components``
|
|
|
|
|
|
|
|
|
|
The number of components per pixel (e.g. 4 for an RGBA image).
|
|
|
|
|
|
|
|
|
|
``component_names``
|
|
|
|
|
|
|
|
|
|
A tuple of strings; see the column "Component names" in the above
|
|
|
|
|
table.
|
|
|
|
|
|
|
|
|
|
``bits_per_component``
|
|
|
|
|
|
|
|
|
|
8, 16 or 32; see "Bits per component" in the above table.
|
|
|
|
|
|
|
|
|
|
``bytes_per_pixel``
|
|
|
|
|
|
|
|
|
|
``components * bits_per_component // 8``, only available for non
|
|
|
|
|
planar modes (see below).
|
|
|
|
|
|
|
|
|
|
``planar``
|
|
|
|
|
|
|
|
|
|
Boolean; ``True`` if the image components reside each in a
|
|
|
|
|
separate plane. Currently this happens if and only if the mode
|
|
|
|
|
uses subsampling.
|
|
|
|
|
|
|
|
|
|
``subsampling``
|
|
|
|
|
|
|
|
|
|
A tuple that for each component in the mode contains a tuple of
|
|
|
|
|
two integers that represent the amount of downsampling in the
|
|
|
|
|
horizontal and vertical direction, respectively. In practice it's
|
|
|
|
|
``((1, 1), (2, 2), (2, 2))`` for ``YV12`` and ``JPEG_YV12`` and
|
|
|
|
|
``((1, 1),) * components`` for everything else.
|
|
|
|
|
|
|
|
|
|
``x_divisor``
|
|
|
|
|
|
|
|
|
|
``max(x for x, y in subsampling)``; the width of an image that
|
|
|
|
|
uses this mode must be divisible for this value.
|
|
|
|
|
|
|
|
|
|
``y_divisor``
|
|
|
|
|
|
|
|
|
|
``max(y for x, y in subsampling)``; the height of an image that
|
|
|
|
|
uses this mode must be divisible for this value.
|
|
|
|
|
|
|
|
|
|
``intervals``
|
|
|
|
|
|
|
|
|
|
A tuple that for each component in the mode contains a tuple of
|
|
|
|
|
two integers: the minimum and maximum valid value for the
|
|
|
|
|
component. Its value is ``((16, 235), (16, 240), (16, 240))`` for
|
|
|
|
|
``YV12`` and ``((0, 2 ** bits_per_component - 1),) * components``
|
|
|
|
|
for everything else.
|
|
|
|
|
|
|
|
|
|
``get_length(iterable[integer]) -> int``
|
|
|
|
|
|
|
|
|
|
The parameter must be an iterable that contains two integers: the
|
|
|
|
|
width and height of an image; it returns the number of bytes
|
|
|
|
|
needed to store an image of these dimensions with this mode.
|
|
|
|
|
|
|
|
|
|
Implementation detail: the modes are instances of a subclass of
|
|
|
|
|
``str`` and have a value equal to their name (e.g. ``imageop.RGB ==
|
|
|
|
|
'RGB'``) except for ``L32`` that has value ``'I'``. This is only
|
|
|
|
|
intended for backward compatibility with existing PIL users; new code
|
|
|
|
|
that uses the image protocol proposed here should not rely on this
|
|
|
|
|
detail.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Image Protocol
|
|
|
|
|
''''''''''''''
|
|
|
|
|
|
|
|
|
|
Any object that supports the image protocol must provide the following
|
|
|
|
|
methods and attributes:
|
|
|
|
|
|
|
|
|
|
``mode``
|
|
|
|
|
|
|
|
|
|
The format and the arrangement of the pixels in this image; it's
|
|
|
|
|
one of the constants in the ``MODES`` set.
|
|
|
|
|
|
|
|
|
|
``size``
|
|
|
|
|
|
|
|
|
|
An instance of the `ImageSize class`_; it's a named tuple of two
|
|
|
|
|
integers: the width and the height of the image in pixels; both of
|
|
|
|
|
them must be >= 1 and can also be accessed as the ``width`` and
|
|
|
|
|
``height`` attributes of ``size``.
|
|
|
|
|
|
|
|
|
|
``buffer``
|
|
|
|
|
|
|
|
|
|
A sequence of integers between 0 and 255; they are the actual
|
|
|
|
|
bytes used for storing the image data (i.e. modifying their values
|
|
|
|
|
affects the image pixels and vice versa); the data has a
|
|
|
|
|
row-major/C-contiguous order without padding and without any
|
|
|
|
|
special memory alignment, even when there are more than 8 bits per
|
|
|
|
|
component. The only supported methods are ``__len__``,
|
|
|
|
|
``__getitem__``/``__setitem__`` (with both integers and slice
|
|
|
|
|
indexes) and ``__iter__``; on the C side it implements the buffer
|
|
|
|
|
protocol.
|
|
|
|
|
|
|
|
|
|
This is a pretty low level interface to the image and the user is
|
|
|
|
|
responsible for using the correct (native) byte order for modes
|
|
|
|
|
with more than 8 bit per component and the correct value ranges
|
|
|
|
|
for ``YV12`` images. A buffer may or may not keep a reference to
|
|
|
|
|
its image, but it's still safe (if useless) to use the buffer even
|
|
|
|
|
after the corresponding image has been destroyed by the garbage
|
|
|
|
|
collector (this will require changes to the image class of
|
|
|
|
|
wxPython and possibly other libraries). Implementation detail:
|
|
|
|
|
this can be an ``array('B')``, a ``bytes()`` object or a
|
|
|
|
|
specialized fixed-length type.
|
|
|
|
|
|
|
|
|
|
``info``
|
|
|
|
|
|
|
|
|
|
A ``dict`` object that can contain arbitrary metadata associated
|
|
|
|
|
with the image (e.g. DPI, gamma, ICC profile, exposure time...);
|
|
|
|
|
the interpretation of this data is beyond the scope of this PEP
|
|
|
|
|
and probably depends on the library used to create and/or to save
|
|
|
|
|
the image; if a method of the image returns a new image, it can
|
|
|
|
|
copy or adapt metadata from its own ``info`` attribute (the
|
|
|
|
|
``ImageMixin`` implementation always creates a new image with an
|
|
|
|
|
empty ``info`` dictionary).
|
|
|
|
|
|
|
|
|
|
| ``bits_per_component``
|
|
|
|
|
| ``bytes_per_pixel``
|
|
|
|
|
| ``component_names``
|
|
|
|
|
| ``components``
|
|
|
|
|
| ``intervals``
|
|
|
|
|
| ``planar``
|
|
|
|
|
| ``subsampling``
|
|
|
|
|
|
|
|
|
|
Shortcuts for the corresponding ``mode.*`` attributes.
|
|
|
|
|
|
|
|
|
|
``map(function[, function...]) -> None``
|
|
|
|
|
|
|
|
|
|
For every pixel in the image, maps each component through the
|
|
|
|
|
corresponding function. If only one function is passed, it is
|
|
|
|
|
used repeatedly for each component. This method modifies the
|
|
|
|
|
image **in place** and is usually very fast (most of the time the
|
|
|
|
|
functions are called only a small number of times, possibly only
|
|
|
|
|
once for simple functions without branches), but it imposes a
|
|
|
|
|
number of restrictions on the function(s) passed:
|
|
|
|
|
|
|
|
|
|
* it must accept a single integer argument and return a number
|
|
|
|
|
(``map`` will round the result to the nearest integer and clip
|
|
|
|
|
it to ``range(0, 2 ** bits_per_component)``, if necessary);
|
|
|
|
|
|
|
|
|
|
* it must *not* try to intercept any ``BaseException``,
|
|
|
|
|
``Exception`` or any unknown subclass of ``Exception`` raised by
|
|
|
|
|
any operation on the argument (implementations may try to
|
|
|
|
|
optimize the speed by passing funny objects, so even a simple
|
|
|
|
|
``"if n == 10:"`` may raise an exception: simply ignore it,
|
|
|
|
|
``map`` will take care of it); catching any other exception is
|
|
|
|
|
fine;
|
|
|
|
|
|
|
|
|
|
* it should be side-effect free and its result should not depend
|
|
|
|
|
on values (other than the argument) that may change during a
|
|
|
|
|
single invocation of ``map``.
|
|
|
|
|
|
|
|
|
|
| ``rotate90() -> image``
|
|
|
|
|
| ``rotate180() -> image``
|
|
|
|
|
| ``rotate270() -> image``
|
|
|
|
|
|
|
|
|
|
Return a copy of the image rotated 90, 180 or 270 degrees
|
|
|
|
|
counterclockwise around its center.
|
|
|
|
|
|
|
|
|
|
``clip() -> None``
|
|
|
|
|
|
|
|
|
|
Saturates invalid component values in ``YV12`` images to the
|
|
|
|
|
minimum or the maximum allowed (see ``mode.intervals``), for other
|
|
|
|
|
image modes this method does nothing, very fast; libraries that
|
|
|
|
|
save/export ``YV12`` images are encouraged to always call this
|
|
|
|
|
method, since intermediate operations (e.g. the ``map`` method)
|
|
|
|
|
may assign to pixels values outside the valid intervals.
|
|
|
|
|
|
|
|
|
|
``split() -> tuple[image]``
|
|
|
|
|
|
|
|
|
|
Returns a tuple of ``L``, ``L16`` or ``L32`` images corresponding
|
|
|
|
|
to the individual components in the image.
|
|
|
|
|
|
|
|
|
|
Planar images also supports attributes with the same names defined in
|
|
|
|
|
``component_names``: they contain grayscale (mode ``L``) images that
|
|
|
|
|
offer a view on the pixel values for the corresponding component; any
|
|
|
|
|
change to the subimages is immediately reflected on the parent image
|
|
|
|
|
and vice versa (their buffers refer to the same memory location).
|
|
|
|
|
|
|
|
|
|
Non-planar images offer the following additional methods:
|
|
|
|
|
|
|
|
|
|
``pixels() -> iterator[pixel]``
|
|
|
|
|
|
|
|
|
|
Returns an iterator that iterates over all the pixels in the
|
|
|
|
|
image, starting from the top line and scanning each line from left
|
|
|
|
|
to right. See below for a description of the `pixel objects`_.
|
|
|
|
|
|
|
|
|
|
``__iter__() -> iterator[line]``
|
|
|
|
|
|
|
|
|
|
Returns an iterator that iterates over all the lines in the image,
|
|
|
|
|
from top to bottom. See below for a description of the `line
|
|
|
|
|
objects`_.
|
|
|
|
|
|
|
|
|
|
``__len__() -> int``
|
|
|
|
|
|
|
|
|
|
Returns the number of lines in the image (``size.height``).
|
|
|
|
|
|
|
|
|
|
``__getitem__(integer) -> line``
|
|
|
|
|
|
|
|
|
|
Returns the line at the specified (y) position.
|
|
|
|
|
|
|
|
|
|
``__getitem__(tuple[integer]) -> pixel``
|
|
|
|
|
|
|
|
|
|
The parameter must be a tuple of two integers; they are
|
|
|
|
|
interpreted respectively as x and y coordinates in the image (0, 0
|
|
|
|
|
is the top left corner) and a pixel object is returned.
|
|
|
|
|
|
|
|
|
|
``__getitem__(slice | tuple[integer | slice]) -> image``
|
|
|
|
|
|
|
|
|
|
The parameter must be a slice or a tuple that contains two slices
|
|
|
|
|
or an integer and a slice; the selected area of the image is
|
|
|
|
|
copied and a new image is returned; ``image[x:y:z]`` is equivalent
|
|
|
|
|
to ``image[:, x:y:z]``.
|
|
|
|
|
|
|
|
|
|
``__setitem__(tuple[integer], integer | iterable[integer]) -> None``
|
|
|
|
|
|
|
|
|
|
Modifies the pixel at specified position; ``image[x, y] =
|
|
|
|
|
integer`` is a shortcut for ``image[x, y] = (integer,)`` for
|
|
|
|
|
images with a single component.
|
|
|
|
|
|
|
|
|
|
``__setitem__(slice | tuple[integer | slice], image) -> None``
|
|
|
|
|
|
|
|
|
|
Selects an area in the same way as the corresponding form of the
|
|
|
|
|
``__getitem__`` method and assigns to it a copy of the pixels from
|
|
|
|
|
the image in the second argument, that must have exactly the same
|
|
|
|
|
mode as this image and the same size as the specified area; the
|
|
|
|
|
alpha component, if present, is simply copied and doesn't affect
|
|
|
|
|
the other components of the image (i.e. no alpha compositing is
|
|
|
|
|
performed).
|
|
|
|
|
|
|
|
|
|
The ``mode``, ``size`` and ``buffer`` (including the address in memory
|
|
|
|
|
of the ``buffer``) never change after an image is created.
|
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
It is expected that, if :pep:`3118` is accepted, all the image objects
|
2007-06-30 15:07:03 -04:00
|
|
|
|
will support the new buffer protocol, however this is beyond the scope
|
|
|
|
|
of this PEP.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
``Image`` and ``ImageMixin`` Classes
|
|
|
|
|
''''''''''''''''''''''''''''''''''''
|
|
|
|
|
|
|
|
|
|
The ``ImageMixin`` class implements all the methods and attributes
|
|
|
|
|
described above except ``mode``, ``size``, ``buffer`` and ``info``.
|
|
|
|
|
``Image`` is a subclass of ``ImageMixin`` that adds support for these
|
|
|
|
|
four attributes and offers the following constructor (please note that
|
|
|
|
|
the constructor is not part of the image protocol):
|
|
|
|
|
|
|
|
|
|
``__init__(mode, size, color, source)``
|
|
|
|
|
|
|
|
|
|
``mode`` must be one of the constants in the ``MODES`` set,
|
|
|
|
|
``size`` is a sequence of two integers (width and height of the
|
|
|
|
|
new image); ``color`` is a sequence of integers, one for each
|
|
|
|
|
component of the image, used to initialize all the pixels to the
|
|
|
|
|
same value; ``source`` can be a sequence of integers of the
|
|
|
|
|
appropriate size and format that is copied as-is in the buffer of
|
|
|
|
|
the new image or an existing image; in Python 2.x ``source`` can
|
|
|
|
|
also be an instance of ``str`` and is interpreted as a sequence of
|
|
|
|
|
bytes. ``color`` and ``source`` are mutually exclusive and if
|
|
|
|
|
they are both omitted the image is initialized to transparent
|
|
|
|
|
black (all the bytes in the buffer have value 16 in the ``YV12``
|
|
|
|
|
mode, 255 in the ``CMYK*`` modes and 0 for everything else). If
|
|
|
|
|
``source`` is present and is an image, ``mode`` and/or ``size``
|
|
|
|
|
can be omitted; if they are specified and are different from the
|
|
|
|
|
source mode and/or size, the source image is converted.
|
|
|
|
|
|
|
|
|
|
The exact algorithms used for resizing and doing color space
|
|
|
|
|
conversions may differ between Python versions and
|
|
|
|
|
implementations, but they always give high quality results (e.g.:
|
|
|
|
|
a cubic spline interpolation can be used for upsampling and an
|
|
|
|
|
antialias filter can be used for downsampling images); any
|
|
|
|
|
combination of mode conversion is supported, but the algorithm
|
|
|
|
|
used for conversions to and from the ``CMYK*`` modes is pretty
|
|
|
|
|
naïve: if you have the exact color profiles of your devices you
|
|
|
|
|
may want to use a good color management tool such as LittleCMS.
|
|
|
|
|
The new image has an empty ``info`` ``dict``.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Line Objects
|
|
|
|
|
''''''''''''
|
|
|
|
|
|
|
|
|
|
The line objects (returned, e.g., when iterating over an image)
|
|
|
|
|
support the following attributes and methods:
|
|
|
|
|
|
|
|
|
|
``mode``
|
|
|
|
|
|
|
|
|
|
The mode of the image from where this line comes.
|
|
|
|
|
|
|
|
|
|
``__iter__() -> iterator[pixel]``
|
|
|
|
|
|
|
|
|
|
Returns an iterator that iterates over all the pixels in the line,
|
|
|
|
|
from left to right. See below for a description of the `pixel
|
|
|
|
|
objects`_.
|
|
|
|
|
|
|
|
|
|
``__len__() -> int``
|
|
|
|
|
|
|
|
|
|
Returns the number of pixels in the line (the image width).
|
|
|
|
|
|
|
|
|
|
``__getitem__(integer) -> pixel``
|
|
|
|
|
|
|
|
|
|
Returns the pixel at the specified (x) position.
|
|
|
|
|
|
|
|
|
|
``__getitem__(slice) -> image``
|
|
|
|
|
|
|
|
|
|
The selected part of the line is copied and a new image is
|
|
|
|
|
returned; the new image will always have height 1.
|
|
|
|
|
|
|
|
|
|
``__setitem__(integer, integer | iterable[integer]) -> None``
|
|
|
|
|
|
|
|
|
|
Modifies the pixel at the specified position; ``line[x] =
|
|
|
|
|
integer`` is a shortcut for ``line[x] = (integer,)`` for images
|
|
|
|
|
with a single component.
|
|
|
|
|
|
|
|
|
|
``__setitem__(slice, image) -> None``
|
|
|
|
|
|
|
|
|
|
Selects a part of the line and assigns to it a copy of the pixels
|
|
|
|
|
from the image in the second argument, that must have height 1, a
|
|
|
|
|
width equal to the specified slice and the same mode as this line;
|
|
|
|
|
the alpha component, if present, is simply copied and doesn't
|
|
|
|
|
affect the other components of the image (i.e. no alpha
|
|
|
|
|
compositing is performed).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Pixel Objects
|
|
|
|
|
'''''''''''''
|
|
|
|
|
|
|
|
|
|
The pixel objects (returned, e.g., when iterating over a line) support
|
|
|
|
|
the following attributes and methods:
|
|
|
|
|
|
|
|
|
|
``mode``
|
|
|
|
|
|
|
|
|
|
The mode of the image from where this pixel comes.
|
|
|
|
|
|
|
|
|
|
``value``
|
|
|
|
|
|
|
|
|
|
A tuple of integers, one for each component. Any iterable of the
|
|
|
|
|
correct length can be assigned to ``value`` (it will be
|
|
|
|
|
automagically converted to a tuple), but you can't assign to it an
|
|
|
|
|
integer, even if the mode has only a single component: use, e.g.,
|
|
|
|
|
``pixel.l = 123`` instead.
|
|
|
|
|
|
|
|
|
|
``r, g, b, a, l, c, m, y, k``
|
|
|
|
|
|
|
|
|
|
The integer values of each component; only those applicable for
|
|
|
|
|
the current mode (in ``mode.component_names``) will be available.
|
|
|
|
|
|
|
|
|
|
| ``__iter__() -> iterator[int]``
|
|
|
|
|
| ``__len__() -> int``
|
|
|
|
|
| ``__getitem__(integer | slice) -> int | tuple[int]``
|
|
|
|
|
| ``__setitem__(integer | slice, integer | iterable[integer]) ->
|
|
|
|
|
None``
|
|
|
|
|
|
|
|
|
|
These four methods emulate a fixed length list of integers, one
|
|
|
|
|
for each pixel component.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
``ImageSize`` Class
|
|
|
|
|
'''''''''''''''''''
|
|
|
|
|
|
|
|
|
|
``ImageSize`` is a named tuple, a class identical to ``tuple`` except
|
|
|
|
|
that:
|
|
|
|
|
|
|
|
|
|
* its constructor only accepts two integers, width and height; they
|
|
|
|
|
are converted in the constructor using their ``__index__()``
|
|
|
|
|
methods, so all the ``ImageSize`` objects are guaranteed to contain
|
|
|
|
|
only ``int`` (or possibly ``long``, in Python 2.x) instances;
|
|
|
|
|
|
|
|
|
|
* it has a ``width`` and a ``height`` property that are equivalent to
|
|
|
|
|
the first and the second number in the tuple, respectively;
|
|
|
|
|
|
|
|
|
|
* the string returned by its ``__repr__`` method is
|
|
|
|
|
``'imageop.ImageSize(width=%d, height=%d)' % (width, height)``.
|
|
|
|
|
|
|
|
|
|
``ImageSize`` is not usually instantiated by end-users, but can be
|
|
|
|
|
used when creating a new class that implements the image protocol,
|
|
|
|
|
since the ``size`` attribute must be an ``ImageSize`` instance.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
C API
|
|
|
|
|
-----
|
|
|
|
|
|
|
|
|
|
The available image modes are visible at the C level as ``PyImage_*``
|
|
|
|
|
constants of type ``PyObject *`` (e.g.: ``PyImage_RGB`` is
|
|
|
|
|
``imageop.RGB``).
|
|
|
|
|
|
|
|
|
|
The following functions offer a C-friendly interface to mode and image
|
|
|
|
|
objects (all the functions return ``NULL`` or -1 on failure):
|
|
|
|
|
|
|
|
|
|
``int PyImageMode_Check(PyObject *obj)``
|
|
|
|
|
|
|
|
|
|
Returns true if the object ``obj`` is a valid image mode.
|
|
|
|
|
|
|
|
|
|
| ``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)``
|
|
|
|
|
|
|
|
|
|
These functions are equivalent to their corresponding Python
|
|
|
|
|
attributes or methods.
|
|
|
|
|
|
|
|
|
|
``int PyImage_Check(PyObject *obj)``
|
|
|
|
|
|
|
|
|
|
Returns true if the object ``obj`` is an ``Image`` object or an
|
|
|
|
|
instance of a subtype of the ``Image`` type; see also
|
|
|
|
|
``PyObject_CheckImage`` below.
|
|
|
|
|
|
|
|
|
|
``int PyImage_CheckExact(PyObject *obj)``
|
|
|
|
|
|
|
|
|
|
Returns true if the object ``obj`` is an ``Image`` object, but not
|
|
|
|
|
an instance of a subtype of the ``Image`` type.
|
|
|
|
|
|
|
|
|
|
| ``PyObject* PyImage_New(PyObject *mode, Py_ssize_t width,
|
|
|
|
|
Py_ssize_t height)``
|
|
|
|
|
|
|
|
|
|
Returns a new ``Image`` instance, initialized to transparent black
|
|
|
|
|
(see ``Image.__init__`` above for the details).
|
|
|
|
|
|
|
|
|
|
| ``PyObject* PyImage_FromImage(PyObject *image, PyObject *mode,
|
|
|
|
|
Py_ssize_t width, Py_ssize_t height)``
|
|
|
|
|
|
|
|
|
|
Returns a new ``Image`` instance, initialized with the contents of
|
|
|
|
|
the ``image`` object rescaled and converted to the specified
|
|
|
|
|
``mode``, if necessary.
|
|
|
|
|
|
|
|
|
|
| ``PyObject* PyImage_FromBuffer(PyObject *buffer, PyObject *mode,
|
|
|
|
|
Py_ssize_t width,
|
|
|
|
|
Py_ssize_t height)``
|
|
|
|
|
|
|
|
|
|
Returns a new ``Image`` instance, initialized with the contents of
|
|
|
|
|
the ``buffer`` object.
|
|
|
|
|
|
|
|
|
|
``int PyObject_CheckImage(PyObject *obj)``
|
|
|
|
|
|
|
|
|
|
Returns true if the object ``obj`` implements a sufficient subset
|
|
|
|
|
of the image protocol to be accepted by the functions defined
|
|
|
|
|
below, even if its class is not a subclass of ``ImageMixin``
|
|
|
|
|
and/or ``Image``. Currently it simply checks for the existence
|
|
|
|
|
and correctness of the attributes ``mode``, ``size`` and
|
|
|
|
|
``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)``
|
|
|
|
|
|
|
|
|
|
These functions are equivalent to their corresponding Python
|
|
|
|
|
attributes or methods; the image memory can be accessed only with
|
|
|
|
|
the GIL and a reference to the image or its buffer held, and extra
|
|
|
|
|
care should be taken for modes with more than 8 bits per
|
|
|
|
|
component: the data is stored in native byte order and it can be
|
|
|
|
|
**not** aligned on 2 or 4 byte boundaries.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
A few examples of common operations with the new ``Image`` class and
|
|
|
|
|
protocol::
|
|
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Backwards Compatibility
|
|
|
|
|
=======================
|
|
|
|
|
|
|
|
|
|
There are three areas touched by this PEP where backwards
|
|
|
|
|
compatibility should be considered:
|
|
|
|
|
|
|
|
|
|
* **Python 2.6**: new classes and objects are added to the ``imageop``
|
|
|
|
|
module without touching the existing module contents; new methods
|
|
|
|
|
and attributes will be added to ``Tkinter.PhotoImage`` and its
|
|
|
|
|
``__getitem__`` and ``__setitem__`` methods will be modified to
|
|
|
|
|
accept integers, tuples and slices (currently they only accept
|
|
|
|
|
strings). All the changes provide a superset of the existing
|
|
|
|
|
functionality, so no major compatibility issues are expected.
|
|
|
|
|
|
|
|
|
|
* **Python 3.0**: the legacy contents of the ``imageop`` module will
|
2022-01-21 06:03:51 -05:00
|
|
|
|
be deleted, according to :pep:`3108`; everything defined in this
|
2007-06-30 15:07:03 -04:00
|
|
|
|
proposal will work like in Python 2.x with the exception of the
|
|
|
|
|
usual 2.x/3.0 differences (e.g. support for ``long`` integers and
|
|
|
|
|
for interpreting ``str`` instances as sequences of bytes will be
|
|
|
|
|
dropped).
|
|
|
|
|
|
|
|
|
|
* **external libraries**: the names and the semantics of the standard
|
|
|
|
|
image methods and attributes are carefully chosen to allow some
|
|
|
|
|
external libraries that manipulate images (including at least PIL,
|
|
|
|
|
wxPython and pygame) to implement the new protocol in their image
|
|
|
|
|
classes without breaking compatibility with existing code. The only
|
|
|
|
|
blatant conflicts between the image protocol and NumPy arrays are
|
|
|
|
|
the value of the ``size`` attribute and the coordinates order in the
|
|
|
|
|
``image[x, y]`` expression.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Reference Implementation
|
|
|
|
|
========================
|
|
|
|
|
|
|
|
|
|
If this PEP is accepted, the author will provide a reference
|
|
|
|
|
implementation of the new classes in pure Python (that can run in
|
|
|
|
|
CPython, PyPy, Jython and IronPython) and a second one optimized for
|
|
|
|
|
speed in Python and C, suitable for inclusion in the CPython standard
|
|
|
|
|
library. The author will also submit the required Tkinter patches.
|
|
|
|
|
For all the code will be available a version for Python 2.x and a
|
|
|
|
|
version for Python 3.0 (it is expected that the two version will be
|
|
|
|
|
very similar and the Python 3.0 one will probably be generated almost
|
|
|
|
|
completely automatically).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Acknowledgments
|
|
|
|
|
===============
|
|
|
|
|
|
|
|
|
|
The implementation of this PEP, if accepted, is sponsored by Google
|
|
|
|
|
through the Google Summer of Code program.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
coding: utf-8
|
|
|
|
|
End:
|