-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpypnm.html
87 lines (86 loc) · 9.64 KB
/
pypnm.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<!doctype html>
<html lang='en-US'>
<head>
<link rel='icon' type='image/x-icon' href='favicon.ico'>
<link rev='made' href='mailto:[email protected]'>
<link rel='canonical' href='https://dnyarri.github.io/pypnm.html'/>
<meta charset='utf-8' />
<meta name='author' content='Ilyich the Toad'>
<meta name='description' content='PGM and PPM image files reading and writing in pure Python'>
<meta name='keywords' content='Python, PPM, PGM, PNM, image, Photoshop, GIMP'>
<link rel='stylesheet' href='toc.css' type='text/css'>
<link rel='stylesheet' href='content.css' type='text/css'>
<title>PGM and PPM image files reading and writing in pure Python</title>
</head>
<body>
<div class='leftarea'> <!-- Nav holder begin -->
<object data='toc.html' class='leftbar'></object>
</div>
<div class='content'> <!-- Content block begin -->
<h1 class='darkbg'>PyPNM - Pythonic Portable Any Map I/O</h1>
<h3 class='lightbg'>Introduction</h3>
<div class='lightbg justborder'>
<p><a href='https://netpbm.sourceforge.net/doc/ppm.html'>Portable Pixel Map (PPM)</a> and <a href='https://netpbm.sourceforge.net/doc/pgm.html'>Portable Gray Map (PGM)</a> (particular cases on PNM format group) are simplest file formats for RGB and L images, correspondingly. In theory, you may import ASCII PGM even in Excel, turning Excel into image editor. Unfortunately, this simplicity lead to some consequences:</p>
<ul>
<li>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;</li>
<li>unwillingness of many software developers to provide any good support 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 their own unusual separator scheme nobody else uses. What as to PNM support in Python, say, Pillow... sorry, I promised not to discuss Pillow anywhere ladies and children are allowed to read it.</li>
</ul>
<p>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. P1 and P4 (1 bit) PBM formats are supported only for reading.</p>
<p>You may easily acquire PyPNM module either <a href='https://github.com/Dnyarri/PyPNM' class='git'>from Github</a> or <a href='https://pypi.org/project/PyPNM/' class='git'>from PyPI</a>.</p>
<p><span class='red'><b>Update:</b></span> in ver. 1.13.10.7 last incompatibility with non-standard hand-made files fixed.</p>
</div>
<h3 class='lightbg'>Format compatibility</h3>
<div class='lightbg justborder'>
<table cols='4'>
<thead style="display: table-header-group">
<tr><th scope='col'><p>Image format</p></th><th scope='col'><p>File format</p></th><th scope='col'><p>Read</p></th><th scope='col'><p>Write</p></th></tr>
</thead>
<tbody>
<tr><td><p>16 bits per channel RGB</p></td><td><p>P6 Binary PPM</p></td><td><p>✅</p></td><td><p>✅</p></td></tr>
<tr><td><p>16 bits per channel RGB</p></td><td><p>P3 ASCII PPM</p></td><td><p>✅</p></td><td><p>✅</p></td></tr>
<tr><td><p>8 bits per channel RGB</p></td><td><p>P6 Binary PPM</p></td><td><p>✅</p></td><td><p>✅</p></td></tr>
<tr><td><p>8 bits per channel RGB</p></td><td><p>P3 ASCII PPM</p></td><td><p>✅</p></td><td><p>✅</p></td></tr>
<tr><td><p>16 bits per channel L</p></td><td><p>P5 Binary PGM</p></td><td><p>✅</p></td><td><p>✅</p></td></tr>
<tr><td><p>16 bits per channel L</p></td><td><p>P2 ASCII PGM</p></td><td><p>✅</p></td><td><p>✅</p></td></tr>
<tr><td><p>8 bits per channel L</p></td><td><p>P5 Binary PGM</p></td><td><p>✅</p></td><td><p>✅</p></td></tr>
<tr><td><p>8 bits per channel L</p></td><td><p>P2 ASCII PGM</p></td><td><p>✅</p></td><td><p>✅</p></td></tr>
<tr><td><p>1 bit ink on/off</p></td><td><p>P4 Binary PBM</p></td><td><p>✅</p></td><td><p>❌</p></td></tr>
<tr><td><p>1 bit ink on/off</p></td><td><p>P1 ASCII PBM</p></td><td><p>✅</p></td><td><p>❌</p></td></tr>
</tbody>
</table>
</div>
<h3 class='lightbg'>Image representation</h3>
<div class='lightbg justborder'>
<p>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 (for example, this structure was used for <a href='scalenx.html'>ScaleNx</a>, and worked well). Therefore, it would be convenient to have module read/write image data from/to such a common structure.</p>
<p>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. I understand that this may sound surprising for professional image I/O programmers, but for normal people writing a loop once and for all is expected behaviour.</p>
<p>Also, since this module is supposed to be used for image editing rather than just reading, 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). For the same reason writing 1 bit PBM files is not planned - 1 bit images are next to useless for editing.</p>
</div>
<h3 class='lightbg'>pnmlpnm.py</h3>
<div class='lightbg justborder'>
<p>Module <b>pnmlpnm.py</b> contains 100% pure Python implementation of everything one may need to read/write from/to a variety of PGM and PPM files. I/O functions are written as functions/procedures, as simple as possible, and listed below:</p>
<ul>
<li><b>pnm2list</b> - reading binary or ascii RGB PPM or L PGM file and returning image data as ints and nested list;</li>
<li><b>list2bin</b> - getting image data as ints and nested list and creating binary PPM (P6) or PGM (P5) data structure in memory. Suitable for generating data to display with Tkinter;</li>
<li><b>list2pnm</b> - writing data created with list2bin to file;</li>
<li><b>list2pnmascii</b> - alternative function to write ASCII PPM (P3) or PGM (P2) files;</li>
<li><b>create_image</b> - 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.</li>
</ul>
<p>Detailed functions arguments description is provided 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.</p>
</div>
<h3 class='lightbg'>viewer.py</h3>
<div class='lightbg justborder'>
<img src='pypnm/viewer.png' alt='Test PPM image opened in viewer' class='floatright'>
<p>Program <b>viewer.py</b> is a small illustrative utility: using <i>pnmlpnm</i> package, it reads different flavours of PGM and PPM files, and allows saving them as different types of PGM/PNM, i.e. it can read ascii PPM and write it as binary PPM or vs. Also this program shows images using <i>pnmlpnm</i> and Tkinter. No, there is no mistake: it does not feed PPM files to Tkinter directly. Instead, it uses nested 3D list data loaded using <i>pnmlpnm</i> to generate in-memory bytes object of PPM structure using <span class='pre'>preview_data = pnmlpnm.list2bin(image3D, maxcolors)</span>, and then feeds this in-memory bytes object to Tkinter as <span class='pre'>preview = PhotoImage(data=preview_data)</span> (note using data=, not file=). This way it shows, for example, ascii PPM which Tkinter itself cannot handle.</p>
<p><i>Funfact</i>: Icon in <b>viewer.py</b> is not the icon, but bytes of PPM produced with <b>pnmlpnm.py</b> and then hardcoded into viewer as byte string. It's 2x2 pixels of basic colors, so when rescaling Tkinter turns it into a four-point gradient.</p>
<p>All this means that you may use <i>pnmlpnm</i> and Tkinter to visualize any data that can be represented as greyscale or RGB without huge external packages and writing files on disk; all you need is Tkinter, included into standard CPython distributions, and highly compatible pure Python <i>pnmlpnm.py</i> taking only 14 kbytes.</p>
</div>
<br>
<div class='lightbg justborder'>
<p>Now when you have initial understanding of what <i>pnmlpnm</i> module is and how it may be used to work with PGM and PPM files, it's time to <a href='https://github.com/Dnyarri/PyPNM' class='git'>pull it from Github</a> and start working with images yourself.</p>
</div>
<br>
<p class='right'><span class='lightbg justborder'>...or move back to <a href='https://dnyarri.github.io'>main page</a>.</span></p>
</div>
</div> <!-- Content block end -->
</body>
</html>