All formats

pypng

LanguagePython 3 (pure Python)
LicenseMIT
Repositorygitlab.com/drj11/pypng
DocumentationPyPI page, docstrings in png.py
Depends onnothing (uses the standard zlib module)
Read / writeyes / yes, all color types and bit depths 1-16; text chunks only as raw chunks

pypng is a single Python module, png.py, that reads and writes PNG without any C extension. It supports every color type and every bit depth, including 16-bit RGB and RGBA, which Pillow reduces to 8 bits. Pixels are plain Python sequences: one row is a list or array of channel values.

It has no image operations (resize, draw, convert to JPEG) and it’s much slower than Pillow, so use it for exact control over the PNG you write, for 16-bit data, or where you can’t install binary packages.

Installation#

Shell
pip install pypng

The module is imported as png.

Basic usage: read and write a PNG#

Python
import png

# read: rows is an iterator of rows, each row a sequence of channel values
width, height, rows, info = png.Reader(filename="in.png").asRGBA8()
print(width, height, info["bitdepth"], info["alpha"])

# invert colors, keep alpha
out_rows = []
for row in rows:
    row = list(row)
    for i in range(0, len(row), 4):
        row[i] = 255 - row[i]
        row[i + 1] = 255 - row[i + 1]
        row[i + 2] = 255 - row[i + 2]
    out_rows.append(row)

writer = png.Writer(width, height, greyscale=False, alpha=True, bitdepth=8)
with open("out.png", "wb") as f:
    writer.write(f, out_rows)

asRGBA8() converts any PNG to 8-bit RGBA. read() returns the pixels in the file’s own format, and asDirect() expands palettes but keeps the bit depth.

How to write a 16-bit grayscale PNG#

Python
import png

width, height = 256, 64
rows = [[x * 257 for x in range(width)] for _ in range(height)]  # 0 .. 65535
writer = png.Writer(width, height, greyscale=True, bitdepth=16)
with open("gray16.png", "wb") as f:
    writer.write(f, rows)

How to write a palette PNG#

Each pixel is an index into the palette. Entries with 4 values (RGBA) add transparency through a tRNS chunk; they must come before all 3-value entries:

Python
import png

palette = [(0, 0, 255, 128), (0, 0, 0), (255, 0, 0)]  # half-transparent blue, black, red
rows = [
    [0, 1, 2, 1],
    [1, 2, 1, 0],
]
writer = png.Writer(4, 2, palette=palette, bitdepth=2)
with open("palette.png", "wb") as f:
    writer.write(f, rows)

How to write a PNG from a 2D list in one line#

png.from_array takes rows and a mode string (L, LA, RGB, RGBA, optionally with ;16 for 16-bit):

Python
import png

png.from_array([[0, 128, 255], [255, 128, 0]], "L").save("small.png")

How to list chunks and read text chunks#

pypng doesn’t decode text chunks, but it gives you every raw chunk:

Python
import png

reader = png.Reader(filename="in.png")
for chunk_type, data in reader.chunks():
    print(chunk_type, len(data))
    if chunk_type == b"tEXt":
        key, _, value = data.partition(b"\x00")
        print("  ", key.decode("latin-1"), "=", value.decode("latin-1"))

Tips#