Build and Inspect Raw PBM Images with Netpbm
You will create a tiny monochrome image, inspect its raw PBM representation, and convert it to the more readable Plain PBM form. The examples use Netpbm 11.5.2, installed here as package version 2:11.05.02-1.1build1. Allow about ten minutes. You need a shell, the netpbm package, and a writable working directory.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide changes only files in a temporary directory. It does not need sudo. PBM is usually an interchange format for a pipe between Netpbm programs, not a space-efficient format for an image archive.
1. Create a known Plain PBM input
Start with Plain PBM because its pixels are visible as ASCII 0 and 1 characters. The image below is eight pixels wide and two pixels high. A 1 is black and a 0 is white.
demo_dir=$(mktemp -d)
printf '%s\n' \
'P1' \
'# two-row test image' \
'8 2' \
'1 0 0 0 0 0 0 1' \
'1 1 1 1 1 1 1 1' > "$demo_dir/example.pbm"
file "$demo_dir/example.pbm"
Expected output identifies an 8 by 2 bitmap and describes it as ASCII text. The comment is optional, but it demonstrates that a PBM comment begins with # and ends at the next carriage return or newline.
/tmp/tmp.XXXX/example.pbm: Netpbm image data, size = 8 x 2, bitmap two-row test image, ASCII text
The temporary directory name will differ. Keep the value of demo_dir in the current shell; later commands use it. Checkpoint: test -s "$demo_dir/example.pbm" && echo ready should print ready.
2. Read the PBM header correctly
Every PBM image begins with a magic number. Plain PBM uses P1; raw PBM uses P4. After that come whitespace, the decimal width, more whitespace, the decimal height, one whitespace character, and the raster.
For this file, 8 2 means eight columns and two rows. The pixels are read from left to right, then top to bottom. Do not interpret the dimensions as a maximum size or as a request to resize an image.
PBM accepts ASCII whitespace: spaces, tabs, carriage returns, line feeds, vertical tabs and form feeds. Comments can occur where whitespace is expected. A comment can even interrupt what appears to be a token, so a parser should follow the format rather than assume that each header field occupies a complete line.
3. Convert the image to raw PBM
Netpbm normally generates raw PBM. Use pamtopnm without -plain to turn the test input into that compact form. Write to a new filename so the original remains available for comparison.
$ pamtopnm "$demo_dir/example.pbm" > "$demo_dir/example-raw.pbm"
$ file "$demo_dir/example-raw.pbm"
/tmp/tmp.XXXX/example-raw.pbm: Netpbm image data, size = 8 x 2, rawbits, bitmap
Verify the beginning of the file before treating it as binary data:
$ head -c 9 "$demo_dir/example-raw.pbm" | od -An -tx1c
50 34 0a 38 20 32 0a 81 ff
P 4 \n 8 2 \n 201 377
The first six bytes are the header P4\n8 2\n. The two remaining bytes are the two raster rows. Each row contains eight pixels packed into one byte. The first row is 10000001, so its byte is hexadecimal 81. The second row is eight black pixels, so its byte is hexadecimal ff.
For widths that are not multiples of eight, each row still occupies enough complete bytes for its width. The unused bits at the end of the final byte are padding and have no defined pixel meaning. Do not count those padding bits as extra columns.
4. Convert raw PBM back to readable Plain PBM
Use the compatibility command pnmtoplainpnm when you want a text representation for inspection or a very tolerant interchange. In current Netpbm it is retained for backwards compatibility and delegates to the common -plain behaviour.
$ pnmtoplainpnm "$demo_dir/example-raw.pbm"
P1
8 2
10000001
11111111
There is exactly one image in Plain PBM. Its raster may contain whitespace between pixels, although Netpbm commonly prints a compact row. Plain PBM has no packed row padding, so the two rows above contain exactly 16 pixel values.
Checkpoint: compare the converted rows with the original input. They should describe the same image. If the dimensions differ, stop and inspect which program produced the file before passing it to another tool.
5. Preview the bitmap in a terminal
pbmtoascii reads a PBM image and prints a rough ASCII graphic. Its default -1x2 mode represents one pixel across by two pixels down, which is useful for a small test image:
$ pbmtoascii "$demo_dir/example-raw.pbm"
MooooooM
The exact character choices are part of pbmtoascii, not the PBM file format. The useful check is that the output has the expected shape. Use -2x4 for a denser preview of a larger bitmap, accepting that it hides individual pixels.
This preview is not an image conversion. It is terminal output, so do not redirect it over a PBM file and expect to recover the raster. Keep the raw file when another Netpbm program needs the actual image.
6. Handle binary output safely
Raw PBM contains arbitrary byte values after its header. Redirect it to a file or pipe it directly to a program that understands PBM. Do not paste the raster into a text editor, copy it through a tool that changes line endings, or use a text filter that treats byte values as characters.
Shell redirection with > truncates its destination before the command starts. Use a new output path while testing:
pamtopnm "$demo_dir/example.pbm" > "$demo_dir/example-raw.pbm.new"
if file "$demo_dir/example-raw.pbm.new" | grep -q 'rawbits'; then
mv -- "$demo_dir/example-raw.pbm.new" "$demo_dir/example-raw.pbm"
else
printf '%s\n' 'conversion did not produce raw PBM' >&2
rm -- "$demo_dir/example-raw.pbm.new"
exit 1
fi
The mv replaces the old raw file only after the check succeeds. If a command fails, the original remains. The cleanup rm targets only the newly created temporary file; review paths before adapting this pattern to a permanent directory.
7. Diagnose the common mistakes
- The header says P1 when you expected P4. That is Plain PBM. It is valid, but it is larger and contains one image only. Use a raw-producing Netpbm command when packed rows matter.
- The preview looks shifted. Check the width first. A wrong width changes where each row ends, and a wrong height changes how many raster rows are read.
- A file contains extra data after the image. Raw PBM permits a sequence of images with no separators. Older programs may read only the first image, so use one image when compatibility with older consumers matters.
- A file is unreadable after editing. Restore the original or regenerate it. Raw PBM is not a text format once the raster begins.
There is no required .pbm filename suffix, although it is the normal convention. The conventional media type is image/x-portable-bitmap; it is not an IANA-registered PBM type. If a system uses the broader PNM type, confirm that choice with the receiving application rather than assuming that MIME metadata changes the file.
Done means
- The test image has a valid
P1header, width, height and raster. - The raw copy begins with
P4and has packed rows with the expected dimensions. - Plain conversion reproduces the same pixels without adding columns for row padding.
- A terminal preview was used only as a visual check, not as a replacement for the PBM file.
- Existing output was protected by writing to a new temporary name before replacement.