Turn Raw RGB Bytes into a Verified PPM with rawtoppm
You will finish with a PPM image made from a stream of raw, 8-bit RGB bytes, plus checks that its dimensions and header are correct. The examples use rawtoppm from Netpbm 11.5.2, installed here as package version 2:11.05.02-1.1build1.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need a shell, a readable raw image file, and the image width and height. This workflow only reads the input and writes a new output file. It does not need sudo.
Safety warning
Shell redirection with > truncates an existing destination before rawtoppm starts. Use a new name while testing, or write a temporary file and rename it only after verification.
1. Check the installed command
Confirm that the command is the Netpbm binary you expect and record its package version:
$ command -v rawtoppm
/usr/bin/rawtoppm
$ dpkg-query -W -f='${Package} ${Version}\n' netpbm
netpbm 2:11.05.02-1.1build1
$ rawtoppm --version 2>&1 | head -4
rawtoppm: Using libnetpbm from Netpbm Version: Netpbm 11.5.2
rawtoppm: Built from source dated 2024-03-31 09:09:47
The version output includes build details, so the date and extra lines can differ between installations. The useful fact here is the Netpbm library version. The installed manual is the contract for the options used below.
Checkpoint
If command -v finds nothing, stop and install Netpbm through your normal package-management process. Do not copy a binary from an unrelated host merely to make the example run.
2. Confirm the input layout
rawtoppm does not discover image dimensions from a raw stream. You must supply width and height. By default, each pixel is three bytes in red, green, blue order, with one byte per component and a maximum value of 255. The expected data size is therefore:
width * height * 3 bytes
For example, a 2 by 2 RGB image needs 12 bytes. A file with a camera header or padding at the end of each row needs additional options, covered below. Keep the original input untouched until the output has been checked.
Do not infer the size from the file name. A wrong width shifts the boundary between rows, and the converter can still produce a syntactically valid but visibly corrupted PPM.
3. Convert a normal interleaved stream
Pass the dimensions and input file, then redirect standard output to a new PPM file:
$ rawtoppm 1920 1080 /path/to/frame.rgb > frame.ppm
The input argument is optional. If you leave it out, rawtoppm reads standard input, which is useful in a pipeline:
$ cat /path/to/frame.rgb | rawtoppm 1920 1080 > frame.ppm
There is normally no progress display because the image is written to standard output. A successful exit status means the converter completed; it does not prove that the supplied dimensions or colour order describe the source correctly.
Checkpoint
Check that the destination is non-empty and identified as a PPM before opening it in an image viewer:
$ test -s frame.ppm && echo 'output is non-empty'
output is non-empty
$ file frame.ppm
frame.ppm: Netpbm image data, size = 1920 x 1080, rawbits, pixmap
$ head -c 11 frame.ppm
P6
1920 1080
255
file may use slightly different wording. The important checks are the expected dimensions and the PPM magic number P6. The pixel data after the header is binary, so do not edit the file in a text editor.
4. Account for a header or padded rows
If a file starts with metadata before the pixel raster, skip that byte count with -headerskip:
$ rawtoppm -headerskip 128 640 480 /path/to/frame-with-header.rgb > frame.ppm
If every input row has padding after its pixels, skip that padding with -rowskip:
$ rawtoppm -rowskip 4 640 480 /path/to/padded-frame.rgb > frame.ppm
These values are bytes, not pixels. For a 640-pixel RGB row, the image data itself is 640 * 3 bytes. A four-byte alignment pad is extra, so -rowskip 4 is the right form. If both conditions apply, use both options:
$ rawtoppm -headerskip 128 -rowskip 4 640 480 /path/to/input.rgb > frame.ppm
Do not guess these counts from a damaged output. Obtain them from the format specification or the producer that wrote the stream. A plausible-looking PPM can still contain shifted rows.
5. Correct component order and interleaving
The default is -rgb and -interpixel: red, green and blue are adjacent for each pixel. Use one of the other component-order options when the source stores each pixel differently:
$ rawtoppm -bgr 1920 1080 /path/to/frame.bgr > frame.ppm
$ rawtoppm -gbr 1920 1080 /path/to/frame.gbr > frame.ppm
The supported orders are -rgb, -rbg, -grb, -gbr, -brg and -bgr. The option names describe the three bytes belonging to each pixel. If red and blue are swapped, people and objects often acquire an obvious colour cast, but a subtle source mistake may need a known test image to expose it.
Some formats store a complete row of one component, followed by a row of the next component, rather than placing components next to each pixel. Select -interrow for that layout:
$ rawtoppm -interrow 640 480 /path/to/planar-by-row.rgb > frame.ppm
-interplane is not implemented by this command. A file containing all red pixels, then all green pixels, then all blue pixels needs a different workflow, such as splitting the data into three parts, converting each part with rawtopgm, and combining the results with rgb3toppm.
6. Handle bottom-first images
The input is assumed to start with the top row. If the source stores rows from bottom to top, conversion still succeeds but the image is upside down. Convert first, then flip the PPM vertically:
$ rawtoppm 640 480 /path/to/bottom-first.rgb > upside-down.ppm
$ pamflip -tb upside-down.ppm > frame.ppm
$ file frame.ppm
frame.ppm: Netpbm image data, size = 640 x 480, rawbits, pixmap
The first command has not changed the source. If you are replacing an existing output, keep both names until you have inspected the corrected image.
7. Recover from a failed or unsafe write
If a conversion fails, the redirected destination may be empty or incomplete. With an important existing output, write to a temporary name in the same directory and replace the old file only after the checks pass:
$ rawtoppm 1920 1080 /path/to/frame.rgb > frame.ppm.new
$ test -s frame.ppm.new && file frame.ppm.new
$ mv frame.ppm.new frame.ppm
mv replaces frame.ppm if it already exists. That replacement is deliberate and can remove the only good copy, so make a backup first when the old output matters. If the conversion fails, leave the original output in place and remove only the incomplete frame.ppm.new after checking its exact path.
Done means
- You recorded the installed Netpbm version and confirmed the command path.
- You supplied dimensions from the real input format, not from a guess.
- The output begins with a P6 PPM header and reports the expected dimensions.
- Header bytes, row padding, component order and row interleaving match the source layout.
- A bottom-first image was flipped with
pamflip -tbwhen required. - An existing output was protected from truncation until the replacement was verified.