# PyPNM - PPM and PGM image files reading and writing in pure Python
## Overview and Justification
PPM and PGM (particular cases of PNM format group) are simplest file formats for RGB and L images, correspondingly. This simplicity lead to some adverse consequences:
- lack of strict official specification. Instead, you may find words like "usual" in format description. Surely, there is someone who implement this part of image format in unprohibited, yet a totally unusual way.
- unwillingness of many software developers to provide any good support to for simple and open format. It took years for almighty Adobe Photoshop developers to include PNM module in distribution rather than count on third-party developers, and surely (see above) they used this chance to implement a separator scheme nobody else uses. What as to PNM support in Python, say, Pillow, it's often incomplete and requires counterintuitive measures when dealing with specific data types.
As a result, novice Python user (like me) may find it difficult to get reliable input/output modules for PPM and PGM image formats; therefore current PyPNM package was developed, combining input/output functions for 8-bits and 16-bits per channel binary and ascii PGM and PPM files, i.e. P2, P5, P3 and P6 PNM file types. Both greyscale and RGB with 16-bit per channel color depth (0...65535 range) are supported directly, without limitations and without any dances with tambourine like using separate methods etc.
Noteworthy that PyPNM module 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.
## 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 RGB 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 to/from such a structure.
Note that for L images memory structure is still `list(list(list(int)))`, with innermost list having only one component, thus enabling further image editing with the same nested Y, X, Z loop regardless of color mode.
Note that for the same reason 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).
## Installation
In case of installing using pip:
`pip install PyPNM`
then in your program import section:
`from pypnm import pnmlpnm`
then use functions as described in section *"pnmlpnm.py functions"* below.
In case you downloaded file **pnmlpnm.py** from Github or somewhere else as plain .py file and not a package, simply put this file into your program folder, then use `import pnmlpnm`.
## pnmlpnm.py functions
Module file **pnmlpnm.py** contains 100% pure Python implementation of everything one may need to read/write a variety of PGM and PPM files. No non-standard dependencies, no extra downloads, no dependency version conflict expected. I/O functions are written as functions/procedures, as simple as possible, and 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** - writing data created with list2bin to file.
- **list2pnmascii** - alternative function to write ASCII PPM (P3) or PGM (P2) files.
- **create_image** - creating empty nested 3D list for image representation. Not used within this particular module but often needed by programs this module is supposed to be used with.
Detailed functions arguments description is provided below as well as in docstrings, but in general looks simple like that - you feed the function with your image data list and a filename, and get PNM file with that name written.
### pnm2list
`X, Y, Z, maxcolors, image3D = pnmlpnm.pnm2list(in_filename)`
read data from PPM/PGM file, 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 = pnmlpnm.list2bin(image3D, maxcolors)`
Convert nested image data list to PGM P5 or PPM P6 (binary) data structure in memory, where:
- `image3D` - `Y*X*Z` list (image) of lists (rows) of lists (pixels) of ints (channels);
- `maxcolors` - number of colors per channel for current image (int).
- `image_bytes` - PNM-structured binary data.
### list2pnm
`pnmlpnm.list2pnm(out_filename, image3D, maxcolors)` where:
- `image3D` - `Y*X*Z` list (image) of lists (rows) of lists (pixels) of ints (channels);
- `maxcolors` - number of colors per channel for current image (int).
- `out_filename` - PNM file name.
### list2pnmascii
Similar to `list2pnm` above but creates ascii pnm file instead of binary.
`pnmlpnm.list2pnmascii(out_filename, image3D, maxcolors)` where:
- `image3D` - `Y*X*Z` list (image) of lists (rows) of lists (pixels) of ints (channels);
- `maxcolors` - number of colors per channel for current image (int).
- `out_filename` - PNM file name.
### create_image
Create empty 3D nested list of `X*Y*Z` sizes.
## References
1. [Netpbm file formats description](https://netpbm.sourceforge.net/doc/).
2. [PyPNM at Github](https://github.com/Dnyarri/PyPNM) containing example viewer application, illustrating using `list2bin` to produce data for Tkinter `PhotoImage(data=...)` to display, and of opening/saving various portable map formats.
Raw data
{
"_id": null,
"home_page": "https://github.com/Dnyarri/PyPNM",
"name": "PyPNM",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "ppm, pgm, pbm, pnm, image, bitmap, format, python",
"author": "Ilya Razmanov",
"author_email": "ilyarazmanov@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/ed/2e/8ffa44f7ed41ff41ee51c17f7b99699d9dfa357a0d16fa558206cee00630/pypnm-1.13.10.7.tar.gz",
"platform": null,
"description": "# PyPNM - PPM and PGM image files reading and writing in pure Python\r\n\r\n## Overview and Justification\r\n\r\nPPM and PGM (particular cases of PNM format group) are simplest file formats for RGB and L images, correspondingly. This simplicity lead to some adverse consequences:\r\n\r\n- lack of strict official specification. Instead, you may find words like \"usual\" in format description. Surely, there is someone who implement this part of image format in unprohibited, yet a totally unusual way.\r\n\r\n- unwillingness of many software developers to provide any good support to for simple and open format. It took years for almighty Adobe Photoshop developers to include PNM module in distribution rather than count on third-party developers, and surely (see above) they used this chance to implement a separator scheme nobody else uses. What as to PNM support in Python, say, Pillow, it's often incomplete and requires counterintuitive measures when dealing with specific data types.\r\n\r\nAs a result, novice Python user (like me) may find it difficult to get reliable input/output modules for PPM and PGM image formats; therefore current PyPNM package was developed, combining input/output functions for 8-bits and 16-bits per channel binary and ascii PGM and PPM files, i.e. P2, P5, P3 and P6 PNM file types. Both greyscale and RGB with 16-bit per channel color depth (0...65535 range) are supported directly, without limitations and without any dances with tambourine like using separate methods etc.\r\n\r\nNoteworthy that PyPNM module 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.\r\n\r\n## Format compatibility\r\n\r\nCurrent PyPNM module read and write capabilities are briefly summarized below.\r\n\r\n| Image format | File format | Read | Write |\r\n| ------ | ------ | ------ | ------ |\r\n| 16 bits per channel RGB | P6 Binary PPM | YES | YES |\r\n| 16 bits per channel RGB | P3 ASCII PPM | YES | YES |\r\n| 8 bits per channel RGB | P6 Binary PPM | YES | YES |\r\n| 8 bits per channel RGB | P3 ASCII PPM | YES | YES |\r\n| 16 bits per channel L | P5 Binary PGM | YES | YES |\r\n| 16 bits per channel L | P2 ASCII PGM | YES | YES |\r\n| 8 bits per channel L | P5 Binary PGM | YES | YES |\r\n| 8 bits per channel L | P2 ASCII PGM | YES | YES |\r\n| 1 bit ink on/off | P4 Binary PBM | YES | NO |\r\n| 1 bit ink on/off | P1 ASCII PBM | YES | NO |\r\n\r\n## Target image representation\r\n\r\nMain goal of module under discussion is not just bytes reading and writing but representing image as some logically organized structure for further image editing.\r\n\r\nIs seems logical to represent an RGB image as nested 3D structure - (X, Y)-sized matrix of three-component RGB 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 to/from such a structure.\r\n\r\nNote that for L images memory structure is still `list(list(list(int)))`, with innermost list having only one component, thus enabling further image editing with the same nested Y, X, Z loop regardless of color mode.\r\n\r\nNote that for the same reason 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).\r\n\r\n## Installation\r\n\r\nIn case of installing using pip:\r\n\r\n`pip install PyPNM`\r\n\r\nthen in your program import section:\r\n\r\n`from pypnm import pnmlpnm`\r\n\r\nthen use functions as described in section *\"pnmlpnm.py functions\"* below.\r\n\r\nIn case you downloaded file **pnmlpnm.py** from Github or somewhere else as plain .py file and not a package, simply put this file into your program folder, then use `import pnmlpnm`.\r\n\r\n## pnmlpnm.py functions\r\n\r\nModule file **pnmlpnm.py** contains 100% pure Python implementation of everything one may need to read/write a variety of PGM and PPM files. No non-standard dependencies, no extra downloads, no dependency version conflict expected. I/O functions are written as functions/procedures, as simple as possible, and listed below:\r\n\r\n- **pnm2list** - reading binary or ascii RGB PPM or L PGM file and returning image data as nested list of int.\r\n- **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.\r\n- **list2pnm** - writing data created with list2bin to file.\r\n- **list2pnmascii** - alternative function to write ASCII PPM (P3) or PGM (P2) files.\r\n- **create_image** - creating empty nested 3D list for image representation. Not used within this particular module but often needed by programs this module is supposed to be used with.\r\n\r\nDetailed functions arguments description is provided below as well as in docstrings, but in general looks simple like that - you feed the function with your image data list and a filename, and get PNM file with that name written.\r\n\r\n### pnm2list\r\n\r\n`X, Y, Z, maxcolors, image3D = pnmlpnm.pnm2list(in_filename)`\r\n\r\nread data from PPM/PGM file, where:\r\n\r\n- `X, Y, Z` - image sizes (int);\r\n- `maxcolors` - number of colors per channel for current image (int);\r\n- `image3D` - image pixel data as list(list(list(int)));\r\n- `in_filename` - PPM/PGM file name (str).\r\n\r\n### list2bin\r\n\r\n`image_bytes = pnmlpnm.list2bin(image3D, maxcolors)`\r\n\r\nConvert nested image data list to PGM P5 or PPM P6 (binary) data structure in memory, where:\r\n\r\n- `image3D` - `Y*X*Z` list (image) of lists (rows) of lists (pixels) of ints (channels);\r\n- `maxcolors` - number of colors per channel for current image (int).\r\n- `image_bytes` - PNM-structured binary data.\r\n\r\n### list2pnm\r\n\r\n`pnmlpnm.list2pnm(out_filename, image3D, maxcolors)` where:\r\n\r\n- `image3D` - `Y*X*Z` list (image) of lists (rows) of lists (pixels) of ints (channels);\r\n- `maxcolors` - number of colors per channel for current image (int).\r\n- `out_filename` - PNM file name.\r\n\r\n### list2pnmascii\r\n\r\nSimilar to `list2pnm` above but creates ascii pnm file instead of binary.\r\n\r\n`pnmlpnm.list2pnmascii(out_filename, image3D, maxcolors)` where:\r\n\r\n- `image3D` - `Y*X*Z` list (image) of lists (rows) of lists (pixels) of ints (channels);\r\n- `maxcolors` - number of colors per channel for current image (int).\r\n- `out_filename` - PNM file name.\r\n\r\n### create_image\r\n\r\nCreate empty 3D nested list of `X*Y*Z` sizes.\r\n\r\n## References\r\n\r\n1. [Netpbm file formats description](https://netpbm.sourceforge.net/doc/).\r\n\r\n2. [PyPNM at Github](https://github.com/Dnyarri/PyPNM) containing example viewer application, illustrating using `list2bin` to produce data for Tkinter `PhotoImage(data=...)` to display, and of opening/saving various portable map formats.\r\n",
"bugtrack_url": null,
"license": null,
"summary": "Reading and writing PPM and PGM image files, including 16 bits per channel, in pure Python.",
"version": "1.13.10.7",
"project_urls": {
"Homepage": "https://github.com/Dnyarri/PyPNM"
},
"split_keywords": [
"ppm",
" pgm",
" pbm",
" pnm",
" image",
" bitmap",
" format",
" python"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "2edd8fe834bc0b314385501fb868432ddd5045071be170e5c22071d70511f8aa",
"md5": "bf31eee2fea43372b2426c4c34e925cf",
"sha256": "726de753a618d61968fbd30b42586b47659e78e353f2364f9cf9371ac518fd53"
},
"downloads": -1,
"filename": "PyPNM-1.13.10.7-py3-none-any.whl",
"has_sig": false,
"md5_digest": "bf31eee2fea43372b2426c4c34e925cf",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 8973,
"upload_time": "2025-01-10T13:19:37",
"upload_time_iso_8601": "2025-01-10T13:19:37.462405Z",
"url": "https://files.pythonhosted.org/packages/2e/dd/8fe834bc0b314385501fb868432ddd5045071be170e5c22071d70511f8aa/PyPNM-1.13.10.7-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "ed2e8ffa44f7ed41ff41ee51c17f7b99699d9dfa357a0d16fa558206cee00630",
"md5": "c1bbb6fc68d3634a2817c906ccb1259c",
"sha256": "4ab18d44ad929fea4da93b37e59e75720712ba710f33bb8c51ebcc0c4aeae46c"
},
"downloads": -1,
"filename": "pypnm-1.13.10.7.tar.gz",
"has_sig": false,
"md5_digest": "c1bbb6fc68d3634a2817c906ccb1259c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 8286,
"upload_time": "2025-01-10T13:19:40",
"upload_time_iso_8601": "2025-01-10T13:19:40.091522Z",
"url": "https://files.pythonhosted.org/packages/ed/2e/8ffa44f7ed41ff41ee51c17f7b99699d9dfa357a0d16fa558206cee00630/pypnm-1.13.10.7.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-10 13:19:40",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Dnyarri",
"github_project": "PyPNM",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "pypnm"
}