Make an Edge-Style PPM Relief with ppmrelief
A client wants that embossed, steel-plate look on a scanned photo, and ppmrelief gets you there. It reads one PPM image and writes a second one with a fixed edge-detection relief baked in, and your original file never gets touched. The examples below use Netpbm 11.5.2, installed here from Debian package netpbm 2:11.05.02-1.1build1; allow about ten minutes if Netpbm is already on the box. The work is unprivileged, you only need read access to the input and write access to wherever the output lands.
The route
Jump straight to the step you need, or tick off Done means at the end.
This is a filter, not a viewer or a general-purpose editor: one PPM in, one PPM out, and there are no ppmrelief-specific tuning options to fiddle with.
1. Confirm the installed version
Run the common Netpbm version query first, so you know which build's quirks you are dealing with:
$ ppmrelief -version 2>&1 | head -n 1
ppmrelief: Using libnetpbm from Netpbm Version: Netpbm 11.5.2
The exact wording differs between builds because the local binary writes it, but the version line tells you which libnetpbm is linked in. The installed command also accepts the long-option spelling, so --version works too if a script prefers two hyphens.
2. Pick a source image and a fresh destination
Put the source at a known path and choose an output name that is not already holding something you need:
$ ls -l /path/to/input.ppm
$ ppmrelief /path/to/input.ppm > /path/to/input-relief.ppm
The positional ppmfile argument is optional: leave it off and ppmrelief reads the PPM stream from standard input instead. Output always goes to standard output, which is why the shell redirect is what actually creates the destination file.
Safety warning
> truncates an existing destination before ppmrelief has even started. Never point it at the only copy of your source, or at a result you might still need. Picked the wrong name and not run yet? Stop and rename. If a failed run left a half-written file behind, remove only that known temporary output after checking its path; the original input is never touched.
3. Prove the filter actually ran
Check the exit status right after the conversion, then look at the file it produced:
$ printf '%s\n' "$?"
0
$ file /path/to/input-relief.ppm
/path/to/input-relief.ppm: Netpbm image data, size = 640 x 480, rawbits, pixmap
Your dimensions will differ. A zero status means the command finished and wrote something, not that the image is the one you meant, so open the result in a trusted viewer or hand it to another tool you already trust.
For a quick, reproducible smoke test, pipe this valid 3 by 3 PPM straight into the filter:
$ printf 'P3\n3 3\n255\n0 0 0 255 0 0 0 255 0\n255 255 255 128 128 128 64 64 64\n10 20 30 40 50 60 70 80 90\n' \
| ppmrelief > /tmp/relief-test.ppm
$ file /tmp/relief-test.ppm
/tmp/relief-test.ppm: Netpbm image data, size = 3 x 3, rawbits, pixmap
ppmrelief accepts PPM in the normal Netpbm stream, plain PPM included, as this test shows. Anything smaller than 3 by 3 gets rejected by this build, because the relief operation needs a local neighbourhood to work with. Treat that error as an input-size problem, not licence to pad or overwrite the source blindly.
4. Understand what the output actually shows
The manual describes the relief as an edge-detection operation: essentially a convolution against this 3 by 3 matrix:
1 0 0
0 0 0
0 0 -1
In practice, it compares separated samples in a local neighbourhood rather than applying anything like a configurable lighting model. Strong changes in that direction show up; a flat area contributes almost nothing. The output stays a colour PPM, but a relief result can look mostly dark or clipped depending on the source, that is a property of the simple maths, not proof the source file is damaged.
Do not confuse ppmrelief with pamshadedrelief: the latter is built for shaded relief from an elevation map, a different job entirely. Reach for ppmrelief only when you want this fixed edge-style transform of a PPM.
5. Switch to plain PPM if something downstream needs it
By default the command writes raw, binary PPM (magic number P6), compact enough for a pipeline, and what file reports as rawbits. If a text-oriented tool or a review process needs plain PPM instead, add -plain:
$ ppmrelief -plain /path/to/input.ppm > /path/to/input-relief-plain.ppm
$ head -n 3 /path/to/input-relief-plain.ppm
P3
640 480
255
Plain PPM is easier to eyeball but usually larger. It changes only how the samples are written, not the relief maths itself. Stick with the raw default for ordinary pipelines unless the next tool specifically wants ASCII.
6. Keep scripted runs quiet without hiding a failure
Netpbm can write informational build messages to standard error. Add -quiet when a wrapper script wants standard error kept clear for its own diagnostics:
if ppmrelief -quiet /path/to/input.ppm > /path/to/input-relief.ppm; then
printf '%s\n' 'relief image written'
else
printf '%s\n' 'ppmrelief failed' >&2
exit 1
fi
-quiet does not make bad input succeed, it only trims chatter. Keep the exit-status check, and log enough context to identify source and destination. Nothing here needs sudo unless your own filesystem permissions demand it; running the filter as root will not fix a malformed PPM.
Common failure traps
- Input will not open. Check the path and permissions with
ls -l /path/to/input.ppm. Do not touch the source while you investigate. - Input is too small. Use an image at least 3 by 3 pixels; smaller input errors out on this installed version by design.
- Output is empty or cut short. Check the command's status and the free space on the destination filesystem, then rerun to a fresh name once the cause is fixed.
- The result looks wrong. Confirm the viewer actually supports PPM and that you opened the right file. Compare dimensions with
file, and keep the source around until you have checked it visually. - A script gets unexpected diagnostics. Use
-quiet, but still test the exit status: redirecting standard error away can hide a real failure.
Done means
- Original intact: the source PPM still exists and was never overwritten.
- Clean exit:
ppmreliefreturned status 0 for the chosen input. - Shape checked:
fileidentifies the destination as a PPM image with the dimensions you expected. - Eyes on it: you checked the relief visually or with the next tool in your pipeline.