Home / Alt manpages / pamtopnm(1)

  • pamtopnm(1)
  • User command
  • linux

Convert PAM Images to PNM Safely with pamtopnm

pamtopnm turns a PAM image back into PBM, PGM or PPM, and which one you get depends on the header, not on what the picture looks like. Give it ten minutes to convert, predict the output format correctly and handle the one failure mode that trips people up.

The examples use Netpbm 11.5.2, from Debian package netpbm 2:11.05.02-1.1build1. You need a shell, an input PAM or PNM file, and enough free space for the output.

  • Unprivileged: these are ordinary user commands.
  • Non-destructive: they read the input and write standard output, the source is never touched.

1. Confirm the installed command

Check the binary and package before you trust behaviour documented for a different Netpbm release:

$ command -v pamtopnm
/usr/bin/pamtopnm
$ dpkg-query -W -f='${Package} ${Version}\n' netpbm
netpbm 2:11.05.02-1.1build1
$ pamtopnm --version
pamtopnm: Using libnetpbm from Netpbm Version: Netpbm 11.5.2

The version command also prints build details on this installation. Those extra lines vary by package build, so treat the package query as your stable checkpoint instead.

2. Convert a PAM file to a named output

Input path last, redirect standard output to a new file:

$ pamtopnm /path/to/input.pam > /path/to/output.pnm
$ file /path/to/output.pnm
/path/to/output.pnm: Netpbm image data, ...

Swap in real paths on both sides, pamtopnm never infers the output filename or appends an extension for you. file is a quick check, but reading the header yourself is the better test:

$ head -n 3 /path/to/output.pnm
P6
2 1
255

These dimensions and maxval are just illustrative. A real PPM header starts P6, PGM starts P5, PBM starts P4. Binary pixel data follows straight after the header, so never open the output in a text editor or run it through a command that reads the whole file as text.

Checkpoint: the shell creates or truncates the destination the moment redirection happens, before pamtopnm has run at all. Use a new destination while testing. If conversion fails, remove the incomplete output with rm -- /path/to/output.pnm and pick a different destination, or restore from your normal backup. Do not reach for sudo to paper over a path or ownership mistake.

3. Predict the output from depth and maxval

pamtopnm chooses the PNM family from the PAM header's depth field, not from the colours actually visible in the pixels:

  • Depth 3 or 4: produces PPM.
  • Depth 1 or 2: produces PBM when MAXVAL is 1, otherwise PGM.

That means an RGB image whose pixels all happen to be grey still comes out as PPM. Just as counterintuitively, a greyscale image made entirely of black and white pixels does not automatically become PBM if its maxval is higher than 1.

Here is a safe in-memory fixture that proves it: a two-pixel RGB PAM stream, converted, with only the header bytes printed:

$ printf 'P7\nWIDTH 2\nHEIGHT 1\nDEPTH 3\nMAXVAL 255\nTUPLTYPE RGB\nENDHDR\n\000\200\377\377\000\000' \
    | pamtopnm | od -An -tc -N 15
   P   6  \n   2       1  \n   2   5   5  \n

That leading P6 confirms the RGB input became binary PPM. Everything after the header is pixel data, so its appearance in an od listing means nothing as text.

4. Handle tuple type failures

Without options, the PAM's tuple type and depth must genuinely describe data that fits PBM, PGM or PPM. A depth-1 image labelled RGB, for instance, gets rejected because RGB needs at least three channels:

$ pamtopnm /path/to/alpha.pam > /tmp/alpha.pnm
pamtopnm: Depth 1 is insufficient for tuple type 'RGB'.  Minimum depth is 3
$ printf 'exit status: %s\n' "$?"
exit status: 1

The path and header here need to point at a matching fixture, something with DEPTH 1 and TUPLTYPE RGB in its header. That diagnostic text is what the installed Netpbm build actually prints. A non-zero exit status means the output is not trustworthy, full stop. Check the PAM header first, and confirm DEPTH, MAXVAL and TUPLTYPE genuinely match the data rather than relabelling it to silence the error.

If you have independently verified that the tuple values already contain the right channels for PNM, -assume bypasses the tuple type check:

$ pamtopnm -assume /path/to/verified-input.pam > /path/to/verified-output.pnm
$ printf 'exit status: %s\n' "$?"
exit status: 0

That flag is an assertion, not a repair. It does not change the depth, rearrange channels or discard alpha, the depth still has to actually conform. If it does not, run a proper Netpbm transformation such as pamchannel first and inspect the resulting header.

Warning: do not reach for -assume on an untrusted or unfamiliar file. You are personally taking responsibility for the channel interpretation, and a command that exits cleanly can still hand you misleading image data.

5. Use pamtopnm in a pipeline

Leave out the input filename and the program reads standard input, which suits a pipeline that has to accept either PAM or PNM:

$ generate-pam | pamtopnm | image-consumer

Netpbm programs that read PAM also happily read PNM as if it were PAM. So if the input is already PBM, PGM or PPM, pamtopnm effectively just copies it straight through to standard output. Keep the pipeline's exit status visible while you debug, and redirect the final output explicitly if the consumer expects a real file.

Done means

  • Version confirmed: pamtopnm --version identified the local Netpbm build.
  • Deliberate output: the result went to an intentional destination and its magic bytes were checked.
  • Format predicted correctly: PBM, PGM or PPM was predicted from depth and maxval, not from how the image looks.
  • Assume used carefully: -assume was treated as a verified data assertion, never a general conversion switch.