I have used ImageMagick quite often for a variety of projects and I often noted the many “pnm” utilities that come with it. For example, pnmrotate, pnmscale, pnmcat, etc. I never really paid attention to what these formats are, however. PNM provides a standard format for uncompressed bitmaps (PBM), grayscale (PGM) and color images (PPM) that is very easy to parse and write. These are perfect for manipulating with Perl, C, or any other language where you want to do a low-level hack.
There are 6 types of these images and they are all identified by the first byte of the file. This byte provides a “magic number” and is decoded as:
- P1: ASCII bitmap
- P2: ASCII grayscale
- P3: ASCII color
- P4: Binary bitmap
- P5: Binary grayscale
- P6: Binary color
After the magic number, there can be an arbitrary amount of whitespace followed by a newline. Then, the next line is an optional comment line if it begins with a “#”. After the comment terminates with a newline, there is again an arbitrary amount of white space followed by a newline. Now, the size of the image is given in pixels using standard ASCII. For example, “640 480″ would be a 640 wide by 480 tall image. The header information can be summarized like this example:
P3
# feep.ppm
4 4
What follows next depends on the image format. Each one is presented briefly.
For P1/P4 bitmap formats, the rasterized bitmap data is in the file. For P1 types, this data is just shown as ASCII “0″ and “1″s with optional whitespace. For P4 types, the data is packed (to the left) big-endian binary. The binary data is padded with don’t care values to give a full line. It is important, however, that NO LINE CAN EXTEND MORE THAN 70 BYTES/CHARACTERS. Some tools ignore this, but others don’t. ASCII example:
P1
# feep.pbm
24 7
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0
0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0
0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 1 0
0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0
0 1 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
For P2/P5 grayscale formats, we first get a single integer that describes the maximum range of the gray. It can be 0 to 65536. 255 would be white while 0 is black.Following this, the data is again encoding as either ASCII numbers or 1-2 bytes that are big enough to hold the maximum value. ASCII example:
P2
# feep.pgm
24 7
15
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 3 3 3 3 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 15 0
0 3 3 3 0 0 0 7 7 7 0 0 0 11 11 11 0 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 0 0
0 3 0 0 0 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
For P3/P6 color images, the format is the same as the grayscale images except that there are now 3, instead of 1, numbers to represent red, blue and green. ASCII example:
P3
# feep.ppm
4 4
15
0 0 0 0 0 0 0 0 0 15 0 15
0 0 0 0 15 7 0 0 0 0 0 0
0 0 0 0 0 0 0 15 7 0 0 0
15 0 15 0 0 0 0 0 0 0 0 0