Build and Apply a Smaller Netpbm Colour Map with pnmcolormap
pnmcolormap builds a smaller colour palette from a PNM or PAM image, so a photo can be quantised down to sixteen colours. You will finish with that palette, a quantised copy made from it, and checks that show what the command actually produced. The examples use Netpbm 11.5.2 from Debian package netpbm 2:11.05.02-1.1build1.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Check the installed command
- 2. Inspect the input before choosing a palette size
- 3. Generate a map without changing the source
- 4. Apply the map to the image
- 5. Make the output easier to compare
- 6. Choose quantisation behaviour deliberately
- 7. Use all colours only when you mean it
- 8. Handle multi-image input consistently
- Common traps
- You need:
pnmcolormap,pnmremap, and an input image in PNM or PAM format. - No elevated privileges needed. Examples write new files beside the input and never alter the original.
- Keep spare disk space. The program may need several passes through the image.
1. Check the installed command
Record the binary and library version first. This is read-only and should work as an ordinary user:
$ command -v pnmcolormap
/usr/bin/pnmcolormap
$ pnmcolormap --version
pnmcolormap: Using libnetpbm from Netpbm Version: Netpbm 11.5.2
...
The remaining version lines identify the build date and distributor, and vary between machines, so the useful checkpoint is the reported Netpbm version. This guide follows the command installed on the machine where the local manual page is available: older Netpbm releases can differ in the details of their tie handling during quantisation.
2. Inspect the input before choosing a palette size
Use an existing file for the first run, and replace INPUT.ppm with a real path rather than overwriting it with shell redirection:
$ file INPUT.ppm
INPUT.ppm: Netpbm image data, size 1920 x 1080, rawbits, pixmap
$ head -n 3 INPUT.ppm
P6
1920 1080
255
- Any PNM works. PBM, PGM or PPM, plus PAM. The output keeps the input format, so a PPM input produces a PPM map.
- A map is a tiny image. One pixel for each selected colour, unless a layout option adds padding.
- Pick a target that fits the job. A bigger number is not automatically better; 16 or 32 colours is a useful first comparison for a preview.
- Fewer colours is normal. If the source has fewer distinct colours than requested, the result can contain slightly fewer. The required argument is a positive number, or the special value
all.
3. Generate a map without changing the source
Write the map to a new file. This ordinary command reads INPUT.ppm and sends the generated image to standard output:
$ pnmcolormap 32 INPUT.ppm > palette-32.ppm
pnmcolormap: making histogram...
pnmcolormap: Scanning image 0
pnmcolormap: ...
$ head -n 3 palette-32.ppm
P6
32 1
255
The diagnostic lines go to standard error while the PNM data goes to the redirected standard output. The default method is Heckbert's median cut: it separates colours into boxes, repeatedly splits boxes, and picks one representative colour from each box. By default it splits the box containing the most pixels and chooses the centre colour of each box.
Do not treat the width in the header as an image width you chose independently. Without -square, this program normally emits one row with one column per colour: it is a colour map for another Netpbm program, not usually a display-ready swatch.
Checkpoint
Confirm the file exists and has a plausible header:
$ test -s palette-32.ppm && sed -n '1,3p' palette-32.ppm
P6
32 1
255
4. Apply the map to the image
pnmcolormap only creates the palette. Use pnmremap to produce an image whose pixels are selected from that palette:
$ pnmremap -map palette-32.ppm INPUT.ppm > INPUT-32.ppm
$ file INPUT-32.ppm
INPUT-32.ppm: Netpbm image data, size 1920 x 1080, rawbits, pixmap
The input image stays untouched. The remapped image has the same dimensions and format in this example, but its colours come from the map. Inspect it with an image viewer or the tool used by your normal pipeline. If you need the combined operation and do not need to keep the palette, pnmquant performs the map-and-remap workflow for you.
There is no undo command because these examples create new files. To remove a result, check the exact path first, then delete only that generated file:
$ ls -l -- palette-32.ppm INPUT-32.ppm
$ rm -- palette-32.ppm INPUT-32.ppm
Warning
This deletion is irreversible unless you have another copy. Never redirect output back onto INPUT.ppm unless you have deliberately made a backup and checked that the source and destination are different.
5. Make the output easier to compare
Use -sort when you want two generated maps to be easier to compare. It sorts colours by red, then green, then blue intensity:
$ pnmcolormap -sort 32 INPUT.ppm > palette-32-sorted.ppm
Sorting changes the order of pixels in the map, not the target number of colours. It uses insertion sort, so it is a poor performance choice for very large maps: leave it out for routine quantisation unless stable visual comparison matters.
Use -square when a viewer or contact sheet works better with a roughly square map:
$ pnmcolormap -square 32 INPUT.ppm > palette-32-square.ppm
$ head -n 3 palette-32-square.ppm
P6
6 6
255
The square layout can duplicate the final pixel so the pixel count is close to a perfect square. Those duplicates are padding, not extra palette choices. If the target is not a perfect square, count the meaningful colours from the requested value and remember that the final pixel may be repeated.
6. Choose quantisation behaviour deliberately
The defaults are reasonable for a first pass, but they are not neutral:
- -meancolor represents each box with the component-wise mean of its colours.
- -meanpixel weights that mean by how often each colour appears. Leave the representative-colour option unset for the default centre colour.
- -splitpixelct is the default split: the box containing the most pixels.
- -splitcolorct splits the box containing the most distinct colours; the manual describes this as mainly useful for academic purposes.
- -splitspread splits the box with the largest colour spread, which can preserve small details whose colours occur less often.
For the component used to measure spread, -spreadbrightness is the default. -spreadluminosity weights components by their contribution to luminosity, so red, green and blue do not count equally. Change one decision at a time and compare the remapped image, because a palette that looks plausible as a strip may still lose an important detail in the result.
Example
This keeps the default representative colour but favours a wide colour spread and a stable order:
$ pnmcolormap -splitspread -spreadbrightness -sort 32 INPUT.ppm > palette-detail.ppm
$ pnmremap -map palette-detail.ppm INPUT.ppm > INPUT-detail.ppm
These algorithm options were added in Netpbm 10.88, and the installed 11.5.2 version supports them. Repeated runs with the same Netpbm version are repeatable, including equal-value tie handling, but do not assume byte-for-byte equality across older versions or different implementations.
7. Use all colours only when you mean it
The special argument all asks for every colour found in the input and skips quantisation:
$ pnmcolormap all INPUT.ppm > palette-all.ppm
$ head -n 3 palette-all.ppm
P6
N 1
255
Replace N with the width the command reports for your own image. On a photograph the number may be large, so the resulting map may offer little space or processing benefit. Use a numeric target when the point is to reduce the palette. If all is used for an exact colour inventory, compare the map with ppmhist -map as described by the manual, rather than treating it as a compressed image.
8. Handle multi-image input consistently
pnmcolormap can read a multi-image stream and build one map from all images. Every image must have the same format, depth and maxval, although dimensions may differ. This is useful for animation frames or a batch that must share one palette: the same source colour will not be assigned differently merely because it appears in another frame.
Do not concatenate unrelated formats and hope the command will reconcile them. If the stream violates those requirements, stop at the diagnostic and normalise the inputs before trying again. A single-image test is easier to inspect, so make that your checkpoint before building a multi-image pipeline.
Common traps
- Colours must be positive. A message saying the number of colours must be positive means the required argument was zero or negative: use a positive integer or
all. - Fewer entries than requested usually means the input contains fewer distinct colours, not that the redirection failed.
- Empty or invalid output file: check the command's standard error and the input path. Shell redirection creates the destination before the program runs, so a failed command can leave an empty file behind; remove it before retrying and do not mistake its existence for a successful run.
- Poor-looking but valid image: keep the source and compare a few target sizes. Try
-meanpixelfor photographs dominated by common colours, or-splitspreadfor small, infrequent details. Keep the map beside each result so you can reproduce the choice later; quantisation is a trade-off, not a promise that every input colour survives.
Done means
- Version and format checked. You recorded the installed Netpbm version and confirmed the input format.
- Palette generated safely. A palette was written to a new file without overwriting the source.
- Map applied. You checked the map header and applied it with
pnmremap. - Defaults understood. You know the default split is by pixel count and the default representative is the centre colour.
- Sort and square used sparingly.
-sortor-squarewere used only for a clear comparison or layout need. - Originals kept. The original image is intact and generated files can be removed by their exact paths.