Deskew a Scanned PGM with pamtilt and pnmrotate
pamtilt measures the skew angle in a scanned PGM so you can straighten it with pnmrotate, instead of eyeballing a rotation value and hoping. Allow about fifteen minutes. You need the Netpbm package, a readable scan, and enough free space for a second image.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide describes Netpbm 11.5.2, installed here as package version 2:11.05.02-1.1build1. The pamtilt manual page was written for an older Netpbm release, so check the installed command before putting the workflow into a batch job.
1. Check the installed command and input
Confirm the executable and its package are the ones you expect. These are ordinary read-only commands and need no elevated privileges:
$ command -v pamtilt
/usr/bin/pamtilt
$ pamtilt --help
pamtilt: Use 'man pamtilt' for help.
$ pamtilt --version
pamtilt: Using libnetpbm from Netpbm Version: Netpbm 11.5.2
$ dpkg-query -W -f='${Package} ${Version}\n' netpbm
netpbm 2:11.05.02-1.1build1
The --help response just points at the manual rather than listing options. The documented options use one leading hyphen, for example -angle=10 and -fast. Use the exact syntax from man pamtilt when writing scripts.
Checkpoint
Make sure the source image is readable and is a PGM, PBM, or a compatible PNM or PAM image whose first plane holds the document:
$ test -r /path/to/crooked.pgm && echo readable
readable
$ file /path/to/crooked.pgm
/path/to/crooked.pgm: Netpbm image data, size 2480 x 3508, graymap
Your file description may differ. If it cannot identify the image, stop and inspect the file before passing it to pamtilt. Do not use sudo to paper over a wrong path or an invalid image format.
2. Measure the document tilt
Run pamtilt with the image path as its optional final argument:
$ pamtilt /path/to/crooked.pgm
0.00
The output is one floating-point number in degrees. The example above comes from a synthetic image with horizontal lines, not a promise about your scan. Save the value in a shell variable if you plan to use it right away:
$ input=/path/to/crooked.pgm
$ angle=$(pamtilt "$input")
$ printf 'measured angle: %s degrees\n' "$angle"
measured angle: 0.00 degrees
There is no persistent state to undo here. pamtilt only reads the input and writes its result to standard output. A non-zero exit status or an error message means you do not have a trustworthy angle yet.
3. Treat an uncertain result as a checkpoint
pamtilt is designed for scanned text with strong horizontal lines, running three searches at increasingly fine increments; it is not a general photo-orientation tool. The manual says it may report the special value 00.00 when it lacks confidence. On this installed build, the same kind of result prints as 0.00.
Do not silently rotate a large archive when the result is uncertain. Inspect the scan first and compare the measured value against what you can actually see. For a useful diagnostic, ask for the measurements on standard error:
$ pamtilt -verbose "$input" > /tmp/pamtilt-angle.txt 2> /tmp/pamtilt-measurements.txt
$ cat /tmp/pamtilt-angle.txt
0.00
$ sed -n '1,5p' /tmp/pamtilt-measurements.txt
21 angles from -10.00 to 10.00 by 1.00: best = 0.00, S:N = 10.36
-10.00: 2418.29 00000
The diagnostic above is intentionally short; the number of tested angles and the measurements depend on the image and options used. The files under /tmp are disposable working data. If a result does not match the page, keep the original scan and investigate rather than forcing a rotation.
4. Widen or tune the search only when the scan needs it
The default maximum angle is 10.0 degrees. If you know the page was scanned outside that range, set a larger maximum explicitly:
$ angle=$(pamtilt -angle=15 "$input")
$ printf 'measured angle: %s degrees\n' "$angle"
- The search still depends on visible document structure.
-fastskips the third iteration, trading accuracy for speed, so leave it out for a first measurement. - -quality=q raises the signal-to-noise threshold. A larger value rejects dubious results instead of producing a plausible-looking angle.
- Leave the sampling controls alone by default.
-hstep,-vstep,-dstepand-astepaffect run time, memory, sampling or the first angle increment; only touch their defaults once you have measured a repeatable problem and can verify the result on representative scans.
5. Rotate to a new file
Once the angle looks sensible, pass it to pnmrotate. The command below writes a new PNM file and leaves the source untouched:
$ output=/path/to/straight.pgm
$ pnmrotate -background white "$angle" "$input" > "$output"
$ file "$output"
/path/to/straight.pgm: Netpbm image data, size 2480 x 3508, graymap
pnmrotate measures its angle counter-clockwise and writes the rotated image to standard output. Its output rectangle can end up larger than the input, and the background colour is chosen automatically unless you supply one; -background white here makes the intended page background explicit.
Warning
Shell redirection truncates an existing destination before pnmrotate starts. Do not point this command at the only copy of the scan. To replace an existing derived image, use a temporary name, verify it, then move it into place:
$ temporary=/path/to/straight.pgm.new
$ pnmrotate -background white "$angle" "$input" > "$temporary"
$ file "$temporary"
$ mv -- "$temporary" "$output"
If conversion fails, remove only the incomplete .new file after checking it really is the temporary output. The original input and previous output remain available. That final mv is the state-changing step; it normally needs no elevated privileges when the directory is yours.
Common traps
- A photo or weak horizontal structure produces an unhelpful angle. Crop or inspect a representative document, but never assume a numerical result is correct just because it printed.
- A result outside the default range needs an explicit
-anglevalue. The default is never inferred from the filename or scanner metadata. - Reducing -hstep or -vstep increases work and memory use. Increasing those sampling steps speeds a run up while raising the risk of a wrong result.
- If pnmrotate cannot open the image, check the path and permissions first. If it cannot write the destination, choose a writable directory rather than running the whole conversion as root.
Done means
- pamtilt is the expected Netpbm 11.5.2 executable and the source image is readable.
- You captured one floating-point angle and checked it against the document.
- You used -verbose or adjusted -angle only when the scan actually justified it.
- pnmrotate wrote a verified new image with the intended background.
- The original scan remains untouched, and any replacement was staged through a temporary file.