Create reversible horizontal difference images with pamtohdiff
You will convert a PAM or PNM image into a PAM horizontal difference image, check the result, and recover the original with hdifftopam. This is useful when repeated rows should compress well or when you need Netpbm's hdiff representation for another processing step. Allow about 10 minutes if Netpbm is already installed. The examples read and write files as your normal user, so they do not need sudo.
The route
Jump straight to the step you need, or tick off Done means at the end.
1. Check the installed Netpbm tools
This guide is verified against the installed netpbm package, version 2:11.05.02-1.1build1, which provides Netpbm 11.5.2. The local pamtohdiff(1) manual is dated 15 April 2002, so the commands below describe this installed implementation. Check your own binary before copying the result into a portable script:
$ command -v pamtohdiff
/usr/bin/pamtohdiff
$ pamtohdiff -version 2>&1 | head -2
pamtohdiff: Using libnetpbm from Netpbm Version: Netpbm 11.5.2
pamtohdiff: Built from source dated 2024-03-31 09:09:47
The input can be supplied as the optional positional pamfile argument, or read from standard input. The output is written to standard output. That separation is useful, but it also means an accidental redirection can overwrite a file before conversion has succeeded.
2. Convert a PAM image to hdiff
Choose an output name that does not already contain useful data. The converter does not alter the input file:
$ pamtohdiff /path/to/source.pam > /path/to/source.hdiff.pam
For a pipeline, omit the input path:
$ cat /path/to/source.pam | pamtohdiff > /path/to/source.hdiff.pam
Verify the new file as PAM and check that its tuple type is hdiff:
$ pamfile /path/to/source.hdiff.pam
/path/to/source.hdiff.pam: PAM, 1920 by 1080 by 3 maxval 255
Tuple type: hdiff
Your dimensions, depth and maxval will differ. The useful checks are that the file is non-empty, the geometry matches the source, and the tuple type is hdiff.
3. Understand what changed
For each sample, pamtohdiff compares the current row with the row directly above it. A run of unchanged rows therefore produces many zero differences, which is the property that can make the result compress better. The first row still has to be represented, so do not treat the file as a general-purpose compressed image format.
PAM samples are unsigned, while a difference can be negative. Netpbm stores the difference modulo maxval + 1, preserving enough context for the inverse operation. It also adds a half-maxval visual bias before the modulus operation. As a result, the output can be displayed as a PNM-style image: zero difference appears around the middle of the intensity range, negative differences appear darker and positive differences lighter when the changes stay within the expected range.
This visual interpretation is a diagnostic aid, not a reason to edit the hdiff samples by hand. If you need the visual mapping to remain useful for larger differences, the manual recommends applying ppmdim 50 to the original and doubling maxval first with pamdepth. Those transformations change the image data, so keep a copy of the original.
4. Recover the original image
Use hdifftopam to reverse the conversion. Without options it emits a PAM image:
$ hdifftopam /path/to/source.hdiff.pam > /path/to/recovered.pam
$ pamfile /path/to/recovered.pam
/path/to/recovered.pam: PAM, 1920 by 1080 by 3 maxval 255
Tuple type: unhdiff
The unhdiff tuple type identifies the recovered PAM. If you need a PGM or PPM instead, use the documented -pnm option. It only succeeds when the depth is 1 or 3:
$ hdifftopam -pnm /path/to/source.hdiff.pam > /path/to/recovered.pnm
$ pamfile /path/to/recovered.pnm
/path/to/recovered.pnm: PPM, 1920 by 1080 255
For a stronger check, compare the pixel content with a trusted image tool or convert both files to the same canonical format before comparing. The PAM headers are allowed to differ: the recovered file normally has tuple type unhdiff, not the source header's original tuple name.
5. Avoid losing an existing output
Shell redirection with > truncates its destination before pamtohdiff starts. For an existing result, write a temporary file in the same directory and replace the destination only after checking it:
$ pamtohdiff /path/to/source.pam > /path/to/source.hdiff.pam.new
$ pamfile /path/to/source.hdiff.pam.new
$ mv /path/to/source.hdiff.pam.new /path/to/source.hdiff.pam
The mv command changes the destination and is normally irreversible if the old file has no backup. Stop before it if pamfile reports an error. If the conversion fails, remove only the incomplete .new file and leave the old result in place:
$ rm /path/to/source.hdiff.pam.new
Run that removal only when the path is exactly the temporary output you chose. Keep the source image until the reverse conversion has been checked.
6. Diagnose the common traps
- An end-of-file error usually means the input is truncated or is not a valid PAM or PNM stream. Check it with
pamfile /path/to/source.pambefore changing anything. - A file with the wrong dimensions is not repaired by renaming it. Recheck the source and its header, then rerun the conversion.
-verboseis accepted by this implementation but currently has no effect. Do not rely on it for progress or metadata.- Do not use
-pnmwithhdifftopamfor an hdiff image whose depth is not 1 or 3; the documented command will fail.
Done means
pamtohdiffaccepted a readable PAM or PNM input without elevated privileges.pamfilereports the output tuple type ashdiffand preserves the expected geometry.- The original file remains untouched and any replacement was checked before
mv. hdifftopamrecovered a PAM, or a PGM/PPM with-pnmwhen the depth permits it.