| 〖EN〗 | 【RU】 |
|---|
PyPNM представляет собой чистый пытоновый модуль для
-
чтения файлов изображений PPM и PGM (как 8, так и 16 бит на канал) в 3D-список для дальнейшего редактирования;
-
отображение полученных 3D-списков путём превращения оных на лету в байт-объекты в памяти, пригодные для отрисовки средствами Tkinter; и в завершение
-
превращение отредактированных 3D списков в памяти в PPM или PGM-файлы на диске, бинарные или ASCII.
Графические форматы PPM (Portable Pixel Map) и PGM (Portable Gray Map) (частные случаи группы форматов PNM) представляют собой простейшие открытые форматы хранения изображений в пространствах RGB и L (серый), соответственно. Как ни странно, эта простота приводит к нежелательным побочным явлениям:
-
Отсутствие чёткой официальной спецификации. Вместо этого описание формата изобилует словами типа "обычно". Не нужно быть пророком, чтобы предсказать, что кто-нибудь в рамках этой расплывчатости напишет файлы ненаказуемым, но извращённым способом.
-
Явное нежелание многих фирменных разработчиков включать поддержку открытого простого формата в свои продукты. От разработчиков Adobe Photoshop потребовались годы, чтобы заметить, что народ качает третьесторонние плагины, то есть зачем-то народу этот формат нужен, после чего, наконец-то взявшись за дело, разработчики незамедлительно поступили вышеуказанным способом, напхав в заголовок схему разделителей, которую не использует больше никто.
Что касается поддержки PNM в Python, например, таких популярных библиотек, как Pillow, ситуация может быть стабильно описана как "ни в сказке сказать, ни при дамах произнести".
По всей видимости, профессиональные программисты считают, что задача слишком проста, чтобы руки марать. Что ж, пусть считают и дальше. Спасение утопающих - дело рук самих утопающих; поскольку мне нужны модули для чтения и записи графических форматов, желательно не использующие третьесторонних библиотек с их собственными глюками и конфликтами версий, пришлось написать этот небольшой модуль на чистом Python, обеспечивающий чтение и запись изображений 16 бит/канал и 8 бит/канал в пространствах RGB (Portable Pixel Map), серый (L) (Portable Gray Map), а также чтение однобитового формата (Portable Bit Map). Для простоты сведения о совместимости сведены в таблицу ниже.
Текущая версия модуля 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 3.10 и выше. Однако существует версия PyPNM для Python 3.4, успешно протестированная с 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:
python -m pip install --upgrade PyPNMпосле чего:
import pypnmи далее используйте функции, как описано ниже, либо просто посмотрите следующий пример:
#!/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-ах модуля.
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.
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, в которых такой опции не было.
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-функцию.
image3D = create_image(X, Y, Z)
Создаёт пустой трёхмерный список размеров X*Y*Z.
Программа 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 |
|---|
![]() |
Помимо минималистического GUI с использованием мышки а-ля Photoshop, viewer.py также в состоянии переварить аргументы командной строки
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 структура данных (вложенные списки) облегчает написание алгоритмов обработки изображений.
-
PyPNM на PyPI - установка PyPN с помощью
pip. Не включает примеров и т.п., только голый пакет ввода/вывода, затоpipобеспечивает автоматизацию обновлений. -
PyPNM на Github - содержит пример приложения для просмотра, иллюстрирующий применение
list2binдля визуализации данных с помощью Tkinter `PhotoImage(data=...), и конверсию изображений между форматами. -
PyPNM ver.34 на Github - то же, что и п.3, но работает под Python 3.4.
-
PyPNM docs (PDF). Текущая версия документации относится к выпуску 9 мая 2025 года "Victory", но, поскольку капитальная модернизация, проведённая в версии 2 сентября 2025 года "Victory II", не затрагивает структуру ввода и вывода, документация действительна и для новых версий. Новая версия делает всё то же самое, просто старается жрать меньше ресурсов.
PixelArtScaling - пример применения, масштабирование изображений методами Scale2x и Scale3x на чистом Python, ввод/вывод PNG основан на PyPNG, а PNM - на PyPNM, что делает приложения кросс-платформенными.
«Averager» - программа фильтрования изображений методом адаптивного усреднения (написана, поскольку именно такой алгоритм был нужен для специфической цели, а в больших программах типа Photoshop он отсутствует), превью "до" и "после" основано на коде PyPNM list2bin и на классе Tkinter PhotoImage(data=...).
| Рис. 2. Приложение для фильтрования изображений, изрядно основанное на использовании PyPNM |
|---|
![]() |
Собственно фильтр построен на простых вложенных циклах и map(), благодаря создаваемой PyPNM структуре вложенных списков обеспечивающих обработку картинок в разных цветовых пространствах одним незатейливым алгоритмом. Таким образом, представлено небольшое, но полноценное интерактивное приложение для фильтрования изображений, реализованное исключительно на Python.
Модуль imin для билинейной и барицентрической интерполяции изображений, written entirely in Python. Sample applications are largely based on PyPNM.
| Рис. 3. Приложение для фильтрования/деформации изображений, изрядно основанное на использовании PyPNM |
|---|
![]() |
Как и в случае предыдущих программ, модуль использует тот факт, что структура изображений как вложенных списков, создаваемая PyPNM, позволяет обрабатывать картинки с любым количеством каналов одной функцией map(); программы-оболочки показывают превью "до" и "после" с помощью кода PyPNM list2bin и класса Tkinter PhotoImage(data=...).
Dnyarri website - больше программ на Python и остальной товар от Жабы Огромной Умственной Силы.


