An open API service indexing awesome lists of open source software.

https://github.com/dnyarri/pypnm

Pure Python PGM and PPM image files read/write support, both 8 and 16 bits per channel.
https://github.com/dnyarri/pypnm

bitmap image image-format pgm pgm-format pgm-image pnm pnm-format pnm-image ppm ppm-format ppm-image python python-3 python-library python3

Last synced: 3 months ago
JSON representation

Pure Python PGM and PPM image files read/write support, both 8 and 16 bits per channel.

Awesome Lists containing this project

README

          

| [〖EN〗](README.md "Приблизительно тот же трёп, но по-английски") | 【RU】 |
| ---- | ---- |

# PyPNM - модуль чтения и записи графических форматов PPM и PGM на чистом Python

[![PyPI - Downloads](https://img.shields.io/pypi/dm/pypnm)](https://pypi.org/project/PyPNM/)

## Обзор и предпосылки

PyPNM представляет собой чистый пытоновый модуль для

- чтения файлов изображений PPM и PGM (как 8, так и 16 бит на канал) в 3D-список для дальнейшего редактирования;

- отображение полученных 3D-списков путём превращения оных на лету в байт-объекты в памяти, пригодные для отрисовки средствами Tkinter; и в завершение

- превращение отредактированных 3D списков в памяти в PPM или PGM-файлы на диске, бинарные или ASCII.

Графические форматы PPM ([Portable Pixel Map](https://netpbm.sourceforge.net/doc/ppm.html)) и PGM ([Portable Gray Map](https://netpbm.sourceforge.net/doc/pgm.html)) (частные случаи группы форматов PNM) представляют собой простейшие открытые форматы хранения изображений в пространствах RGB и L (серый), соответственно. Как ни странно, эта простота приводит к нежелательным побочным явлениям:

- Отсутствие чёткой официальной спецификации. Вместо этого описание формата изобилует словами типа "обычно". Не нужно быть пророком, чтобы предсказать, что кто-нибудь в рамках этой расплывчатости напишет файлы ненаказуемым, но извращённым способом.

- Явное нежелание многих фирменных разработчиков включать поддержку открытого простого формата в свои продукты. От разработчиков Adobe Photoshop потребовались годы, чтобы заметить, что народ качает третьесторонние плагины, то есть зачем-то народу этот формат нужен, после чего, наконец-то взявшись за дело, разработчики незамедлительно поступили вышеуказанным способом, напхав в заголовок схему разделителей, которую не использует больше никто.

Что касается поддержки PNM в Python, например, таких популярных библиотек, как Pillow, ситуация может быть стабильно описана как "ни в сказке сказать, ни при дамах произнести".

По всей видимости, профессиональные программисты считают, что задача слишком проста, чтобы руки марать. Что ж, пусть считают и дальше. Спасение утопающих - дело рук самих утопающих; поскольку мне нужны модули для чтения и записи графических форматов, желательно не использующие третьесторонних библиотек с их собственными глюками и конфликтами версий, пришлось написать этот небольшой модуль на чистом Python, обеспечивающий чтение и запись изображений 16 бит/канал и 8 бит/канал в пространствах RGB ([Portable Pixel Map](https://netpbm.sourceforge.net/doc/ppm.html)), серый (L) ([Portable Gray Map](https://netpbm.sourceforge.net/doc/pgm.html)), а также чтение однобитового формата ([Portable Bit Map](https://netpbm.sourceforge.net/doc/pbm.html)). Для простоты сведения о совместимости сведены в таблицу ниже.

## Совместимость форматов

Текущая версия модуля PyPNM обеспечивает чтение и запись следующих версий форматов группы Netpbm:

| Image format | File format | Read | Write |
| ------ | ------ | ------ | ------ |
| 16 бит/канал RGB | P6 двоичный PPM | ✅ | ✅ |
| 16 бит/канал RGB | P3 текстовый PPM | ✅ | ✅ |
| 8 бит/канал RGB | P6 двоичный PPM | ✅ | ✅ |
| 8 бит/канал RGB | P3 текстовый PPM | ✅ | ✅ |
| 16 бит/канал L | P5 двоичный PGM | ✅ | ✅ |
| 16 бит/канал L | P2 текстовый PGM | ✅ | ✅ |
| 8 бит/канал L | P5 двоичный PGM | ✅ | ✅ |
| 8 бит/канал L | P2 текстовый PGM | ✅ | ✅ |
| 1 бит (краска есть/нет) | P4 двоичный PBM | ✅ | ❌ |
| 1 бит (краска есть/нет) | P1 текстовый PBM | ✅ | ❌ |

## Совместимость с Python

Текущая версия протестирована под Python 3.10 и выше. Однако существует версия [PyPNM для Python 3.4](https://github.com/Dnyarri/PyPNM/tree/py34/), успешно протестированная с Python 3.4 под Windows XP 32-bit.

## Представление изображения

Поскольку целью данного модуля является не передвигание битов по диску, а обеспечение возможности работы с изображениями, например, их редактирования, модуль должен выдавать и принимать данные в виде какой-то логичным образом устроенной структуры. Представляется логичным представлять, например, RGB-изображение в виде трёхмерной структуры - картинки из рядов из пикселей из цветовых каналов. Поскольку в Python практически единственной изменяемой структурой данных для этого является список, предполагается представлять изображения в формате `list(list(list(int)))`. Таким образом, модуль должен превращать данные из не пойми чего на диске в трёхмерные вложенные списки в памяти.

Заметим, что и для серых картинок структурой остаётся `list(list(list(int)))`, при этом внутренний список состоит из одного элемента. С точки зрения профессиональных программистов это безобразие, но людям обыкновенным даёт возможность обрабатывать картинки в любом пространстве одним и тем же вложенным циклом по Y, X, Z, просто указывая количество каналов Z.

Заметим также, что, поскольку конечной целью является всё-таки обработка изображений, данный модуль обеспечивает поддержку 1-битовых форматов PBM только на чтение, при этом масштабирует цвет из 1 бита "краска есть/краски нет" в обычный яркостный L, т.е. инвертирует исходный сигнал (т.к. "краска есть" соответствует "яркости нет"), и умножает на 255.

## Установка

В случае использования pip:

```console
python -m pip install --upgrade PyPNM
```

после чего:

```python
import pypnm
```

и далее используйте функции, как описано ниже, либо просто посмотрите следующий пример:

## Пример

```python
#!/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()

```

Выше приведён пример минимальной программы, использующей все функции PyPNM: она читает файл PPM, записывает прочитанную картинку в виде двоичного PPM, записывает её же в виде ASCII PPM, а напоследок показывает её на экране средствами Tkinter.

## Описание функций

Модуль PyPNM содержит 100% чистую реализацию на Python всего, что может понадобиться для работы с файлами PGM и PPM. Ввод/вывод реализован в форме максимально простых в обращении функций, использование которых описано ниже. Также использование функций описано в docstrings, так что потерять инструкцию к модулю у вас вряд ли получится.

- **pnm2list** - читает RGB PPM, или L PGM, или 1-бит PBM-файл, и возвращает данные в форме вложенного трёхмерного списка целых чисел.
- **list2bin** - получает изображение в форме вложенного трёхмерного списка целых чисел, и создаёт в памяти байтовую структуру типа PPM (P6) или PGM (P5). Полученная структура может быть легко использована для визуализации изображений с помощью Tkinter.
- **list2pnm** - получает изображение в форме вложенного трёхмерного списка целых чисел, и записывает на диск либо двоичный PPM (P6) или PGM (P5) файл, либо ASCII PPM (P3) или PGM (P2), в зависимости от аргумента-переключателя.

Подробное описание аргументов функций приведено ниже,а также в docstring-ах модуля.

### pnm2list

`X, Y, Z, maxcolors, image3D = pypnm.pnm2list(in_filename)`

чтение данных из файла PPM/PGM, где:

- `X, Y, Z` - размеры изображения, int;
- `maxcolors` - количество цветов на канал, int;
- `image3D` - собственно данные пикселей, list(list(list(int)));
- `in_filename` - имя файла PPM/PGM, str.

### list2bin

`image_bytes = pypnm.list2bin(image3D, maxcolors, show_chessboard)`

Превращает изображение в форме вложенного трёхмерного списка целых чисел в байтовый объект типа PPM (P6) или PGM (P5) в памяти:

- `image3D` - `Y*X*Z` список (изображение) списков (рядов) списков (пикселей) целых чисел (каналов);
- `maxcolors` - количество цветов на канал, int;
- `show_chessboard` - bool, при установке `True` генерирует превью LA и RGBA-картинок на фоне шахматной паттерны; `False` или отсутствие переменной приводит к простому игнорированию альфа-канала. Значение по умолчанию `False` для задней совместимости;
- `image_bytes` - байты PNM.

Полученный объект `image_bytes` совместим с методом Tkinter `PhotoImage(data=...)` и предназначен для визуализации любых данных, похожих на картинку в виде 3D-списка.

> [!NOTE]
> В случае списков с 2 или 4 каналами свежая версия `list2bin` может считать их LA или RGBA картинками, и генерировать для них превью на фоне шахматной паттерны (как Photoshop или GIMP). Поскольку форматы PNM не поддерживают прозрачности, данная картинка на самом деле имеет структуру L или RGB, а паттерну генерирует и подмешивает на лету сама функция `list2bin`. Данное поведение контролируется переменной `show_chessboard`; текущая установка `False` (просто выбрасывает альфа-канал) для совместимости со старыми версиями PyPNM, в которых такой опции не было.

### list2pnm

`pypnm.list2pnm(out_filename, image3D, maxcolors, bin)`

Запись картинки в бинарный или ASCII файл:

- `image3D` - `Y*X*Z` список (изображение) списков (рядов) списков (пикселей) целых чисел (каналов);
- `maxcolors` - количество цветов на канал, int;
- `bin` - переключатель (bool) между записью двоичного и ASCII файла. Текущее положение True, то есть запись двоичного.
- `out_filename` - имя файла PNM.

Заметьте, что `list2pnm` представляет собой просто переключатель между внутренними функциями `list2pnmbin` and `list2pnmascii`, облегчающий написание функций типа "Save as...", особенно для графических диалогов - можно сохранять разные типы файлов одной функцией, просто передавая `bin` через lambda-функцию.

### create_image

`image3D = create_image(X, Y, Z)`

Создаёт пустой трёхмерный список размеров `X*Y*Z`.

## viewer.py

Программа [**viewer.py**](https://github.com/Dnyarri/PyPNM/blob/main/viewer.py) представляет собой небольшой иллюстративный пример использования PyPNM: с помощью *PyPNM* она читает разные форматы файлов PGM и PPM, и позволяет сохранять их в другом формате PGM/PNM, например, позволяет прочитать текстовый PPM и записать изображение в двоичный PPM, или наоборот. Также эта программа отображает изображения с помощью *PyPNM* и Tkinter. Это не ошибка - программа не скармливает PPM-файл Tkinter напрямую; вместо этого с помощью *PyPNM* она раскрывает изображение в трёхмерный список, затем с помощью *PyPNM* же генерирует из этого списка байты PNM `preview_data = pypnm.list2bin(image3D, maxcolors)`, а только после этого передаёт эти байты в Tkinter `preview = PhotoImage(data=preview_data)` (именно *data=*, а не *file=*). Таким образом программа отображает, например, текстовые PNM, которые сам Tkinter не поддерживает.

| Рис. 1. *Пример отрисовки текстового .ppm с помощью Viewer.py* |
| :---: |
| [![Пример отрисовки текстового .ppm с помощью Viewer.py](https://dnyarri.github.io/pypnm/viewer.png "Пример отрисовки текстового .ppm с помощью Viewer.py")](https://dnyarri.github.io/pypnm.html) |

Помимо минималистического GUI с использованием мышки а-ля Photoshop, *viewer.py* также в состоянии переварить аргументы командной строки

```console
python viewer.py filename.ppm
```

для открытия файлов. В принципе, вы можете даже зарегистрировать его в качестве системного смотрела для файлов PPM, PGM и PBM, и стать первым человеком на этой планете с системным смотрелом, написанном на Python.

> [!NOTE]
> Начиная с версии 2.21.22.23 Viewer снабжен пунктом "Export via Tkinter..." а основном меню, позволяющим сохранить открытое изображение с помощью 100% натуральных экологически чистых механизмов Tkinter `PhotoImage.write`. Данная опция включена исключительно для иллюстрации ограничений Tkinter. Например, изображение с цветовой глубиной 16 бит на канал после сохранения таким способом превращается в 8 бит на канал, потому что иначе Tkinter не умеет. Это несколько объясняет одну из причин, по которым я был вынужден написать свой собственный модуль, которому всё равно, что у вас за картинка - он обработает её правильно.

## Заключение

Использование *PyPNM* и Tkinter позволяет легко визуализировать любые данные, которые могут быть представлены в виде L/RGB изображений, и в первую очередь собственно картинки.

*PyPNM* обеспечивает чтение и запись файлов PPM и PGM с цветовой глубиной как 8б так и 16 бит на канал, а также чтение однобитовых PBM.

Используемая *PyPNM* структура данных (вложенные списки) облегчает написание алгоритмов обработки изображений.

## Ссылки

1. [Описание форматов Netpbm](https://netpbm.sourceforge.net/doc/).

2. [PyPNM на PyPI](https://pypi.org/project/PyPNM/) - установка PyPN с помощью `pip`. Не включает примеров и т.п., только голый пакет ввода/вывода, зато `pip` обеспечивает автоматизацию обновлений.

3. [PyPNM на Github](https://github.com/Dnyarri/PyPNM/) - содержит пример приложения для просмотра, иллюстрирующий применение `list2bin` для визуализации данных с помощью Tkinter `PhotoImage(data=...), и конверсию изображений между форматами.

4. [PyPNM ver.34 на Github](https://github.com/Dnyarri/PyPNM/tree/py34) - то же, что и п.3, но работает под Python 3.4.

5. [PyPNM docs (PDF)](https://dnyarri.github.io/pypnm/pypnm.pdf). Текущая версия документации относится к выпуску 9 мая 2025 года "Victory", но, поскольку капитальная модернизация, проведённая в версии 2 сентября 2025 года "Victory II", не затрагивает структуру ввода и вывода, документация действительна и для новых версий. Новая версия делает всё то же самое, просто старается жрать меньше ресурсов.

## Иллюстрации

[PixelArtScaling](https://dnyarri.github.io/scalenx.html) - пример применения, масштабирование изображений методами Scale2x и Scale3x на чистом Python, ввод/вывод PNG основан на [PyPNG](https://gitlab.com/drj11/pypng), а PNM - на [PyPNM](https://pypi.org/project/PyPNM/), что делает приложения кросс-платформенными.

[«Averager»](https://dnyarri.github.io/povthread.html#averager) - программа фильтрования изображений методом адаптивного усреднения (написана, поскольку именно такой алгоритм был нужен для специфической цели, а в больших программах типа Photoshop он отсутствует), превью "до" и "после" основано на коде PyPNM `list2bin` и на классе Tkinter `PhotoImage(data=...)`.

| Рис. 2. *Приложение для фильтрования изображений, изрядно основанное на использовании PyPNM* |
| :---: |
| [![Пример приложения для фильтрования изображений с применением PyPNM](https://dnyarri.github.io/thread/ave.png "Пример приложения для фильтрования изображений с применением PyPNM")](https://dnyarri.github.io/povthread.html#averager) |

Собственно фильтр построен на простых вложенных циклах и `map()`, благодаря создаваемой PyPNM структуре вложенных списков обеспечивающих обработку картинок в разных цветовых пространствах одним незатейливым алгоритмом. Таким образом, представлено небольшое, но полноценное интерактивное приложение для фильтрования изображений, реализованное исключительно на Python.

[Модуль **imin** для билинейной и барицентрической интерполяции изображений](https://dnyarri.github.io/imin.html), written entirely in Python. Sample applications are largely based on PyPNM.

| Рис. 3. *Приложение для фильтрования/деформации изображений, изрядно основанное на использовании PyPNM* |
| :---: |
| [![Пример приложения для фильтрования изображений с применением PyPNM](https://dnyarri.github.io/imin/anigui.png "Пример приложения для фильтрования изображений с применением PyPNM")](https://dnyarri.github.io/imin.html "Пример приложения для фильтрования изображений с применением PyPNM") |

Как и в случае предыдущих программ, модуль использует тот факт, что структура изображений как вложенных списков, создаваемая PyPNM, позволяет обрабатывать картинки с любым количеством каналов одной функцией `map()`; программы-оболочки показывают превью "до" и "после" с помощью кода PyPNM `list2bin` и класса Tkinter `PhotoImage(data=...)`.

---

[Dnyarri website - больше программ на Python](https://dnyarri.github.io/) и остальной товар от Жабы Огромной Умственной Силы.