Home / Alt manpages / pamseq(1)

  • pamseq(1)
  • User command
  • linux

Build Reproducible PAM Sequence Images with pamseq

Need a test image with exactly known pixel values instead of a photo or a random noise generator? pamseq builds a one-row PAM image from a numerical sequence, a multi-plane tuple grid, or a small colour map, byte for byte the same every time. Allow about fifteen minutes.

The examples use pamseq from Netpbm 11.5.2, supplied here by package netpbm 2:11.05.02-1.1build1. You need a shell, pamseq, and optionally pamrestack or pamfile from Netpbm. The commands write ordinary files in your working directory and need no elevated privileges. Do not use sudo unless your chosen destination is deliberately protected and you've checked the ownership first.

1. Check the installed command

Confirm which executable will run and record the Netpbm version. These are read-only checks:

$ command -v pamseq
/usr/bin/pamseq
$ pamseq --version
pamseq: Using libnetpbm from Netpbm Version: Netpbm 11.5.2
pamseq: Built from source dated 2024-03-31 09:09:47
$ dpkg-query -W -f='${Package} ${Version}\n' netpbm
netpbm 2:11.05.02-1.1build1

The version matters: -min, -max and -step only arrived in Netpbm 10.99. If another machine has an older Netpbm, check its local manual before copying these examples into a script.

Checkpoint

If command -v finds nothing, stop here. Install Netpbm through your normal package-management process and repeat the check. There's no reason to run the generator as root, ever.

2. Generate a simple sequence

The required arguments are depth and maxval. Depth is the number of samples in each tuple. With depth 1 and maxval 4, pamseq creates one row containing the five values from 0 through 4:

$ pamseq 1 4 > sequence.pam
$ pamfile sequence.pam
sequence.pam: PAM, 5 by 1 by 1 maxval 4
$ od -An -t u1 -j 44 sequence.pam
   0   1   2   3   4

The output is a binary PAM file, so don't use cat as a visual check: the header is text, but the sample data may contain control bytes. pamfile checks the image structure and od makes the small sample values visible. Header length can vary when attributes are present, so the offset in this deliberately simple example is not a general parsing rule.

Checkpoint

The width is maxval minus the starting value plus one when the default range is used. For pamseq 1 4, that's five pixels. Height is always one until another Netpbm program reshapes the stream.

3. Set a range and step explicitly

Use -min, -max and -step when the default 0-to-maxval ramp isn't the sequence you need. For depth 1, each option takes one whole-number value:

$ pamseq 1 255 -min=4 -max=8 -step=2 > even-range.pam
$ pamfile even-range.pam
even-range.pam: PAM, 3 by 1 by 1 maxval 255
$ pamtopnm -assume even-range.pam | od -An -t u1
  80  53  10  51  32  49  10  50  53  53  10   4   6   8
  • The endpoint is included when the step lands on it exactly.
  • A step that doesn't land on the maximum produces values only up to the last value in the sequence; don't assume -max will always appear in the output.
  • Every step must be positive and no greater than maxval.
  • Every maximum must be at least its matching minimum.

pamseq accepts either one or two leading hyphens, whitespace or an equals sign before a value, and unique abbreviations. Full option names are shown here because they're easier to audit, so keep them in scripts.

4. Generate tuples across several planes

For depth greater than one, provide one comma-separated number per plane. pamseq varies the highest-numbered plane first, moving towards lower-numbered planes. With depth 2, the second sample changes fastest:

$ pamseq 2 255 -min=0,4 -max=2,8 -step=1,2 > tuples.pam
$ pamfile tuples.pam
tuples.pam: PAM, 9 by 1 by 2 maxval 255
$ pamseq 2 255 -min=0,4 -max=2,8 -step=1,2 \
    | pamrestack -width=3 > tuple-grid.pam
$ pamfile tuple-grid.pam
tuple-grid.pam: PAM, 3 by 3 by 2 maxval 255

The first stream has nine tuples in one row: (0,4), (0,6), (0,8), then the same second-plane values with first-plane value 1, then 2. pamrestack -width=3 turns that one row into three rows, a reshape that's entirely separate from pamseq and doesn't touch the values.

A common mistake is supplying the wrong number of comma-separated values. This fails safely before a useful image gets produced:

$ pamseq 2 10 -min=0
pamseq: Wrong number of values for -min: 1.  Need 2

Match the count to depth for all three range options. Do not silently reuse a depth-1 option list for a multi-plane image.

5. Add a tuple type for colour data

-tupletype sets the PAM TUPLTYPE attribute. It does not change the number of planes or scale the samples. A depth-3 image with maxval 5 contains every combination of three values from 0 through 5, so it comes out 216 pixels wide:

$ pamseq 3 5 -tupletype=RGB > web-safe-map.pam
$ pamfile web-safe-map.pam
web-safe-map.pam: PAM, 216 by 1 by 3 maxval 5, RGB
$ pamtopnm web-safe-map.pam > web-safe-map.ppm
$ file web-safe-map.ppm
web-safe-map.ppm: Netpbm image data, size 216 x 1, rawbits, pixmap

This is a compact colour map, not a photograph. The RGB label just describes the tuple type for readers that understand it. Modern Netpbm programs can consume the PAM RGB image directly; pamtopnm here is an explicit conversion for a tool that expects a plain PNM stream.

Keep the PAM file if you need its tuple metadata. If you only need a PPM, verify the generated PAM first and convert it afterwards.

6. Write output without losing an existing file

Warning

Shell redirection truncates its destination before pamseq starts. If sequence.pam already holds a good result, don't redirect straight over it. Generate a temporary sibling, verify it, then replace the old file deliberately:

$ pamseq 1 255 -min=0 -max=255 -step=16 > sequence.pam.new
$ pamfile sequence.pam.new
sequence.pam.new: PAM, 16 by 1 by 1 maxval 255
$ mv sequence.pam.new sequence.pam

Recovery

If generation or verification fails, leave the original untouched and remove only the known temporary file after checking its path. Removing a file is irreversible in the normal shell workflow, so don't use a broad wildcard. If the final mv replaced the wrong destination, restore it from your backup or version control, pamseq has no undo operation of its own.

For automation, check the exit status before accepting the result and keep the output in a directory the invoking user can write to. A successful command only says pamseq completed; pamfile still has to confirm the dimensions, depth and maxval the next stage expects.

Common traps

  • Complaint about a range list. Its length usually doesn't match depth. Match the count of comma-separated values to depth for -min, -max and -step together.
  • "Invalid maximum" or "invalid step". The requested sequence violates the numeric limits. Reduce the command to a small range, check it with pamfile, then add the real values.
  • Downstream program rejects the image. Inspect the PAM header and check whether it expects one row or a reshaped image, pamseq always creates height 1. Use pamrestack only when you have a deliberate width and know how many tuples the input holds.
  • Output looks empty or unreadable in a terminal. That's expected for binary image data. Use pamfile, file, a Netpbm converter, or an image viewer instead of guessing from terminal characters.

Done means

  • Versions confirmed. You checked the installed pamseq and Netpbm version.
  • Image inspected. You generated and checked a PAM image with the expected width, height, depth and maxval.
  • Range lists match depth. Every range list has one value per plane, with positive steps.
  • Reshaping kept separate. You used pamrestack only as a deliberate, standalone reshape step.
  • Tuple type labelled. RGB tuples were labelled explicitly when a downstream reader needed that metadata.
  • Existing output protected. You protected an existing file from shell redirection and verified replacements before moving them into place.