Reading, displaying and writing PNM (PPM and PGM) image files, including 16 bits per channel, in pure Python
Project description
PyPNM - PPM and PGM image files reading, viewing and writing module in pure Python
Overview
PyPNM is a pure Python module, providing functions for:
-
reading PPM and PGM image files (both 8 and 16 bits per channel color depth) to image 3D nested lists for further editing;
(Reading support for 1 bpc PBM is provided as well.)
-
displaying 3D list thus obtained by converting it to Tkinter-compatible data in memory;
-
subsequent writing edited image pixel data 3D list to disk as PPM or PGM file, either binary or ASCII.
Functions are detailed in "Functions description", and illustrated in "Usage example" sections below.
Justification
PPM (Portable Pixel Map) and PGM (Portable Gray Map) (particular cases of PNM format group) are simplest file formats for RGB (color) and L (greyscale) images, correspondingly. Not surprisingly for this decaying Universe, such a simplicity lead to some adverse consequences:
-
lack of strict official specification. Instead, you may find words like "usual" in format description. Surely, there is always someone who implement this part of image format in unprohibited, yet a totally unusual way.
-
unwillingness of many professional software developers to spend their precious time on such a trivial task as supporting simple open format. It took years for almighty Adobe team to include PNM module in Photoshop rather than count on third-party developers, and surely (see above) they took their chance to implement a header scheme nobody else seem to use. What as to PNM support in Python, say, Pillow, it's often incomplete and/or requires counterintuitive measures when dealing with specific image types (like 16 bit per channel) in rare cases such a support exist.
As a result, novice Python user (like the writer of these words) may find it difficult to get simple yet reliable input/display/output modules for PPM and PGM image formats.
Objectives
-
To obtain suitable facility for visualization of image-like data (images first and foremost), represented as 3D nested lists, by means of Tkinter
PhotoImage(data=...)class.That is, getting something to easily view images being edited, without downloading excessively large packages.
-
To obtain simple and compact cross-platform module for reading PPM and PGM files as 3D nested lists for further processing with Python, and subsequent writing of processed 3D nested lists data to PPM or PGM files.
It is necessary to have not only 8 bpc but 16 bpc images supported fully and directly, for the developer to consider PyPNM as "just working", without going deeply into details.
-
Finally, to inspire and facilitate further development of image editing algorithms in Python (meaning Python itself, not C or Rust or whatever) by attaining objectives № 1 and 2 above.
That is, once you can read and write images, and view the result of analyzing/filtering image with your algorithm, you, as a developer, may finally concentrate on image processing algorithm itself, rather than any auxiliary facilities.
To accomplish this, current PyPNM module was developed, combining read/write functions for binary and ASCII PGM and PPM files (i.e. P2, P5, P3 and P6 PNM file types), and suitable facilities for image display. Both greyscale and RGB color spaces with 8 bit and 16 bit per channel color depths (0..255 and 0..65535 ranges respectively) are supported directly, without limitations and without any dances with tambourine like using separate methods for different bit depths etc.
Thus, PyPNM may simplify writing image processing applications in Python, either as a part of rapid prototyping or as finalized software.
Noteworthy that PyPNM is pure Python module, which makes it pretty compact and OS-independent. No third-party imports, no Numpy version conflicts (some may find it surprising, but list reshaping in Python can be done with one line without Numpy) etc.
Python compatibility
Current PyPNM version, created for PyPI distribution, is a maximal backward compatibility build. While most of the development was performed using Python 3.12, extensive testing with other versions was carried out, and PyPNM proven to work with antique Python 3.4 (reached end of life 18 Mar 2019) under Windows XP 32-bit (reached end of support 8 Apr 2014).
[!NOTE] Tkinter, bundled with standard CPython distributions 3.10 and below have problems with 16 bpc images. Although it's not PyPNM but Tkinter problem, it's still ungood and severely discombobulating. As a workaround,
list2binfunction in PyPNM extended compatibility version (.34) includes a routine for color depth reduction from 16 bpc to 8 bpc when generating a preview. Surelylist2bintries to avoid such a remapping unless it is absolutely necessary since remapping requires extra calculation and therefore slows the function down; decision on remapping, however, is based on correlation between CPython version and bundled Tkinter version, and therefore may fail if you have custom builds of Tkinter, or Python, or both. Failure is most likely to manifest as unnecessary slowdowns, and least likely as Tkinter exception. Remember that this module is provided under Unlicense, I don't care much of my copyright, so you may edit the source at will, including Python version detection criteria.If you have only new versions of Python (3.11 and above) and Tkinter, you may consider downloading Main version of PyPNM, which doesn't have any backward compatibility fixes, and therefore doesn't waste CPU time on it.
Format compatibility
Current PyPNM module read and write capabilities are briefly summarized below.
| Image format | File format | Read | Write |
|---|---|---|---|
| 16 bits per channel RGB | P6 Binary PPM | Yes | Yes |
| 16 bits per channel RGB | P3 ASCII PPM | Yes | Yes |
| 8 bits per channel RGB | P6 Binary PPM | Yes | Yes |
| 8 bits per channel RGB | P3 ASCII PPM | Yes | Yes |
| 16 bits per channel L | P5 Binary PGM | Yes | Yes |
| 16 bits per channel L | P2 ASCII PGM | Yes | Yes |
| 8 bits per channel L | P5 Binary PGM | Yes | Yes |
| 8 bits per channel L | P2 ASCII PGM | Yes | Yes |
| 1 bit ink on/off | P4 Binary PBM | Yes | No |
| 1 bit ink on/off | P1 ASCII PBM | Yes | No |
Target image representation
Main goal of module under discussion is not just bytes reading and writing but representing image as some logically organized structure for further image editing.
Is seems logical to represent an RGB image as nested 3D structure - (X, Y)-sized matrix of three-component (R, G, B) 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. Therefore, it would be convenient to have module read/write image data from/to such a structure.
Note that for L images memory structure is still list(list(list(int))), with innermost list having only one component, which enables further image editing with the same nested loop or map() regardless of color mode.
Note that since main PyPNM purpose is facilitating image editing, 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) - since any palette-based images, 1-bit included, are next to useless for general image processing (try to imagine 1-bit Gaussian blur, for example), and have to be converted to smooth color for that, conversion is performed by PyPNM automatically.
Installation
In case of installing from PyPI via pip:
python -m pip install --upgrade PyPNM
Usage
Since version 2.21.3.4.post7 recommended import is:
import pypnm
then follow functions descriptions in section "Functions description", or just take a look at "Usage example" section below.
Note that legacy import schemes like
from pypnm import pnmlpnm
are still working, so old programs do not need rewriting after PyPNM update.
Usage example
Below is a minimal Python program, illustrating all PyPNM functions at once: reading PPM file (image files are not included into PyPI PyPNM distribution. You may use any of compatibility testing samples from Git repository) to image nested list, writing image list to disk as binary PPM, writing image list as ASCII PPM, and displaying image list using Tkinter:
#!/usr/bin/env python3
from tkinter import Button, PhotoImage, Tk
from pypnm import list2bin, list2pnm, pnm2list
X, Y, Z, maxcolors, image3D = pnm2list('example.ppm') # Open "example.ppm"
list2pnm('binary.ppm', image3D, maxcolors, bin=True) # Save as binary pnm
list2pnm('ascii.ppm', image3D, maxcolors, bin=False) # Save as ascii pnm
main_window = Tk()
main_window.title('PyPNM demo')
preview_data = list2bin(image3D, maxcolors) # Image list -> preview bytes
preview = PhotoImage(data=preview_data) # Preview bytes -> PhotoImage object
preview_button = Button(main_window, text='Example\n(click to exit)', image=preview,
compound='top', command=lambda: main_window.destroy()) # Showing PhotoImage
preview_button.pack()
main_window.mainloop()
With a fistful of code for widgets and events this simplistic program may be easily turned into a rather functional application.
Functions description
PyPNM module contains 100% pure Python implementation of everything one may need to read and write a variety of PGM and PPM files, as well as to display corresponding image data. No non-standard dependencies used, no extra downloads needed, no dependency version conflicts expected. All the functionality is provided as functions/procedures, as simple as possible; main functions are listed below:
- pnm2list - reading binary or ASCII RGB PPM or L PGM file and returning image data as nested list of int.
- list2bin - getting image data as nested list of int and creating binary PPM (P6) or PGM (P5) data structure in memory. Suitable for generating data to display with Tkinter.
- list2pnm - getting image data as nested list of int and writing either binary or ASCII file depending on
binargument.
Detailed functions arguments description is provided below, as well as in module docstrings and PyPNM documentation bedside book (PDF).
pnm2list
X, Y, Z, maxcolors, image3D = pypnm.pnm2list(in_filename)
Read data from PPM/PGM file to nested image data list, where:
X, Y, Z- image sizes (int);maxcolors- number of colors per channel for current image (int);image3D- image pixel data as list(list(list(int)));in_filename- PPM/PGM file name (str).
list2bin
image_bytes = pypnm.list2bin(image3D, maxcolors, show_chessboard)
Convert nested image data list to PGM P5 or PPM P6 (binary) data structure in memory, where:
-
image3D-Y * X * Zlist (image) of lists (rows) of lists (pixels) of ints (channels); -
maxcolors- number of colors per channel for current image (int); -
show_chessboard- optional bool, setTrueto show LA and RGBA images against chessboard pattern;Falseor missing show existing L or RGB data for transparent areas as opaque.Default is
Falsefor backward compatibility; -
image_bytes- PNM-structured binary data.image_bytesobject thus obtained is well compatible with TkinterPhotoImage(data=...)method and therefore may be used to (and actually was developed for) visualize any data represented as image-like 3D list.
When encountering image list with 2 or 4 channels, current version of list2bin may treat it as LA or RGBA image correspondingly, and generate image preview for Tkinter as transparent over chessboard background (like Photoshop or GIMP). Since PNM images do not have transparency, this preview is actually either L or RGB, with image mixed with chessboard background, generated by list2bin on the fly (pattern settings match Photoshop "Light Medium" defaults). This behaviour is controlled by show_chessboard option. Default setting is False (meaning simply skipping alpha channel) for backward compatibility.
list2pnm
pypnm.list2pnm(out_filename, image3D, maxcolors, bin)
Write either binary or ASCII file from nested image data list, where:
-
image3D-Y * X * Zlist (image) of lists (rows) of lists (pixels) of ints (channels); -
maxcolors- number of colors per channel for current image (int); -
bin- switch (bool) defining whether to write binary file or ASCII.Default is
True, meaning binary output, to provide backward compatibility. -
out_filename- Name of PNM file to be written.
Note that list2pnm is a switch between internal list2pnmbin and list2pnmascii, whose direct usage is considered legacy. Using list2pnm instead of legacy calls simplifies writing "Save as..." functions for main programs - now you can use one function for all PNM flavours. Default is bin = True since binary PNM seem to be more convenient for big programs like Photoshop.
References
-
Netpbm file formats specifications strictly followed in the course of PyPNM development.
-
PyPNM at Github contains both PyPNM module and viewer application example, illustrating using
list2binto produce data for TkinterPhotoImage(data=...)to display, and other PyPNM functions for opening/saving various portable map formats (so viewer may be used as converter between binary and ASCII variants of PPM and PGM files).Issues and discussions are open for possible bug reports and suggestions, correspondingly.
-
PyPNM for Python 3.4 at Github - same as above, but compatible with Python down to 3.4. Besides PPM and PGM support, image viewer in this branch also have PNG support, based on PyPNG, and therefore may be used as pure Python PNM <=> PNG converter.
-
PyPNM docs (PDF). While current documentation was written for 9 May 2025 "Victory" version, it remains valid for 2 Sep 2025 "Victory II" release since the latter involves total inner optimization without changing input and output data types and structure.
Illustrations
-
Adaptive image averager is an example of special effect image filter, necessary for the whole POV‑Ray Thread, but absent in normal image editors like Photoshop. As a result, filter was written in Python, and works surprisingly fast for a Python image editing.
Nested list image representation, as provided by PyPNM, allows pixel processing with a single map() in any color mode from L to RGBA, thus making the code both simple and fast.
-
Bilinear and barycentric image interpolation, rescaling, and other transformations in pure Python also utilizes map()-based approach to simplify processing images, represented by PyPNM-generated nested lists.
Apparently, PyPNM also provides displaying images being edited by means of Tkinter.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pypnm-2.26.26.34.post1.tar.gz.
File metadata
- Download URL: pypnm-2.26.26.34.post1.tar.gz
- Upload date:
- Size: 15.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c41edc0c195fc3520325b54e838f425a2f7d0dea6fb60a13925edb6a17a1b8d5
|
|
| MD5 |
a8375dafa86459fb94c636411ee4b098
|
|
| BLAKE2b-256 |
b1f3224c033664382055cf96aade7f981b17d6278b5bcbe1f21ea3f17169c624
|
File details
Details for the file pypnm-2.26.26.34.post1-py3-none-any.whl.
File metadata
- Download URL: pypnm-2.26.26.34.post1-py3-none-any.whl
- Upload date:
- Size: 16.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
72e1c24800a3f6c35b9b96aadfcd0f43fd065fe7b9eee9cb7f251a109de22f5b
|
|
| MD5 |
0b3fc64972020c865b490630434ee6b3
|
|
| BLAKE2b-256 |
ccfb636d7b431fd4a9e10acc711c41f14bcb2d341de1fc85c26e482447d624aa
|