Replace ppmnorm with pnmnorm Without Losing Image Contrast
You will finish with a tested replacement for the obsolete ppmnorm command, using pnmnorm to stretch contrast in PBM, PGM or PPM images. The replacement writes a Netpbm image to standard output, so it fits existing pipelines while giving you explicit control over the input and output files.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need the Netpbm package, a shell, and a PNM image that you can process without overwriting the original. The checks below use Netpbm 11.5.2 from package version 2:11.05.02-1.1build1. No elevated privileges are needed unless your source image is in a directory that your account cannot read.
1. Confirm why ppmnorm must be replaced
The installed ppmnorm(1) manual is a historical notice, not a separate image-processing interface. It says that ppmnorm was replaced by pnmnorm in Netpbm 9.25, released in March 2002. It also records the compatibility boundary: the replacement accepts the old command's behaviour, except that PBM and PGM input produces PBM and PGM output rather than being treated as PPM output.
Check the installed package and both command paths:
$ dpkg-query -W -f='${Package} ${Version}\n' netpbm
netpbm 2:11.05.02-1.1build1
$ command -v ppmnorm
/usr/bin/ppmnorm
$ command -v pnmnorm
/usr/bin/pnmnorm
Checkpoint
The command name in an old script can still resolve to /usr/bin/ppmnorm, but that does not make it the right interface for new work. Ask the old command for help to see the local compatibility behaviour:
$ ppmnorm --help
ppmnorm: Use 'man ppmnorm' for help.
$ printf 'exit status: %s\n' "$?"
exit status: 1
This is not an image conversion failure. It is a sign that the old entry point is retained for documentation or compatibility. Change scripts to call pnmnorm directly.
2. Make a disposable test image
Do not test a replacement by redirecting over the source file. A shell redirection opens the destination before the program runs, so a typo can destroy the input before an error is reported. Create a small PGM file in /tmp instead:
$ input=$(mktemp /tmp/pnmnorm-input.XXXXXX.pgm)
$ output=$(mktemp /tmp/pnmnorm-output.XXXXXX.pgm)
$ printf 'P2\n4 1\n100\n0 25 75 100\n' > "$input"
$ printf 'input: %s\noutput: %s\n' "$input" "$output"
input: /tmp/pnmnorm-input.A1B2C3.pgm
output: /tmp/pnmnorm-output.D4E5F6.pgm
The four samples deliberately cover the available range. The names printed by mktemp are examples; yours will differ. Keep the two paths separate until you have inspected the result.
3. Run the replacement and inspect its output
Run pnmnorm with the input path and redirect its standard output to the disposable destination:
$ pnmnorm -quiet "$input" > "$output"
$ printf 'exit status: %s\n' "$?"
exit status: 0
$ sed -n '1,4p' "$output"
P5
4 1
100
The output is binary PGM, so the pixel row is not useful to display with a text command. The header is the useful check here: P5 identifies a binary PGM, the dimensions remain 4 1, and the maximum sample value remains 100. A successful exit status means the transformation completed; it does not mean that the image is visually suitable.
For a real file, use a new output path and inspect it with the next tool in your workflow. If you need to preserve a plain-text PGM representation for review, add an explicit format conversion step after this test rather than assuming that the output encoding will match the input encoding.
4. Understand the contrast defaults before changing them
pnmnorm maps the darkest pixels towards black and the brightest towards white, spreading intermediate values between them. By default, it uses the darkest 2 percent and brightest 1 percent as the stretch points. The percentage is a property of the image histogram, not a promise that exactly that many pixels will change. Whole-number brightness levels mean the program can only arrange for at least the requested percentage.
That default can exaggerate noise or clip useful detail in a small or specialised image. Set explicit points when you know the sample range:
$ pnmnorm -quiet -bvalue=0 -wvalue=100 "$input" > "$output"
$ printf 'exit status: %s\n' "$?"
exit status: 0
Use -bpercent and -wpercent when the image population, rather than fixed sample values, should determine the stretch. Use -bsingle or -wsingle when the single darkest or brightest value should define an endpoint. If value and percentage forms are mixed for the same endpoint, the current manual says the choice producing the least change wins. Older Netpbm releases differed, so do not infer this rule for a legacy host without checking its own manual.
5. Protect colour images
For PPM input, the default normalises colour components independently. That can change the hue: an intensely red but dimly green pixel may become more red and less green. Add -keephues when preserving hue is normally more important than reproducing the historical default:
$ pnmnorm -quiet -keephues input.ppm > output.ppm
Hue preservation is not magic. A colour component can already be at its maximum, leaving no room to reach the requested brightness. In that case the result can clip and the final hue may still differ. Review representative bright, dark and saturated areas before replacing a batch job.
6. Diagnose and recover safely
If the command reports an unreadable or malformed input, keep the source unchanged and test the file with the appropriate Netpbm format checker or viewer. If the output is too harsh, adjust -bpercent, -wpercent or use -maxexpand to limit the permitted range expansion. The program reports mappings such as remapping 25..75 to 0..100 unless quiet mode suppresses that diagnostic.
There is no in-place undo operation. Recovery is simple while you keep the original: discard the new output and rerun with different options. Only replace the original after comparing the output and retaining a backup or a reproducible source copy. The example files are disposable, but do not delete a real image merely because a test succeeded.
Done means
- Old scripts call
pnmnorm, notppmnorm. - The source image remains intact and the output path is separate.
- The output format, dimensions and exit status have been checked.
- The default percentile stretch is acceptable, or explicit endpoints are documented.
- PPM workflows use
-keephueswhen independent component normalisation would be unsafe.