{"id":29434388,"url":"https://github.com/dnyarri/pypnm","last_synced_at":"2026-02-27T18:01:07.247Z","repository":{"id":266415842,"uuid":"897196202","full_name":"Dnyarri/PyPNM","owner":"Dnyarri","description":"Pure Python PGM and PPM image files read/write support, both 8 and 16 bits per channel.","archived":false,"fork":false,"pushed_at":"2026-02-23T17:19:59.000Z","size":842,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-24T00:59:19.290Z","etag":null,"topics":["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"],"latest_commit_sha":null,"homepage":"https://dnyarri.github.io/pypnm.html","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Dnyarri.png","metadata":{"files":{"readme":"README.RU.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-12-02T07:55:15.000Z","updated_at":"2026-02-23T17:17:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"e14cb682-9363-408b-8765-1fb3771fb192","html_url":"https://github.com/Dnyarri/PyPNM","commit_stats":null,"previous_names":["dnyarri/pypnm"],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/Dnyarri/PyPNM","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dnyarri%2FPyPNM","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dnyarri%2FPyPNM/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dnyarri%2FPyPNM/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dnyarri%2FPyPNM/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Dnyarri","download_url":"https://codeload.github.com/Dnyarri/PyPNM/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dnyarri%2FPyPNM/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29907344,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T17:28:36.873Z","status":"ssl_error","status_checked_at":"2026-02-27T17:28:20.970Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["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"],"created_at":"2025-07-13T02:16:02.146Z","updated_at":"2026-02-27T18:01:07.228Z","avatar_url":"https://github.com/Dnyarri.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n| [〖EN〗](README.md \"Приблизительно тот же трёп, но по-английски\") | 【RU】 |\n| ---- | ---- |\n\n# PyPNM - модуль чтения и записи графических форматов PPM и PGM на чистом Python\n\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/pypnm)](https://pypi.org/project/PyPNM/)\n\n## Обзор и предпосылки\n\nPyPNM представляет собой чистый пытоновый модуль для\n\n- чтения файлов изображений PPM и PGM (как 8, так и 16 бит на канал) в 3D-список для дальнейшего редактирования;\n\n- отображение полученных 3D-списков путём превращения оных на лету в байт-объекты в памяти, пригодные для отрисовки средствами Tkinter; и в завершение\n\n- превращение отредактированных 3D списков в памяти в PPM или PGM-файлы на диске, бинарные или ASCII.\n\nГрафические форматы 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 (серый), соответственно. Как ни странно, эта простота приводит к нежелательным побочным явлениям:\n\n- Отсутствие чёткой официальной спецификации. Вместо этого описание формата изобилует словами типа \"обычно\". Не нужно быть пророком, чтобы предсказать, что кто-нибудь в рамках этой расплывчатости напишет файлы  ненаказуемым, но извращённым способом.\n\n- Явное нежелание многих фирменных разработчиков включать поддержку открытого простого формата в свои продукты. От разработчиков Adobe Photoshop потребовались годы, чтобы заметить, что народ качает третьесторонние плагины, то есть зачем-то народу этот формат нужен, после чего, наконец-то взявшись за дело, разработчики незамедлительно поступили вышеуказанным способом, напхав в заголовок схему разделителей, которую не использует больше никто.\n\n  Что касается поддержки PNM в Python, например, таких популярных библиотек, как Pillow, ситуация может быть стабильно описана как \"ни в сказке сказать, ни при дамах произнести\".\n\nПо всей видимости, профессиональные программисты считают, что задача слишком проста, чтобы руки марать. Что ж, пусть считают и дальше. Спасение утопающих - дело рук самих утопающих; поскольку мне нужны модули для чтения и записи графических форматов, желательно не использующие третьесторонних библиотек с их собственными глюками и конфликтами версий, пришлось написать этот небольшой модуль на чистом 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)). Для простоты сведения о совместимости сведены в таблицу ниже.\n\n## Совместимость форматов\n\nТекущая версия модуля PyPNM обеспечивает чтение и запись следующих версий форматов группы Netpbm:\n\n| Image format | File format | Read | Write |\n| ------ | ------ | ------ | ------ |\n| 16 бит/канал RGB | P6 двоичный PPM | ✅ | ✅ |\n| 16 бит/канал RGB | P3 текстовый PPM | ✅ | ✅ |\n| 8 бит/канал RGB | P6 двоичный PPM | ✅ | ✅ |\n| 8 бит/канал RGB | P3 текстовый PPM | ✅ | ✅ |\n| 16 бит/канал L | P5 двоичный PGM | ✅ | ✅ |\n| 16 бит/канал L | P2 текстовый PGM | ✅ | ✅ |\n| 8 бит/канал L | P5 двоичный PGM | ✅ | ✅ |\n| 8 бит/канал L | P2 текстовый PGM | ✅ | ✅ |\n| 1 бит (краска есть/нет) | P4 двоичный PBM | ✅ | ❌ |\n| 1 бит (краска есть/нет) | P1 текстовый PBM | ✅ | ❌ |\n\n## Совместимость с Python\n\nТекущая версия протестирована под Python 3.10 и выше. Однако существует версия [PyPNM для Python 3.4](https://github.com/Dnyarri/PyPNM/tree/py34/), успешно протестированная с Python 3.4 под Windows XP 32-bit.\n\n## Представление изображения\n\nПоскольку целью данного модуля является не передвигание битов по диску, а обеспечение возможности работы с изображениями, например, их редактирования, модуль должен выдавать и принимать данные в виде какой-то логичным образом устроенной структуры. Представляется логичным представлять, например, RGB-изображение в виде трёхмерной структуры - картинки из рядов из пикселей из цветовых каналов. Поскольку в Python практически единственной изменяемой структурой данных для этого является список, предполагается представлять изображения в формате `list(list(list(int)))`. Таким образом, модуль должен превращать данные из не пойми чего на диске в трёхмерные вложенные списки в памяти.\n\nЗаметим, что и для серых картинок структурой остаётся `list(list(list(int)))`, при этом внутренний список состоит из одного элемента. С точки зрения профессиональных программистов это безобразие, но людям обыкновенным даёт возможность обрабатывать картинки в любом пространстве одним и тем же вложенным циклом по Y, X, Z, просто указывая количество каналов Z.\n\nЗаметим также, что, поскольку конечной целью является всё-таки обработка изображений, данный модуль обеспечивает поддержку 1-битовых форматов PBM только на чтение, при этом масштабирует цвет из 1 бита \"краска есть/краски нет\" в обычный яркостный L, т.е. инвертирует исходный сигнал (т.к. \"краска есть\" соответствует \"яркости нет\"), и умножает на 255.\n\n## Установка\n\nВ случае использования pip:\n\n```console\npython -m pip install --upgrade PyPNM\n```\n\nпосле чего:\n\n```python\nimport pypnm\n```\n\nи далее используйте функции, как описано ниже, либо просто посмотрите следующий пример:\n\n## Пример\n\n```python\n#!/usr/bin/env python3\n\nfrom tkinter import Button, PhotoImage, Tk\n\nfrom pypnm import list2bin, list2pnm, pnm2list\n\nX, Y, Z, maxcolors, image3D = pnm2list('example.ppm')  # Open \"example.ppm\"\nlist2pnm('binary.ppm', image3D, maxcolors, bin=True)  # Save as binary pnm\nlist2pnm('ascii.ppm', image3D, maxcolors, bin=False)  # Save as ascii pnm\n\nmain_window = Tk()\nmain_window.title('PyPNM demo')\npreview_data = list2bin(image3D, maxcolors)  # Image list 🡢 preview bytes\npreview = PhotoImage(data=preview_data)  # Preview bytes 🡢 PhotoImage object\npreview_button = Button(main_window, text='Example\\n(click to exit)', image=preview,\n    compound='top', command=lambda: main_window.destroy())  # Showing PhotoImage\npreview_button.pack()\nmain_window.mainloop()\n\n```\n\nВыше приведён пример минимальной программы, использующей все функции PyPNM: она читает файл PPM, записывает прочитанную картинку в виде двоичного PPM, записывает её же в виде ASCII PPM, а напоследок показывает её на экране средствами Tkinter.\n\n## Описание функций\n\nМодуль PyPNM содержит 100% чистую реализацию на Python всего, что может понадобиться для работы с файлами PGM и PPM. Ввод/вывод реализован в форме максимально простых в обращении функций, использование которых описано ниже. Также использование функций описано в docstrings, так что потерять инструкцию к модулю у вас вряд ли получится.\n\n- **pnm2list**  - читает RGB PPM, или L PGM, или 1-бит PBM-файл, и возвращает данные в форме вложенного трёхмерного списка целых чисел.\n- **list2bin**  - получает изображение в форме вложенного трёхмерного списка целых чисел, и создаёт в памяти байтовую структуру типа PPM (P6) или PGM (P5). Полученная структура может быть легко использована для визуализации изображений с помощью Tkinter.\n- **list2pnm**  - получает изображение в форме вложенного трёхмерного списка целых чисел, и записывает на диск либо двоичный PPM (P6) или PGM (P5) файл, либо ASCII PPM (P3) или PGM (P2), в зависимости от аргумента-переключателя.\n\nПодробное описание аргументов функций приведено ниже,а также в docstring-ах модуля.\n\n### pnm2list\n\n`X, Y, Z, maxcolors, image3D = pypnm.pnm2list(in_filename)`\n\nчтение данных из файла PPM/PGM, где:\n\n- `X, Y, Z`   - размеры изображения, int;\n- `maxcolors` - количество цветов на канал, int;\n- `image3D`   - собственно данные пикселей, list(list(list(int)));\n- `in_filename` - имя файла PPM/PGM, str.\n\n### list2bin\n\n`image_bytes = pypnm.list2bin(image3D, maxcolors, show_chessboard)`\n\nПревращает изображение в форме вложенного трёхмерного списка целых чисел в байтовый объект типа PPM (P6) или PGM (P5) в памяти:\n\n- `image3D`   - `Y*X*Z` список (изображение) списков (рядов) списков (пикселей) целых чисел (каналов);\n- `maxcolors` - количество цветов на канал, int;\n- `show_chessboard` - bool, при установке `True` генерирует превью LA и RGBA-картинок на фоне шахматной паттерны; `False` или отсутствие переменной приводит к простому игнорированию альфа-канала. Значение по умолчанию `False` для задней совместимости;\n- `image_bytes` - байты PNM.\n\nПолученный объект `image_bytes` совместим с методом Tkinter `PhotoImage(data=...)` и предназначен для визуализации любых данных, похожих на картинку в виде 3D-списка.\n\n\u003e [!NOTE]\n\u003e В случае списков с 2 или 4 каналами свежая версия `list2bin` может считать их LA или RGBA картинками, и генерировать для них превью на фоне шахматной паттерны (как Photoshop или GIMP). Поскольку форматы PNM не поддерживают прозрачности, данная картинка на самом деле имеет структуру L или RGB, а паттерну генерирует и подмешивает на лету сама функция `list2bin`. Данное поведение контролируется переменной `show_chessboard`; текущая установка `False` (просто выбрасывает альфа-канал) для совместимости со старыми версиями PyPNM, в которых такой опции не было.\n\n### list2pnm\n\n`pypnm.list2pnm(out_filename, image3D, maxcolors, bin)`\n\nЗапись картинки в бинарный или ASCII файл:\n\n- `image3D`     - `Y*X*Z` список (изображение) списков (рядов) списков (пикселей) целых чисел (каналов);\n- `maxcolors`   - количество цветов на канал, int;\n- `bin`         - переключатель (bool) между записью двоичного и ASCII файла. Текущее положение True, то есть запись двоичного.\n- `out_filename`    - имя файла PNM.\n\nЗаметьте, что `list2pnm` представляет собой просто переключатель между внутренними функциями `list2pnmbin` and `list2pnmascii`, облегчающий написание функций типа \"Save as...\", особенно для графических диалогов - можно сохранять разные типы файлов одной функцией, просто передавая `bin` через lambda-функцию.\n\n### create_image\n\n`image3D = create_image(X, Y, Z)`\n\nСоздаёт пустой трёхмерный список размеров `X*Y*Z`.\n\n## viewer.py\n\nПрограмма [**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 не поддерживает.\n\n| Рис. 1. *Пример отрисовки текстового .ppm с помощью Viewer.py* |\n| :---: |\n| [![Пример отрисовки текстового .ppm с помощью Viewer.py](https://dnyarri.github.io/pypnm/viewer.png \"Пример отрисовки текстового .ppm с помощью Viewer.py\")](https://dnyarri.github.io/pypnm.html) |\n\nПомимо минималистического GUI с использованием мышки а-ля Photoshop, *viewer.py* также в состоянии переварить аргументы командной строки\n\n```console\npython viewer.py filename.ppm\n```\n\nдля открытия файлов. В принципе, вы можете даже зарегистрировать его в качестве системного смотрела для файлов PPM, PGM и PBM, и стать первым человеком на этой планете с системным смотрелом, написанном на Python.\n\n\u003e [!NOTE]\n\u003e Начиная с версии 2.21.22.23 Viewer снабжен пунктом \"Export via Tkinter...\" а основном меню, позволяющим сохранить открытое изображение с помощью 100% натуральных экологически чистых механизмов Tkinter `PhotoImage.write`. Данная опция включена исключительно для иллюстрации ограничений Tkinter. Например, изображение с цветовой глубиной 16 бит на канал после сохранения таким способом превращается в 8 бит на канал, потому что иначе Tkinter не умеет. Это несколько объясняет одну из причин, по которым я был вынужден написать свой собственный модуль, которому всё равно, что у вас за картинка - он обработает её правильно.\n\n## Заключение\n\nИспользование *PyPNM* и Tkinter позволяет легко визуализировать любые данные, которые могут быть представлены в виде L/RGB изображений, и в первую очередь собственно картинки.\n\n*PyPNM* обеспечивает чтение и запись файлов PPM и PGM с цветовой глубиной как 8б так и 16 бит на канал, а также чтение однобитовых PBM.\n\nИспользуемая *PyPNM* структура данных (вложенные списки) облегчает написание алгоритмов обработки изображений.\n\n## Ссылки\n\n1. [Описание форматов Netpbm](https://netpbm.sourceforge.net/doc/).\n\n2. [PyPNM на PyPI](https://pypi.org/project/PyPNM/) - установка PyPN с помощью `pip`. Не включает примеров и т.п., только голый пакет ввода/вывода, зато `pip` обеспечивает автоматизацию обновлений.\n\n3. [PyPNM на Github](https://github.com/Dnyarri/PyPNM/) - содержит пример приложения для просмотра, иллюстрирующий применение `list2bin` для визуализации данных с помощью Tkinter `PhotoImage(data=...), и конверсию изображений между форматами.\n\n4. [PyPNM ver.34 на Github](https://github.com/Dnyarri/PyPNM/tree/py34) - то же, что и п.3, но работает под Python 3.4.\n\n5. [PyPNM docs (PDF)](https://dnyarri.github.io/pypnm/pypnm.pdf). Текущая версия документации относится к выпуску 9 мая 2025 года \"Victory\", но, поскольку капитальная модернизация, проведённая в версии 2 сентября 2025 года \"Victory II\", не затрагивает структуру ввода и вывода, документация действительна и для новых версий. Новая версия делает всё то же самое, просто старается жрать меньше ресурсов.\n\n## Иллюстрации\n\n[PixelArtScaling](https://dnyarri.github.io/scalenx.html) - пример применения, масштабирование изображений методами Scale2x и Scale3x на чистом Python, ввод/вывод PNG основан на [PyPNG](https://gitlab.com/drj11/pypng), а PNM - на [PyPNM](https://pypi.org/project/PyPNM/), что делает приложения кросс-платформенными.\n\n[«Averager»](https://dnyarri.github.io/povthread.html#averager) - программа фильтрования изображений методом адаптивного усреднения (написана, поскольку именно такой алгоритм был нужен для специфической цели, а в больших программах типа Photoshop он отсутствует), превью \"до\" и \"после\" основано на коде PyPNM `list2bin` и на классе Tkinter `PhotoImage(data=...)`.\n\n| Рис. 2. *Приложение для фильтрования изображений, изрядно основанное на использовании PyPNM* |\n| :---: |\n| [![Пример приложения для фильтрования изображений с применением PyPNM](https://dnyarri.github.io/thread/ave.png \"Пример приложения для фильтрования изображений с применением PyPNM\")](https://dnyarri.github.io/povthread.html#averager) |\n\nСобственно фильтр построен на простых вложенных циклах и `map()`, благодаря создаваемой PyPNM структуре вложенных списков обеспечивающих обработку картинок в разных цветовых пространствах одним незатейливым алгоритмом. Таким образом, представлено небольшое, но полноценное интерактивное приложение для фильтрования изображений, реализованное исключительно на Python.\n\n[Модуль **imin** для билинейной и барицентрической интерполяции изображений](https://dnyarri.github.io/imin.html), written entirely in Python. Sample applications are largely based on PyPNM.\n\n| Рис. 3. *Приложение для фильтрования/деформации изображений, изрядно основанное на использовании PyPNM* |\n| :---: |\n| [![Пример приложения для фильтрования изображений с применением PyPNM](https://dnyarri.github.io/imin/anigui.png \"Пример приложения для фильтрования изображений с применением PyPNM\")](https://dnyarri.github.io/imin.html \"Пример приложения для фильтрования изображений с применением PyPNM\") |\n\nКак и в случае предыдущих программ, модуль использует тот факт, что структура изображений как вложенных списков, создаваемая PyPNM, позволяет обрабатывать картинки с любым количеством каналов одной функцией `map()`; программы-оболочки показывают превью \"до\" и \"после\" с помощью кода PyPNM `list2bin` и класса Tkinter `PhotoImage(data=...)`.\n\n---\n\n[Dnyarri website - больше программ на Python](https://dnyarri.github.io/) и остальной товар от Жабы Огромной Умственной Силы.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdnyarri%2Fpypnm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdnyarri%2Fpypnm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdnyarri%2Fpypnm/lists"}