PyPNG Code Examples — PyPNG v0.0.4 documentation

### Navigation

- index
- modules |
- next |
- previous |
- PyPNG v0.0.4 documentation »

# PyPNG Code Examples

This section discusses some example Python programs that use the png module for reading and writing PNG files.

## Writing

The basic strategy is to create a `Writer` object (instance of `png.Writer`) and then call its `png.write()` method with an open (binary) file, and the pixel data. The `Writer` object encapsulates all the information about the PNG file: image size, colour, bit depth, and so on.

### A Ramp

Create a one row image, that has all grey values from 0 to 255. This is a bit like Netpbm’s `pgmramp`.

    import png
    f = open('ramp.png', 'wb')      # binary mode is important
    w = png.Writer(255, 1, greyscale=True)
    w.write(f, [range(256)])
    f.close()

Note that our single row, generated by `range(256)`, must itself be enclosed in a list. That’s because the `png.write()` method expects a list of rows.

From now on `import png` will not be mentioned.

### A Little Message

A list of strings holds a graphic in ASCII graphic form. We convert it to a list of integer lists (the required form for the `write()` method), and write it out as a black-and-white PNG (bilevel greyscale).

    s = ['110010010011',
         '101011010100',
         '110010110101',
         '100010010011']
    s = map(lambda x: map(int, x), s)

    f = open('png.png', 'wb')
    w = png.Writer(len(s[0]), len(s), greyscale=True, bitdepth=1)
    w.write(f, s)
    f.close()

Note how we use `len(s[0])` (the length of the first row) for the x argument and `len(s)` (the number of rows) for the y argument.

### A Palette

The previous example, “a little message”, can be converted to colour simply by creating a PNG file with a palette. The only difference is that a palette argument is passed to the `write()` method instead of `greyscale=True`:

    # Assume f and s have been set up as per previous example
    palette=[(0x55,0x55,0x55), (0xff,0x99,0x99)]
    w = png.Writer(len(s[0]), len(s), palette=palette, bitdepth=1)
    f.write(f, s)

Note that the palette consists of two entries (the bit depth is 1 so there are only 2 possible colours). Each entry is an RGB triple. If we wanted transparency then we can use RGBA 4-tuples for each palette entry.

### Colour

For colour images the input rows are generally 3 times as long as for greyscale, because there are 3 channels, RGB, instead of just one, grey. Below, the p literal has 2 rows of 9 values (3 RGB pixels per row). The spaces are just for your benefit, to mark out the separate pixels; they have no meaning in the code.

    p = [(255,0,0, 0,255,0, 0,0,255),
         (128,0,0, 0,128,0, 0,0,128)]
    f = open('swatch.png', 'wb')
    w = png.Writer(3, 2)
    w.write(f, p) ; f.close()

### More Colour

A further colour example illustrates some of the manoeuvres you have to perform in Python to get the pixel data in the right format.

Say we want to produce a PNG imag with 1 row of 8 pixels, with all the colours from a 3-bit colour system (with 1-bit for each channel; such systems were common on 8-bit micros from the 1980s).

We produce all possible 3-bit numbers:

    >>> range(8)
    [0, 1, 2, 3, 4, 5, 6, 7]

We can convert each number into an RGB triple by assigning bit 0 to blue, bit 1 to red, bit 2 to green (the convention used by a certain 8-bit micro):

    >>> map(lambda x: (bool(x&2), bool(x&4), bool(x&1)), _)
    [(False, False, False), (False, False, True), (True, False, False),
    (True, False, True), (False, True, False), (False, True, True), (True,
    True, False), (True, True, True)]

(later on we will convert False into 0, and True into 255, so don’t worry about that just yet). Here we have each pixel as a tuple. We want to flatten the pixels so that we have just one row. In other words instead of [(R,G,B), (R,G,B), ...] we want [R,G,B,R,G,B,...]. It turns out that `itertools.chain(*...)` is just what we need:

    >>> list(itertools.chain(*_))
    [False, False, False, False, False, True, True, False, False, True,
    False, True, False, True, False, False, True, True, True, True, False,
    True, True, True]

Note that the `list` is not necessary, we can usually use the iterator directly instead. I just used `list` here so we can see the result.

Now to convert False to 0 and True to 255 we can multiply by 255 (Python use’s Iverson’s convention, so `False==0`, `True==1`). We could do that with `map(lambda x:255*x, _)`. Or, we could use a “magic” bound method:

    >>> map((255).__mul__, _)
    [0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255,
    255, 255, 0, 255, 255, 255]

Now we write the PNG file out:

    >>> p=_
    >>> f=open('speccy.png', 'wb')
    >>> w.write(f, [p]) ; f.close()

## Reading

The basic strategy is to create a `Reader` object (a `png.Reader` instance), then call its `png.read()` method to extract the size, and pixel data.

### PngSuite

The `Reader()` constructor can take either a filename, a file-like object, or a sequence of bytes directly. Here we use `urllib` to download a PNG file from the internet.

    >>> r=png.Reader(file=urllib.urlopen('http://www.schaik.com/pngsuite/basn0g02.png'))
    >>> r.read()
    (32, 32, <itertools.imap object at 0x10b7eb0>, {'greyscale': True,
    'alpha': False, 'interlace': 0, 'bitdepth': 2, 'gamma': 1.0})

The `png.read()` method returns a 4-tuple. Note that the pixels are returned as an iterator (not always, and the interface doesn’t guarantee it; the returned value might be an iterator or a sequence).

    >>> l=list(_[2])
    >>> l[0]
    array('B', [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 0, 0, 0, 0,
    1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3])

We have extracted the top row of the image. Note that the row itself is an `array` (see module `array`), but in general any suitable sequence type may be returned by `read()`. The values in the row are all integers less than 4, because the image has a bit depth of 2.

### Navigation

- index
- modules |
- next |
- previous |
- PyPNG v0.0.4 documentation »