55Overview
66---------
77
8- pnmlpnm (pnm-list-pnm) module is a pack of functions for dealing with PPM and PGM image files.
8+ PyPNM module is a pack of functions for dealing with PPM and PGM image files.
99Functions included are:
1010
11- - pnm2list: reading binary or ascii RGB PPM or L PGM file and returning image data as nested list of int.
12- - list2bin: getting image data as nested list of int
13- and creating binary PPM (P6) or PGM (P5) data structure in memory.
14- Suitable for generating data to display with Tkinter `PhotoImage(data=...)` class.
15- - list2pnm: getting image data as nested list of int and writing binary PPM (P6) or PGM (P5) image file.
16- Note that bytes generations procedure is optimized to save memory
17- while working with large files and therefore is different from that used in 'list2bin'.
11+ - pnm2list: reading binary or ascii RGB PPM or L PGM file and returning image data
12+ as nested list of int.
13+ - list2bin: getting image data as nested list of int and creating binary PPM (P6) or PGM (P5)
14+ data structure in memory. Suitable for generating data to display with
15+ Tkinter `PhotoImage(data=...)` class.
16+ - list2pnmbin: getting image data as nested list of int and writing binary PPM (P6) or PGM (P5) image file.
17+ Note that bytes generations procedure is optimized to save memory while working with large files and
18+ therefore is different from that used in 'list2bin'.
1819- list2pnmascii: alternative function to write ASCII PPM (P3) or PGM (P2) files.
20+ - list2pnm: getting image data as nested list of int and writing either binary or ASCII PNM
21+ depending on `bin` argument value.
1922- create_image: creating empty nested 3D list for image representation.
2023Not used within this particular module but often needed by programs this module is supposed to be used with.
2124
3437
3538Usage
3639------
37- After `import pnmlpnm`, use something like:
40+
41+ After `from pypnm import pnmlpnm`, use something like:
3842
3943 `X, Y, Z, maxcolors, list_3d = pnmlpnm.pnm2list(in_filename)`
4044
4145for reading data from PPM/PGM, where:
4246
43- - `X`, `Y`, `Z`: image dimensions (int);
44- - `maxcolors`: number of colors per channel for current image (int);
45- - `list_3d`: image pixel data as list(list(list(int)));
47+ - `X`, `Y`, `Z`: image dimensions (int);
48+ - `maxcolors`: number of colors per channel for current image (int);
49+ - `list_3d`: image pixel data as list(list(list(int)));
4650
4751and:
4852
5256
5357or:
5458
55- `pnmlpnm.list2pnm(out_filename, list_3d, maxcolors)`
56-
57- for writing data from `list_3d` nested list to binary PPM/PGM file `out_filename`, or:
58-
59- `pnmlpnm.list2pnmascii(out_filename, list_3d, maxcolors)`
60-
61- for writing data from `list_3d` nested list to ASCII PPM/PGM file `out_filename`.
59+ `pnmlpnm.list2pnm(out_filename, list_3d, maxcolors, bin)`
6260
61+ for writing data from `list_3d` nested list to PPM/PGM file `out_filename`,
62+ where `bin` is a bool switch defining where resulting file will be binary or ASCII.
6363
6464Copyright and redistribution
6565-----------------------------
66+
6667Written by `Ilya Razmanov <https://dnyarri.github.io/>`_ to facilitate working with PPM/PGM files
67- and converting image-like data to PPM/PGM bytes to be displayed with Tkinter `PhotoImage` class.
68+ and displaying arbitrary image-like data with Tkinter `PhotoImage` class.
6869
6970May be freely used, redistributed and modified.
7071In case of introducing useful modifications, please report to the developer.
7172
7273References
7374-----------
7475
75- `Netpbm specifications <https://netpbm.sourceforge.net/doc/>`_
76-
77- `PyPNM at GitHub <https://github.com/Dnyarri/PyPNM/>`_
78-
79- `PyPNM at PyPI <https://pypi.org/project/PyPNM/>`_
76+ 1. Netpbm specifications: https://netpbm.sourceforge.net/doc/
77+ 2. PyPNM at GitHub: https://github.com/Dnyarri/PyPNM/
78+ 3. PyPNM at PyPI: https://pypi.org/project/PyPNM/
79+ 4. PyPNM Documentation: https://dnyarri.github.io/pypnm/pypnm.pdf
8080
8181"""
8282
8383__author__ = 'Ilya Razmanov'
8484__copyright__ = '(c) 2024-2025 Ilya Razmanov'
8585__credits__ = 'Ilya Razmanov'
8686__license__ = 'unlicense'
87- __version__ = '1.16.1.9 '
87+ __version__ = '1.16.29.19 '
8888__maintainer__ = 'Ilya Razmanov'
8989__email__ = 'ilyarazmanov@gmail.com'
9090__status__ = 'Production'
100100def pnm2list (in_filename : str ) -> tuple [int , int , int , int , list [list [list [int ]]]]:
101101 """Read PGM or PPM file to nested image data list.
102102
103- Usage
104- -
103+ Usage:
105104
106105 `X, Y, Z, maxcolors, list_3d = pnmlpnm.pnm2list(in_filename)`
107106
108107 for reading data from PPM/PGM, where:
109108
110- - `X`, `Y`, `Z`: image dimensions (int);
111- - `maxcolors`: number of colors per channel for current image (int);
112- - `list_3d`: image pixel data as list(list(list(int)));
113- - `in_filename`: PPM/PGM file name (str).
109+ - `X`, `Y`, `Z`: image dimensions (int);
110+ - `maxcolors`: number of colors per channel for current image (int);
111+ - `list_3d`: image pixel data as list(list(list(int)));
112+ - `in_filename`: PPM/PGM file name (str).
114113
115114 """
116115
@@ -274,6 +273,8 @@ def pnm2list(in_filename: str) -> tuple[int, int, int, int, list[list[list[int]]
274273
275274 else :
276275 raise ValueError (f'Unsupported format { in_filename } : { full_bytes [:32 ]} ' )
276+
277+
277278# End of pnm2list PNM reading function
278279
279280
@@ -287,20 +288,16 @@ def list2bin(list_3d: list[list[list[int]]], maxcolors: int, show_chessboard: bo
287288
288289 Based on `Netpbm specifications<https://netpbm.sourceforge.net/doc/>`_.
289290
290- Usage
291- -
291+ Usage:
292292
293- `image_bytes = pnmlpnm.list2bin(list_3d, maxcolors, show_chessboard)` where:
293+ `image_bytes = pnmlpnm.list2bin(list_3d, maxcolors, show_chessboard)`
294294
295- Input :
295+ where :
296296
297- - `list_3d`: Y * X * Z list (image) of lists (rows) of lists (pixels) of ints (channel values);
298- - `maxcolors`: number of colors per channel for current image (int);
299- - `show_chessboard`: optional bool, set `True` to show LA and RGBA images against chessboard pattern; `False` or missing show existing L or RGB data for transparent areas as opaque. Default is `False` for backward compatibility.
300-
301- Output:
302-
303- - `image_bytes`: PNM-structured binary data.
297+ - `list_3d`: Y * X * Z list (image) of lists (rows) of lists (pixels) of ints (channel values);
298+ - `maxcolors`: number of colors per channel for current image (int);
299+ - `show_chessboard`: optional bool, set `True` to show LA and RGBA images against chessboard pattern; `False` or missing show existing L or RGB data for transparent areas as opaque. Default is `False` for backward compatibility.
300+ - `image_bytes`: PNM-structured binary data.
304301
305302 """
306303
@@ -309,7 +306,7 @@ def _chess(x: int, y: int) -> int:
309306
310307 Photoshop chess pattern preset parameters:
311308 Small: 4 px; Medium: 8 px, Large: 16 px
312- Light: 0.8, 1; Medium: 0.4, 0.6; Dark: 0.2, 0.4 of maxcolors
309+ Light: ( 0.8, 1.0) ; Medium: ( 0.4, 0.6) ; Dark: ( 0.2, 0.4) of maxcolors
313310
314311 """
315312 return int (maxcolors * 0.8 ) if ((y // 8 ) % 2 ) == ((x // 8 ) % 2 ) else maxcolors
@@ -330,7 +327,7 @@ def _chess(x: int, y: int) -> int:
330327 list_1d = [list_3d [y ][x ][z ] for y in range (Y ) for x in range (X ) for z in range (Z_READ )]
331328
332329 else : # Source has alpha
333- Z_READ = min (Z , 4 ) - 1 # To fiddle with alpha; clipping anything above RGBA off
330+ Z_READ = min (Z , 4 ) - 1 # To fiddle with alpha; clipping anything above RGB off
334331
335332 if show_chessboard :
336333 # Flattening 3D list to 1D list, mixing with chessboard
@@ -353,25 +350,28 @@ def _chess(x: int, y: int) -> int:
353350 content .byteswap () # Critical for 16 bits per channel
354351
355352 return b'' .join ((f'{ magic } \n { X } { Y } \n { maxcolors } \n ' .encode ('ascii' ), content .tobytes ()))
353+
354+
356355# End of 'list2bin' list to in-memory PNM conversion function
357356
358357
359- """ ╔══════════╗
360- ║ list2pnm ║
361- ╚══════════╝ """
358+ """ ╔═════════════ ╗
359+ ║ list2pnmbin ║
360+ ╚═════════════ ╝ """
362361
363362
364- def list2pnm (out_filename : str , list_3d : list [list [list [int ]]], maxcolors : int ) -> None :
363+ def list2pnmbin (out_filename : str , list_3d : list [list [list [int ]]], maxcolors : int ) -> None :
365364 """Write binary PNM `out_filename` file; writing performed per row to reduce RAM usage.
366365
367- Usage
368- -
366+ Usage:
367+
368+ `pnmlpnm.list2pnm(out_filename, list_3d, maxcolors)`
369369
370- `pnmlpnm.list2pnm(out_filename, list_3d, maxcolors)` where:
370+ where:
371371
372- - `list_3d`: X * Y * Z list (image) of lists (rows) of lists (pixels) of ints (channels);
373- - `maxcolors`: number of colors per channel for current image (int);
374- - `out_filename`: PNM file name.
372+ - `list_3d`: X * Y * Z list (image) of lists (rows) of lists (pixels) of ints (channels);
373+ - `maxcolors`: number of colors per channel for current image (int);
374+ - `out_filename`: PNM file name.
375375
376376 """
377377
@@ -388,7 +388,7 @@ def list2pnm(out_filename: str, list_3d: list[list[list[int]]], maxcolors: int)
388388 if Z == 3 or Z == 1 :
389389 Z_READ = Z
390390 else :
391- Z_READ = min (Z , 4 ) - 1 # To skip alpha later
391+ Z_READ = min (Z , 4 ) - 1 # To skip alpha later; clipping anything above RGB off
392392
393393 if maxcolors < 256 :
394394 datatype = 'B'
@@ -404,6 +404,8 @@ def list2pnm(out_filename: str, list_3d: list[list[list[int]]], maxcolors: int)
404404 file_pnm .write (row_array ) # Adding row bytes array to file
405405
406406 return None
407+
408+
407409# End of 'list2pnm' function writing binary PPM/PGM file
408410
409411
@@ -415,14 +417,15 @@ def list2pnm(out_filename: str, list_3d: list[list[list[int]]], maxcolors: int)
415417def list2pnmascii (out_filename : str , list_3d : list [list [list [int ]]], maxcolors : int ) -> None :
416418 """Write ASCII PNM `out_filename` file; writing performed per sample to reduce RAM usage.
417419
418- Usage
419- -
420+ Usage:
421+
422+ `pnmlpnm.list2pnmascii(out_filename, list_3d, maxcolors)`
420423
421- `pnmlpnm.list2pnmascii(out_filename, list_3d, maxcolors)` where:
424+ where:
422425
423- - `list_3d`: Y * X * Z list (image) of lists (rows) of lists (pixels) of ints (channels);
424- - `maxcolors`: number of colors per channel for current image (int);
425- - `out_filename`: PNM file name.
426+ - `list_3d`: Y * X * Z list (image) of lists (rows) of lists (pixels) of ints (channels);
427+ - `maxcolors`: number of colors per channel for current image (int);
428+ - `out_filename`: PNM file name.
426429
427430 """
428431
@@ -431,22 +434,13 @@ def list2pnmascii(out_filename: str, list_3d: list[list[list[int]]], maxcolors:
431434 X = len (list_3d [0 ])
432435 Z = len (list_3d [0 ][0 ])
433436
434- if Z == 1 : # L image
437+ if Z < 3 : # L or LA image
435438 magic = 'P2'
436439 Z_READ = 1
437-
438- if Z == 2 : # LA image
439- magic = 'P2'
440- Z_READ = 1 # To skip alpha later
441-
442- if Z == 3 : # RGB image
440+ else : # RGB or RGBA image
443441 magic = 'P3'
444442 Z_READ = 3
445443
446- if Z > 3 : # RGBA image
447- magic = 'P3'
448- Z_READ = 3 # To skip alpha later
449-
450444 with open (out_filename , 'w' ) as file_pnm :
451445 file_pnm .write (f'{ magic } \n { X } { Y } \n { maxcolors } \n ' ) # Writing PNM header to file
452446 sample_count = 0 # Start counting samples to break line <= 60 char
@@ -459,9 +453,42 @@ def list2pnmascii(out_filename: str, list_3d: list[list[list[int]]], maxcolors:
459453 file_pnm .write (f'{ list_3d [y ][x ][z ]} ' ) # Writing channel value to file
460454
461455 return None
456+
457+
462458# End of 'list2pnmascii' function writing ASCII PPM/PGM file
463459
464460
461+ """ ╔══════════╗
462+ ║ list2pnm ║
463+ ╚══════════╝ """
464+
465+
466+ def list2pnm (out_filename : str , list_3d : list [list [list [int ]]], maxcolors : int , bin : bool = True ) -> None :
467+ """Write PNM `out_filename` file using either `list2pnmbin` or `list2pnmascii` depending on `bin` switch.
468+
469+ Usage:
470+
471+ `pnmlpnm.list2pnm(out_filename, list_3d, maxcolors, bin)`
472+
473+ where:
474+
475+ - `list_3d`: X * Y * Z list (image) of lists (rows) of lists (pixels) of ints (channels);
476+ - `maxcolors`: number of colors per channel for current image (int);
477+ - `bin`: whether output file is binary (bool);
478+ - `out_filename`: PNM file name.
479+
480+ """
481+ if bin :
482+ list2pnmbin (out_filename , list_3d , maxcolors )
483+ else :
484+ list2pnmascii (out_filename , list_3d , maxcolors )
485+
486+ return None
487+
488+
489+ # End of 'list2pnm' switch function writing any type of PPM/PGM file
490+
491+
465492""" ╔════════════════════╗
466493 ║ Create empty image ║
467494 ╚════════════════════╝ """
@@ -477,6 +504,8 @@ def create_image(X: int, Y: int, Z: int) -> list[list[list[int]]]:
477504 ]
478505
479506 return new_image
507+
508+
480509# End of 'create_image' empty nested 3D list creation
481510
482511
0 commit comments