The png Module¶
The png
module can read and write PNG files.
Installation and Overview¶
pip install pypng
For help, type import png; help(png)
in your python interpreter.
A good place to start is the Reader
and Writer
classes.
Coverage of PNG formats is fairly complete; all allowable bit depths (1/2/4/8/16/24/32/48/64 bits per pixel) and colour combinations are supported:
greyscale (1/2/4/8/16 bit);
RGB, RGBA, LA (greyscale with alpha) with 8/16 bits per channel;
colour mapped images (1/2/4/8 bit).
Interlaced images, originally intended for progressive display when downloading, are supported for reading (but not for writing).
A number of optional chunks can be specified (when writing)
and understood (when reading): tRNS
, bKGD
, gAMA
.
The sBIT
chunk can be used to specify precision for
non-native bit depths.
Requires Python 3.5 or higher.
Installation is trivial,
but see the README.txt
file (with the source distribution) for details.
Full use of all features will need some reading of the PNG specification https://www.w3.org/TR/2003/REC-PNG-20031110/.
The package also comes with command line utilities.
pripamtopng
converts Netpbm PAM/PNM files to PNG;pripngtopam
converts PNG to file PAM/PNM.
There are a few more for simple PNG manipulations.
Spelling and Terminology¶
Generally British English spelling is used in the documentation. So that’s “greyscale” and “colour”. This not only matches the author’s native language, it’s also used by the PNG specification.
Colour Models¶
The major colour models supported by PNG (and hence by PyPNG) are:
greyscale;
greyscale–alpha;
RGB;
RGB–alpha.
Also referred to using the abbreviations: L, LA, RGB, RGBA. Each letter codes a single channel: L is for Luminance or Luma or Lightness (greyscale images); A stands for Alpha, the opacity channel (used for transparency effects, but higher values are more opaque, so it makes sense to call it opacity); R, G, B stand for Red, Green, Blue (colour image).
Lists, arrays, sequences, and so on¶
When getting pixel data out of this module (reading) and presenting data to this module (writing) there are a number of ways the data could be represented as a Python value.
The preferred format is a sequence of rows, which each row being a sequence of values. In this format, the values are in pixel order, with all the values from all the pixels in a row being concatenated into a single sequence for that row.
Consider an image that is 3 pixels wide by 2 pixels high, and each pixel has RGB components:
Sequence of rows:
list([R,G,B, R,G,B, R,G,B],
[R,G,B, R,G,B, R,G,B])
Each row appears as its own list, but the pixels are flattened so that three values for one pixel simply follow the three values for the previous pixel.
This is the preferred because
it provides a good compromise between space and convenience.
PyPNG regards itself as at liberty to replace any sequence type with
any sufficiently compatible other sequence type;
in practice each row is an array (bytearray
or array.array
).
To allow streaming the outer list is sometimes an iterator rather than an explicit list.
An alternative format is a single array holding all the values.
Array of values:
[R,G,B, R,G,B, R,G,B,
R,G,B, R,G,B, R,G,B]
The entire image is one single giant sequence of colour values. Generally an array will be used (to save space), not a list.
The top row comes first, and within each row the pixels are ordered from left-to-right. Within a pixel the values appear in the order R-G-B-A (or L-A for greyscale–alpha).
There is another format, which should only be used with caution.
It is mentioned because it is used internally,
is close to what lies inside a PNG file itself,
and has some support from the public API.
This format is called packed.
When packed, each row is a sequence of bytes (integers from 0 to 255),
just as it is before PNG scanline filtering is applied.
When the bit depth is 8 this is the same as a sequence of rows;
when the bit depth is less than 8 (1, 2 and 4),
several pixels are packed into each byte;
when the bit depth is 16 each pixel value is decomposed into 2 bytes
(and packed is a misnomer).
This format is used by the Writer.write_packed
method.
It isn’t usually a convenient format,
but may be just right if the source data for
the PNG image comes from something that uses a similar format
(for example, 1-bit BMPs, or another PNG file).
png module Classes and Functions¶
- class png.Image(rows, info)¶
A PNG image. You can create an
Image
object from an array of pixels by callingpng.from_array()
. It can be saved to disk with thesave()
method.Note
The constructor is not public. Please do not call it.
- save(file)¶
Save the image to the named file.
See
write()
if you already have an open file object.In general, you can only call this method once; after it has been called the first time the PNG image is written, the source data will have been streamed, and cannot be streamed again.
- stream()¶
Stream the rows into a list, so that the rows object can be accessed multiple times, or randomly.
- exception png.ProtocolError¶
Problem with the way the programming interface has been used, or the data presented to it.
- class png.Reader(_guess=None, filename=None, file=None, bytes=None)¶
Pure Python PNG decoder in pure Python.
The constructor expects exactly one keyword argument. If you supply a positional argument instead, it will guess the input type. Choose from the following keyword arguments:
- filename
Name of input file (a PNG file).
- file
A file-like object (object with a read() method).
- bytes
bytes
orbytearray
with PNG data.
- asDirect()¶
Returns the image data as a direct representation of an
x * y * planes
array. This removes the need for callers to deal with palettes and transparency themselves. Images with a palette (colour type 3) are converted to RGB or RGBA; images with transparency (atRNS
chunk) are converted to LA or RGBA as appropriate. When returned in this format the pixel values represent the colour value directly without needing to refer to palettes or transparency information.Like the
read
method this method returns a 4-tuple:(width, height, rows, info)
This method returns pixel values with the bit depth they have in the source image.
The info dictionary that is returned reflects the direct format and not the original source image. For example, an RGB source image with a
tRNS
chunk to represent a transparent colour, will start withplanes=3
andalpha=False
for the source image, but the info dictionary returned by this method will haveplanes=4
andalpha=True
because an alpha channel is synthesized and added.rows is a sequence of rows; each row being a sequence of values (like the
read()
method).All the other aspects of the image data are not changed.
- asRGB()¶
Return image as RGB pixels. RGB colour images are passed through unchanged; greyscales are expanded into RGB triplets (there is a small speed overhead for doing this).
An alpha channel in the source image will raise an exception.
The return values are as for the
read()
method except that the info reflect the returned pixels, not the source image. In particular, for this methodinfo['greyscale']
will beFalse
.
- asRGBA()¶
Return image as RGBA pixels. Greyscales are expanded into RGB triplets; an alpha channel is synthesized if necessary. The return values are as for the
read()
method except that the info reflect the returned pixels, not the source image. In particular, for this methodinfo['greyscale']
will beFalse
, andinfo['alpha']
will beTrue
.
- chunk()¶
Read the next PNG chunk from the input file; returns a (type, data) tuple. type is the chunk’s type as a byte string (all PNG chunk types are 4 bytes long). data is the chunk’s data content, as a byte string.
- chunk_of_type(type)¶
Return the next chunk of the given type, which is a 4 character ASCII string. Raises an error if the chunk is not found.
- chunks()¶
Return an iterator that will yield each chunk as a (chunktype, content) pair.
- palette(alpha='natural')¶
Returns a palette that is a sequence of 3-tuples or 4-tuples, synthesizing it from the
PLTE
andtRNS
chunks. These chunks should have already been processed (for example, by calling thepreamble
method). All the tuples are the same size: 3-tuples if there is notRNS
chunk, 4-tuples when there is atRNS
chunk.Assumes that the image is colour type 3 and therefore a
PLTE
chunk is required.If the alpha argument is
'force'
then an alpha channel is always added, forcing the result to be a sequence of 4-tuples.
- preamble()¶
Extract the image metadata by reading the initial part of the PNG file up to the start of the
IDAT
chunk. All the chunks that precede theIDAT
chunk are read and either processed for metadata or discarded.
- process_chunk()¶
Process the next chunk and its data. This only processes the following chunk types:
IHDR
,PLTE
,bKGD
,tRNS
,gAMA
,sBIT
,pHYs
. All other chunk types are ignored.
- read()¶
Read the PNG file and decode it. Returns (width, height, rows, info).
May use excessive memory.
rows is a sequence of rows; each row is a sequence of values.
- read_flat()¶
Read a PNG file and decode it into a single array of values. Returns (width, height, values, info).
May use excessive memory.
values is a single array.
The
read
method is more stream-friendly than this, because it returns a sequence of rows.
- undo_filter(filter_type, scanline, previous)¶
Undo the filter for a scanline. scanline is a sequence of bytes that does not include the initial filter type byte. previous is decoded previous scanline (for straightlaced images this is the previous pixel row, but for interlaced images, it is the previous scanline in the reduced image, which in general is not the previous pixel row in the final image). When there is no previous scanline (the first row of a straightlaced image, or the first row in one of the passes in an interlaced image), then this argument should be
None
.The scanline will have the effects of filtering removed; the result will be returned as a fresh sequence of bytes.
- validate_signature()¶
If signature (header) has not been read then read and validate it; otherwise do nothing. No signature (empty read()) will raise EOFError; An invalid signature will raise FormatError. EOFError is raised to make possible the case where a program can read multiple PNG files from the same stream. The end of the stream can be distinguished from non-PNG files or corrupted PNG files.
- class png.Writer(width=None, height=None, size=None, greyscale=<class 'png.Default'>, alpha=False, bitdepth=8, palette=None, transparent=None, background=None, gamma=None, compression=None, planes=None, colormap=None, maxval=None, chunk_limit=1048576, physical=(), x_pixels_per_unit=None, y_pixels_per_unit=None, unit_is_meter=False)¶
PNG encoder in pure Python.
Create a PNG encoder object.
Arguments:
- width, height
Image size in pixels, as two separate arguments.
- size
Image size (w,h) in pixels, as single argument.
- greyscale
Pixels are greyscale, not RGB.
- alpha
Input data has alpha channel (RGBA or LA).
- bitdepth
Bit depth: from 1 to 16 (for each channel).
- palette
Create a palette (PLTE chunk); enable colormap if it is not specified.
- transparent
Specify a transparent colour (create a
tRNS
chunk).- background
Specify a default background colour (create a
bKGD
chunk).- gamma
Specify a gamma value (create a
gAMA
chunk).- compression
zlib compression level: in range 0 to 9, or -1, or None;
- planes
Number of planes (values per pixel)
- colormap
True for type 3 PNG (a palette is required)
- chunk_limit
Write multiple
IDAT
chunks to save memory.- physical
Write
pHYs
chunk using 3 values in a list.- x_pixels_per_unit
Use physical argument instead.
- y_pixels_per_unit
Use physical argument instead.
- unit_is_meter
Use physical argument instead.
The image size (in pixels) can be specified either by using the width and height arguments, or with the single size argument. If size is used it should be a pair (width, height).
The greyscale argument indicates whether input pixels are greyscale (when true), or colour (when false). The default is true unless palette is used.
The alpha argument (a boolean) specifies whether input pixels have an alpha channel (or not).
bitdepth specifies the bit depth of the source pixel values. Each channel may have a different bit depth. Each source pixel must have values that are an integer between 0 and
2**bitdepth-1
, where bitdepth is the bit depth for the corresponding channel. For example, 8-bit images have values between 0 and 255. PNG only stores images with bit depths of 1,2,4,8, or 16 (the same for all channels). When bitdepth is not one of these values or where channels have different bit depths, the next highest valid bit depth is selected, and ansBIT
(significant bits) chunk is generated that specifies the original precision of the source image. In this case the supplied pixel values will be rescaled to fit the range of the selected bit depth.The PNG file format supports many bit depth / colour model combinations, but not all. The details are somewhat arcane (refer to the PNG specification for full details). Briefly: Bit depths < 8 (1,2,4) are only allowed with greyscale and colour mapped images; colour mapped images cannot have bit depth 16.
For colour mapped images (when the colormap argument is true, or has been implicitly made true via the palette argument) the bitdepth argument must match one of the valid PNG bit depths: 1, 2, 4, or 8. (It is valid to have a PNG image with a palette and an
sBIT
chunk, but the meaning is slightly different; it would be awkward to use the bitdepth argument for this.)The colormap option, when true, the PNG colour type is set to 3; greyscale must not be true; alpha must not be true; transparent must not be set. The bit depth must be 1,2,4, or 8. When a colour mapped image is created, the pixel values are palette indexes and the bitdepth argument specifies the size of these indexes (not the size of the colour values in the palette).
The palette argument adds a palette. It also implicitly sets the colormap (to True) if the colormap option is defaulted (thus making a colour type 3 PNG).
The palette argument value should be a sequence of 3- or 4-tuples. 3-tuples specify RGB palette entries; 4-tuples specify RGBA palette entries. All the 4-tuples (if present) must come before all the 3-tuples. A
PLTE
chunk is created; if there are 4-tuples then atRNS
chunk is created as well. ThePLTE
chunk will contain all the RGB triples in the same sequence; thetRNS
chunk will contain the alpha channel for all the 4-tuples, in the same sequence. Palette entries are always 8-bit.If specified, the transparent and background parameters must be a tuple with one element for each channel in the image. Either a 3-tuple of integer (RGB) values for a colour image, or a 1-tuple of a single integer for a greyscale image.
If specified, the gamma parameter must be a positive number (generally, a
float
). AgAMA
chunk will be created. Note that this will not change the values of the pixels as they appear in the PNG file, they are assumed to have already been converted appropriately for the gamma specified.The compression argument specifies the compression level. It is passed to the
zlib
module (unless it isNone
, in which case nothing is passed, andzlib
defaults are used). Values from 1 to 9 (highest) specify compression. 0 means no compression. -1 is thezlib
default (and so will also be used when this argument isNone
) and indicates the default level of compression (which is generally acceptable).chunk_limit is used to limit the amount of memory used whilst compressing the image. In order to avoid using large amounts of memory, multiple
IDAT
chunks may be created.physical should be a list of up to 3 items: [xpp, ypp, ism]. xpp is x-pixels-per-unit; ypp is y-pixels-per-unit (defaults to xpp if not present); ism is is-meter,
True
when the x- and y-resolutions are specified per meter (defaults toFalse
if not present).x_pixels_per_unit y_pixels_per_unit unit_is_meter alternative to using physical keyword. physical will override these values.
- array_scanlines(pixels)¶
Generates rows (each a sequence of values) from a single array of values.
- write(outfile, rows)¶
Write a PNG image to the output file. rows should be an iterable that yields each row (each row is a sequence of values).
This method only consumes sufficient rows for the PNG file (
self.height
rows). Extra rows are left unconsumed, but insufficient rows will raise aProtocolError
. Each row should haveself.width * self.planes
values.
- write_array(outfile, pixels)¶
Write an array that holds all the image values as a PNG file on the output file. See also
write()
method.
- write_packed(outfile, rows)¶
Write PNG file to outfile. rows should be an iterator that yields each packed row; a packed row being a sequence of packed bytes.
The rows have a filter byte prefixed and are then compressed into one or more
IDAT
chunks. They are not processed any further, so if bitdepth is other than 1, 2, 4, 8, 16, the pixel values should have been scaled before passing them to this method.For interlaced images (no longer written by PyPNG), the rows should be presented in the order that they appear in the file.
- write_passes(outfile, rows)¶
Write a PNG image to the output file.
Most users are expected to find the
write
orwrite_array
method more convenient.The rows should be given to this method in the order that they appear in the output file. For straightlaced images, this is the usual top to bottom ordering. For interlaced images the rows should have been interlaced before passing them to this function (though PyPNG no longer writes interlaced images).
rows should be an iterable that yields each row (each row being a sequence of values).
- png.from_array(a, mode=None, info={})¶
Create a PNG
Image
object from a 2-dimensional array. One application of this function is easy PIL-style saving:png.from_array(pixels, 'L').save('foo.png')
.Python doesn’t really have 2-dimensional arrays, a sequence of sequences should be supplied (list of array, list of list, or similar).
Unless they are specified using the info parameter, the PNG’s height and width are taken from the array size. The height is the length of the sequence a; the width is the length of the first row divided by the number of channels.
The argument a is assumed to be a sequence of rows, each row being a sequence of values (
width*channels
in number). So an RGB image that is 20 pixels high and 32 wide will occupy a 2-dimensional array that is 20x96 (each row will be 32*3 = 96 sample values).mode is a string that specifies the image colour format in a PIL-style mode. It can be:
'L'
greyscale (1 channel)
'LA'
greyscale with alpha (2 channel)
'RGB'
colour image (3 channel)
'RGBA'
colour image with alpha (4 channel)
The bit depth defaults to 8, but can be changed by appending
';16'
to mode; any decimal from 1 to 16 can be used to specify the bit depth.mode determines how many channels the image has, and so allows the width to be derived from the row length (the second array dimension).
Canonically the argument a is a list of lists:
png.from_array([[0, 255, 0], [255, 0, 255]], 'L')
. Other forms may be suitable, particular if they are made from Python Standard Library types. The exact rules are:len(a)
gives the first dimension, height;len(a[0])
gives the second dimension. It’s slightly more complicated than that because an iterator of rows can be used, and it all still works. Using an iterator allows data to be streamed efficiently.The info parameter is a dictionary that can be used to specify metadata (in the same style as the arguments to the
png.Writer
class). For this function the keys that are useful are:- height
overrides the height derived from the array dimensions and allows a to be an iterable.
- width
overrides the width derived from the array dimensions.
- bitdepth
select bit depth (must match mode if that also specifies a bit depth).
Generally anything specified in the info dictionary will override any implicit choices that this function would otherwise make, but must match any explicit ones. For example, if the info dictionary has a
greyscale
key then this must be true when mode is'L'
or'LA'
and false when mode is'RGB'
or'RGBA'
.
- png.write_chunks(out, chunks)¶
Create a PNG file by writing out the chunks.