Reduce a PNM Image's Palette Safely with pnmquant
You will finish with a smaller-colour PNM copy, a check that the output is valid, and a clear choice about dithering. The examples use pnmquant from Netpbm 11.5.2, packaged here as netpbm 2:11.05.02-1.1build1.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell, a readable PNM image, and enough free space for a second copy. This guide changes only the output file you create. It does not need elevated privileges, and it does not alter the input.
1. Check the installed command
Confirm that the executable is available and record the package version. These are ordinary read-only checks:
$ command -v pnmquant
/usr/bin/pnmquant
$ dpkg-query -W -f='${Package} ${Version}\n' netpbm
netpbm 2:11.05.02-1.1build1
$ pnmquant -version
pnmcolormap: Using libnetpbm from Netpbm Version: 11.5.2
The version command also prints build details from the helper used by pnmquant. Exact build dates and packaging lines can vary, so the useful checkpoint is that the command resolves and reports the installed Netpbm version.
pnmquant reads a PNM image, chooses a palette containing up to the number of colours you request, remaps the pixels to that palette, and writes a PNM image to standard output. The input may be PBM, PGM or PPM as supported by PNM. The output is not written beside the input unless you redirect it there.
2. Quantise to a new file
Choose a target palette size that suits the image. The number is required and must be positive. For a first run, 256 is a reasonable starting point for a photographic image, while a simple illustration may need far fewer colours:
$ pnmquant 256 /path/to/input.ppm > /path/to/output-256.ppm
pnmquant writes image data to standard output and diagnostic messages to the terminal. A successful run normally produces no progress display in the output file. Check the command status and inspect the result:
$ printf 'exit status: %s\n' "$?"
exit status: 0
$ file /path/to/output-256.ppm
/path/to/output-256.ppm: Netpbm image data, size = 1920 x 1080, rawbits, pixmap
The dimensions in the example are illustrative. Your file command should report the dimensions you expect, and the output should be non-empty. Keep the original until you have opened or converted the result and are satisfied with it.
Checkpoint: you should now have two files. The source is unchanged, and the destination is a separate PNM image with a reduced palette.
3. Do not let redirection destroy a useful file
Shell redirection with > truncates an existing destination before pnmquant starts. That is an irreversible loss unless you have a backup. Use a new name, or make a deliberate backup before replacing an old result:
$ cp --preserve=all /path/to/output.ppm /path/to/output.ppm.bak
$ pnmquant 256 /path/to/input.ppm > /path/to/output.ppm.new
$ file /path/to/output.ppm.new
$ mv /path/to/output.ppm.new /path/to/output.ppm
The final mv is the state-changing step. Run it only after the new file exists and passes your checks. If pnmquant fails, leave the old output in place and investigate the error. To recover a replacement later, copy the backup back over the output, then remove the backup only when you no longer need it:
$ cp --preserve=all /path/to/output.ppm.bak /path/to/output.ppm
Do not use sudo for image conversion. If the directory is not writable, choose a writable working directory or fix ownership and permissions through your normal administration process rather than making a one-off root-owned output.
4. Decide whether to dither
For each input colour, pnmquant must select a nearby colour from the smaller palette. Floyd-Steinberg dithering spreads the resulting error across neighbouring pixels, which can preserve the impression of gradients but can add a visible pattern. Try both modes on a copy when the image contains smooth shading:
$ pnmquant -floyd 32 /path/to/input.ppm > /path/to/output-floyd.ppm
$ pnmquant -nofloyd 32 /path/to/input.ppm > /path/to/output-plain.ppm
-floyd can also be written -fs. -nofloyd can also be written -nofs. The manual describes these as remapping options passed to pnmremap. If you do not choose one, do not assume that a particular visual result is guaranteed by the short command. State the choice explicitly in scripts so a later reader can reproduce it.
The two output files may contain fewer than the requested 32 colours. That is expected. pnmquant builds a palette with pnmcolormap and then remaps each pixel with pnmremap; the palette selection and per-pixel matching are approximate, so some selected palette entries may never be used.
5. Make palette selection repeatable when needed
The palette-selection side has options for how the palette is spread through the image, including -spreadbrightness and its documented aliases. The remapping side has randomised behaviour for some modes. If repeatability matters for generated assets, choose the random behaviour deliberately:
$ pnmquant -nofloyd -norandom 32 /path/to/input.ppm > /path/to/output-repeatable.ppm
-norandom is available in the installed version and is useful when you want the command line to state that randomisation is disabled. -randomseed=n selects a seed when you need a fixed seed instead. A seed is not a visual quality setting, and changing the image, Netpbm version or other options can still change the result.
In the local test, using -norandom without Floyd dithering produced the diagnostic that it had no effect. That is a useful warning rather than a failure: use the option combination that matches the behaviour you are trying to control, and read stderr when testing a batch command.
6. Diagnose failures without guessing
A missing or unreadable input is usually a path or permission problem. Check it without changing anything:
$ ls -l /path/to/input.ppm
$ test -r /path/to/input.ppm && echo readable
readable
If the command reports an invalid colour count, correct the number rather than trying abbreviated options at random. pnmquant accepts option abbreviations only when the prefix is unique, and it permits one or two hyphens for options. Full option names are easier to audit in scripts. For an option with a value, the manual permits either whitespace or an equals sign, for example -randomseed=42.
If file identifies the result as a PNM but the picture looks wrong, check the source and destination dimensions and inspect both images with a trusted viewer or a later Netpbm converter. A successful exit status proves that the program completed; it does not prove that your chosen palette is visually suitable.
For a faster production pipeline, the manual notes that calling pnmcolormap and pnmremap directly avoids pnmquant's convenience-wrapper overhead. Use that split only when you need to reuse a palette or tune the two stages separately. For one image and one output, pnmquant is the simpler interface.
Done means
- The installed pnmquant and Netpbm versions are known.
- The input PNM remains untouched.
- The output file is non-empty, has the expected dimensions, and is recognised as PNM.
- Dithering is either selected explicitly or deliberately left out after comparing copies.
- Any replacement used a temporary destination and a checked backup, so a failed conversion did not destroy the previous output.
- No elevated privileges or persistent system changes were needed.