netpbm(file-format)

The term "netpbm" actually stands for a set of open-source tools and libraries used for manipulating a set of graphics formats. The set of formats is as follows:

Some has said that they are sometimes collectively called as the "portable anymap format (PNM)".

All 3 formats has a plain-data and a binary-data variant, but the header part of them are always in plain ASCII.

PBM

Header

  • P1 (plain-data) or P4 (binary-data)
  • Whitespace. (spaces, tabs, carriage return or linefeed)
  • The width of the image, decimal, in ASCII.
  • Whitespace.
  • The height of the image, decimal, in ASCII.
  • A single whitespace character, dividing the header and the data part.

Within the header, any (part of) line that starts from a hash sign # to a linefeed is considered a comment and would/should be ignored.

Data

In plain-data (magic number P1) each pixel is represented by a number that is either 0 or 1. Each number should be separated by whitespace. It is not necessary to have a line break at the end of a pixel row.

In binary-data PBM (magic number P4), each pixel should take 1-bit, and each row is padded at the right-end to a multiple of 8-bit, with the value of padded bits being ignored.

In both plain-data and binary-data variants the data is stored in the order of from top to bottom, and within each row the value for each pixel is stored in the order of from left to right.

Example

An example of P1 PBM can be seen as follow:

P1
# efg.pbm
18 7
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 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0
0 1 1 1 0 0 0 1 1 1 0 0 0 1 0 1 1 0
0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0
0 1 1 1 1 0 0 1 0 0 0 0 0 1 1 1 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

In P4 PBM this could be:

P4
# efg.pbm
18 7
\x00\x00\x00\x79\xe7\x80\x41\x04\x00\x71\xc5\x80\x41\x04\x80\x79\x07\x80\x00\x00\x00
00000000: 5034 0a23 2065 6667 2e70 626d 0a31 3820  P4.# efg.pbm.18 
00000010: 370a 0000 0079 e780 4104 0071 c580 4104  7....y..A..q..A.
00000020: 8079 0780 0000 00                        .y.....

PGM

Header

  • P2 (plain-data) or P5 (binary-data)
  • Whitespace. (spaces, tabs, carriage return or linefeed)
  • The width of the image, decimal, in ASCII.
  • Whitespace.
  • The height of the image, decimal, in ASCII.
  • Whitespace.
  • The maximum gray value, decimal, in ASCII. (referred to as maxval below.) This is the value that denotes white. Must be smaller than 65536 (i.e. the maximum allowed value for this field would be 65535) and must be bigger than zero.
    • Apparently the ability to specify value greater than the 8-bit limit is not supported by the "old" specification of PGM. Some software may reject cases where maxval is bigger or equal than 256 and may not render those cases properly for one reason or another; one is adviced to not depend on the support of 16-bit and should always opt for 8-bit maxval if possible.
  • A single whitespace character, dividing the header and the data part.

Within the header, any (part of) line that starts from a hash sign # to a linefeed is considered a comment and would/should be ignored.

Data

In plain-data (magic number P1) each pixel is represented by a number that is between 0 and maxval. Each number should be separated by whitespace. It is not necessary to have a line break at the end of a pixel row.

In binary-data PBM (magic number P4), each pixel would take up 1 byte (if maxval is less than 256) or 2 bytes (if maxval is equal or bigger than 256). When each pixel takes up 2 bytes, it's stored in big-endian. Note that this is the "raw" value instead of scaled to [0, 255] or [0, 65535]; e.g., if the maxval of a P5 PGM is 11, there should never be a byte that is bigger than 11 in its data part.

In both plain-data and binary-data variants the data is stored in the order of from top to bottom, and within each row the value for each pixel is stored in the order of from left to right.

According to the netpbm website, PGM contains "level" after gamma correction with BT.709 gamma function, which is defined as:

\[ f(x) = \begin{cases} 4.500 x & (x < 0.018) \\ x & (0.018 \le x < 0.081) \\ 1.099 x^{0.45} - 0.099 & (x \ge 0.081) \end{cases} \]

f(x) = 4.500 * x    (x < 0.018)
     | x    (0.018 <= x < 0.081)
     | 1.099 * x ^ 0.45 - 0.099    (x >= 0.081)

Example

An example of P2 PGM can be seen as follow:

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

In P5 PGM this could be:

00000000: 5035 0a23 2066 6565 702e 7067 6d0a 3234  P5.# feep.pgm.24
00000010: 2037 0a31 350a 0000 0000 0000 0000 0000   7.15...........
00000020: 0000 0000 0000 0000 0000 0000 0000 0003  ................
00000030: 0303 0300 0007 0707 0700 000b 0b0b 0b00  ................
00000040: 000f 0f0f 0f00 0003 0000 0000 0007 0000  ................
00000050: 0000 000b 0000 0000 000f 0000 0f00 0003  ................
00000060: 0303 0000 0007 0707 0000 000b 0b0b 0000  ................
00000070: 000f 0f0f 0f00 0003 0000 0000 0007 0000  ................
00000080: 0000 000b 0000 0000 000f 0000 0000 0003  ................
00000090: 0000 0000 0007 0707 0700 000b 0b0b 0b00  ................
000000a0: 000f 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000       ..............

The same image can also be expressed as follows; notice that the maxval has increased to 4095 and values for each pixel is accordingly scaled. Sometimes some differences in color will not show if your image displaying software treats all PGM as 8-bit grayscale, which only has 256 different meaningful levels.

00000000: 5035 0a23 2066 6565 702e 7067 6d0a 3234  P5.# feep.pgm.24
00000010: 2037 0a34 3039 350a 0000 0000 0000 0000   7.4095.........
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 0000 0000 0000 0000 0000 0333 0333 0333  ...........3.3.3
00000050: 0333 0000 0000 0777 0777 0777 0777 0000  .3.....w.w.w.w..
00000060: 0000 0bbb 0bbb 0bbb 0bbb 0000 0000 0fff  ................
00000070: 0fff 0fff 0fff 0000 0000 0333 0000 0000  ...........3....
00000080: 0000 0000 0000 0777 0000 0000 0000 0000  .......w........
00000090: 0000 0bbb 0000 0000 0000 0000 0000 0fff  ................
000000a0: 0000 0000 0fff 0000 0000 0333 0333 0333  ...........3.3.3
000000b0: 0000 0000 0000 0777 0777 0777 0000 0000  .......w.w.w....
000000c0: 0000 0bbb 0bbb 0bbb 0000 0000 0000 0fff  ................
000000d0: 0fff 0fff 0f64 0000 0000 0333 0000 0000  .....d.....3....
000000e0: 0000 0000 0000 0777 0000 0000 0000 0000  .......w........
000000f0: 0000 0bbb 0000 0000 0000 0000 0000 0fff  ................
00000100: 0000 0000 0000 0000 0000 0333 0000 0000  ...........3....
00000110: 0000 0000 0000 0777 0777 0777 0777 0000  .......w.w.w.w..
00000120: 0000 0bbb 0bbb 0bbb 0bbb 0000 0000 0fff  ................
00000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000140: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000150: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000160: 0000 0000 0000 0000                      ........

PPM

Header

  • P3 (plain-data) or P6 (binary-data)
  • Whitespace. (spaces, tabs, carriage return or linefeed)
  • The width of the image, decimal, in ASCII.
  • Whitespace.
  • The height of the image, decimal, in ASCII.
  • Whitespace.
  • The maximum gray value, decimal, in ASCII. (referred to as maxval below.) This is the value that denotes the maximum value in each of the RGB channel; in PPM, all of the three channels share the same maxval. Must be smaller than 65536 (i.e. the maximum allowed value for this field would be 65535) and must be bigger than zero.
    • Apparently the ability to specify value greater than the 8-bit limit is not supported by the "old" specification of PGM. Some software may reject cases where maxval is bigger or equal than 256 and may not render those cases properly for one reason or another; one is adviced to not depend on the support of 16-bit and should always opt for 8-bit maxval if possible.
  • A single whitespace character, dividing the header and the data part.

Within the header, any (part of) line that starts from a hash sign # to a linefeed is considered a comment and would/should be ignored.

Data

In plain-data (magic number P3) each pixel is represented by three number that is between 0 and maxval. These three numbers denote the value of the Red, Green and Blue channel respectively in that order. Each number should be separated by whitespace. It is not necessary to have a line break at the end of a pixel row.

In binary-data PBM (magic number P6), each pixel would take up 3 byte (if maxval is less than 256) or 6 bytes (if maxval is equal or bigger than 256), consisting of 3 different numbers that can be 1 byte (if maxval is less than 256) or 2 bytes (if maxval is equal or bigger than 256); these 3 different numbers denote the value of the Red, Green and Blue channel respectively in that order. When each channel value takes up 2 bytes, it's stored in big-endian. Note that this is the "raw" value instead of scaled to [0, 255] or [0, 65535]; e.g., if the maxval of a P5 PGM is 11, there should never be a byte that is bigger than 11 in its data part.

In both plain-data and binary-data variants the data is stored in the order of from top to bottom, and within each row the value for each pixel is stored in the order of from left to right.

According to the netpbm website, just like PGM, PPM contains "level" after gamma correction with BT.709 gamma function, which is defined as:

\[ f(x) = \begin{cases} 4.500 x & (x < 0.018) \\ x & (0.018 \le x < 0.081) \\ 1.099 x^{0.45} - 0.099 & (x \ge 0.081) \end{cases} \]

f(x) = 4.500 * x    (x < 0.018)
     | x    (0.018 <= x < 0.081)
     | 1.099 * x ^ 0.45 - 0.099    (x >= 0.081)

Example

A small PPM image can be seen as follows:

P3
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

The equivalent of this image using binary-data can be seen as follows:

00000000: 5036 0a34 0a34 0a31 350a 0000 0000 0000  P6.4.4.15.......
00000010: 0000 000f 000f 0000 0000 0f07 0000 0000  ................
00000020: 0000 0000 0000 0000 000f 0700 0000 0f00  ................
00000030: 0f00 0000 0000 0000 0000                 ..........

2025.3.27

Back