Normalise PGM Images Safely with pgmnorm and pnmnorm
You will finish with a repeatable way to stretch the contrast of a PGM greyscale image, keep the original file intact, and recognise why pgmnorm and pnmnorm are the same tool on a current Netpbm installation. Allow about ten minutes. You need the Netpbm package, a PGM file to process, and enough disk space for a second copy of the image.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide uses Netpbm 11.5.2 from package version 2:11.05.02-1.1build1. The installed /usr/bin/pgmnorm is a symbolic link to pnmnorm. The old command name remains useful for scripts and notes, but new commands should normally use pnmnorm because it is the maintained name and also accepts PPM input.
1. Confirm the replacement on this machine
Start with read-only checks. They do not need elevated privileges:
$ command -v pgmnorm
/usr/bin/pgmnorm
$ ls -l /usr/bin/pgmnorm
lrwxrwxrwx ... /usr/bin/pgmnorm -> pnmnorm
$ pnmnorm -version
pnmnorm: Using libnetpbm from Netpbm Version: Netpbm 11.5.2
The exact permissions, build information and package revision can differ. The useful checks are that the command exists and that the reported Netpbm version is the one you expect. If pgmnorm is missing but pnmnorm exists, use pnmnorm directly. If neither command exists, install Netpbm through your system's normal package process before continuing.
Checkpoint
You have verified the executable name and version. Nothing has changed on disk.
2. Inspect the source image before changing anything
Choose a source file and inspect its PNM metadata. Replace the example path with a real file. This is still an ordinary, unprivileged operation:
$ INPUT='/path/to/input.pgm'
$ pnmfile "$INPUT"
/path/to/input.pgm: PGM raw, 240 by 160 maxval 255
The dimensions and maximum sample value will vary. PGM files can be plain text or raw binary; pnmnorm reads both and writes the same kind of Netpbm image family. Do not use a path you do not control as the output target. A typo in an output redirection can replace an unrelated file before the program starts.
For a quick file check without a separate metadata utility, the first line of a PGM is normally P2 or P5. Prefer pnmfile when it is available because it also reports dimensions and maxval.
3. Create a normalised copy
Send standard output to a new filename. The source is read, not edited in place:
$ OUTPUT='/path/to/input-normalised.pgm'
$ pnmnorm "$INPUT" > "$OUTPUT"
pnmnorm: remapping 0..255 to 0..255
$ printf 'exit status: %s\n' "$?"
exit status: 0
The diagnostic reports the input brightness range that was selected for the stretch. Its numbers are image-dependent, and a message such as remapping 0..255 to 0..255 means the selected range already spans the full sample range. A zero exit status means the command completed; verify the output as a separate step:
$ pnmfile "$OUTPUT"
/path/to/input-normalised.pgm: PGM raw, 240 by 160 maxval 255
$ test -s "$OUTPUT" && printf '%s\n' 'output exists and is non-empty'
output exists and is non-empty
There is nothing to undo because this workflow creates a new file. If you no longer need the copy, remove that specific output with your normal file-management process after checking its path. The original input remains the recovery copy.
4. Understand the default stretch
By default, pnmnorm maps the darkest 2 percent of pixels to black and the brightest 1 percent to white, then spreads the intermediate values across the available range. This deliberately ignores small groups of extreme pixels, which often gives a more useful contrast adjustment than treating one unusually dark or bright pixel as an endpoint.
The percentages are not guaranteed to be exact because a histogram contains whole pixels. The program chooses thresholds that remap at least the requested proportion. On a small or nearly uniform image, the black and white thresholds can meet. Netpbm adjusts one threshold by one sample value to keep the mapping legal, so a small image may not behave like a large photograph.
Distraction trap: do not interpret the default as an exposure correction or a quality improvement. It changes pixel values. Inspect the result for clipped shadow and highlight detail before replacing a workflow's original asset.
5. Set conservative endpoints when the default is too strong
Use -maxexpand to limit how much the selected input range can be expanded. This example permits at most 50 percent additional expansion:
$ pnmnorm -maxexpand=50 "$INPUT" > "$OUTPUT"
pnmnorm: limiting expansion of 150% to 50%
pnmnorm: remapping 35..85 to 0..100
The diagnostic values are examples, not fixed output. If the source has a narrow middle range, a full stretch can exaggerate noise and banding. A limit gives you a bounded first attempt; it does not guarantee that the image will look good.
For measured control, specify exact values with -bvalue and -wvalue. For example, this maps input values 20 and 230 to black and white:
$ pnmnorm -bvalue=20 -wvalue=230 "$INPUT" > "$OUTPUT"
pnmnorm: remapping 20..230 to 0..255
Use values that fit the image's maxval. A histogram from ppmhist can help you choose meaningful elbows, but inspect the image as well. If you give both a value and a percentage for the same endpoint, current Netpbm uses the choice that produces the least change. Older Netpbm releases differed, so avoid mixing them when a script must behave consistently across old hosts.
6. Use the legacy name only when compatibility calls for it
The installed manual for pgmnorm says that it was replaced in Netpbm 9.25 in March 2002. It also records the key compatibility fact: pnmnorm is backward compatible with pgmnorm and additionally handles PPM images. The safest migration is therefore a small, tested name change:
$ pgmnorm "$INPUT" > "$OUTPUT.old-name-test"
$ pnmnorm "$INPUT" > "$OUTPUT.new-name-test"
$ cmp "$OUTPUT.old-name-test" "$OUTPUT.new-name-test" && printf '%s\n' 'outputs match'
outputs match
Run this comparison only when both output files are disposable and you have enough space. Do not use cmp as a visual quality test; it only checks byte-for-byte equality. Once a script has been updated and tested, prefer pnmnorm and document the Netpbm version if reproducibility matters.
Done means
- You confirmed that Netpbm is installed and recorded its version.
- You used
pnmfileor an equivalent check to identify the input image. - You redirected output to a deliberate new path and left the source unchanged.
- You know the default 2 percent black and 1 percent white thresholds may clip detail.
- You used
-maxexpandor explicit endpoint values when the default stretch was too aggressive. - You use
pnmnormfor new scripts, keepingpgmnormonly for compatibility.