PyPNM - Pythonic Portable Any Map I/O

Introduction

Portable Pixel Map (PPM) and Portable Gray Map (PGM) (particular cases on PNM format group) are simplest file formats for RGB and L images, correspondingly. In theory, you may import ASCII PGM even in Excel, turning Excel into image editor. Unfortunately, this simplicity lead to some consequences:

As a result, novice Python user (like me) may find it difficult to get reliable input/output modules for PPM and PGM image formats; therefore current PyPNM package was developed, combining input/output functions for 8-bits and 16-bits per channel binary and ascii PGM and PPM files, i.e. P2, P5, P3 and P6 PNM file types. P1 and P4 (1 bit) PBM formats are supported only for reading.

PyPNM key functionality comprise:

(Interlude start) Actually what I needed was small and simple facility for displaying image-like data for image editing/filtering applications like «Averager» from POVThread. Yes, I want to be able to write image filter in Python and view result without huge extra packages. Using Tkinter with PPM/PGM looked like simplest solution, so I created some module for turning nested 3D lists to PPM-like bytes structures in memory. Since it required learning PNM specs anyway, I decided to add PPM/PGM files reading and writing on top of that. Etc., as Kurt Vonnegut kept saying. (Interlude end)

You may easily acquire PyPNM module either from Github or from PyPI and use it absolutely free.

PyPNM version

Download site

Download content

Current version
(Python 3.10 and above)

Github main

PyPNM module and sample viewer application,
illustrating all reading, displaying and writing functions in action.

PyPI main

PyPNM module only.

Python 3.4 compatible
(tested under Windows XP)

Github py34

PyPNM module and sample viewer application,
illustrating all reading, displaying and writing functions in action.

PyPI v. 1.15.1.34

PyPNM module only.

Update: in ver. 1.15.1.1 image viewing function updated to show LA and RGBA images over chessboard background (like Photoshop and GIMP). This mode requires enabling newly added option, which, in turn, requires RTFM.

Previously on PyPNM: in ver. 1.14.8.19 memory usage for writing PPM and PGM files greatly reduced. Binary files (P6 and P5 correspondingly) are written per image row (which is considered as reasonable tradeoff between memory usage and disk thrashing), ASCII files (P3 and P2) are written per sample (which is minimal memory using scenario).

Format compatibility

Image format

File format

Read

Write

16 bits per channel RGB

P6 Binary PPM

16 bits per channel RGB

P3 ASCII PPM

8 bits per channel RGB

P6 Binary PPM

8 bits per channel RGB

P3 ASCII PPM

16 bits per channel L

P5 Binary PGM

16 bits per channel L

P2 ASCII PGM

8 bits per channel L

P5 Binary PGM

8 bits per channel L

P2 ASCII PGM

1 bit ink on/off

P4 Binary PBM

1 bit ink on/off

P1 ASCII PBM

Image representation

Is seems logical to represent an RGB image as nested 3D structure - X, Y-sized matrix of three-component RGB vectors. Since in Python list seem to be about the only variant for mutable structures like that, it is suitable to represent image as list(list(list(int))) structure (for example, this structure was used for ScaleNx, and worked well). Therefore, it would be convenient to have module read/write image data from/to such a common structure.

Note that for L images memory structure is still list(list(list(int))), with innermost list having only one component, thus enabling further image editing with the same nested Y, X, Z loop regardless of color mode. I understand that this may sound surprising for professional image I/O programmers, but for normal people writing a loop once and for all is expected behaviour.

Also, since this module is supposed to be used for image editing rather than just reading, when reading 1 bit PBM files into image this module promotes data to 8 bit L, inverting values and multiplying by 255, so that source 1 (ink on) is changed to 0 (black), and source 0 (ink off) is changed to 255 (white). For the same reason writing 1 bit PBM files is not planned - 1 bit images are next to useless for editing.

pnmlpnm.py

Module pnmlpnm.py contains 100% pure Python implementation of everything one may need to read/write from/to a variety of PGM and PPM files. I/O functions are written as functions/procedures, as simple as possible, and listed below:

Detailed functions arguments description is provided in docstrings, but in general looks as simple as that - you feed the function with your image data list and a filename, and get PNM file written; or you feed it to another function and get your image displayed.

viewer.py

Test PPM image opened in viewer

Program viewer.py is a small illustrative utility: using pnmlpnm package, it reads different flavours of PGM and PPM files, and allows saving them as different types of PGM/PNM, i.e. it can read ascii PPM and write it as binary PPM or vs. Also this program shows image data using pnmlpnm and Tkinter. No, there is no mistake: it does not feed PPM files to Tkinter directly. Instead, it uses nested 3D list data loaded using pnmlpnm to generate in-memory bytes object of PPM structure using preview_data = pnmlpnm.list2bin(image3D, maxcolors), and then feeds this in-memory bytes object to Tkinter as preview = PhotoImage(data=preview_data) (note using data=, not file=). This way it shows, for example, ascii PPM which Tkinter itself cannot handle.

Fun fact: Icon in viewer.py is not the icon, but bytes of PPM produced with pnmlpnm.py and then hardcoded into viewer as byte string. It's 2x2 pixels of basic colors, so when rescaling Tkinter turns it into a four-point gradient.

All this means that you may use pnmlpnm and Tkinter to visualize any data that can be represented as greyscale or RGB without huge external packages and writing files on disk; all you need is Tkinter, included into standard CPython distributions, and highly compatible pure Python pnmlpnm.py taking only 14 kbytes.

Compatibility issue: Tkinter included in Python 3.10 shows 16 bpc images incorrectly. This problem is not related to PyPNM, and was fixed in higher Python versions distributions.


Now when you have initial understanding of what pnmlpnm module is and how it may be used to work with images in general and PGM and PPM files in particular, it's time to pull it from Github and start working with images yourself.


...or move back to main page.