Paraprogramming Dispatches


The simplest bitmap format ever


lisp
Eugene Zaikonnikov

So here's one fine, terse, human readable (!) image format you can roll quickly in a squeeze. It is also understood by nearly every web browser and image viewer out there. It's Netpbm, the underdog of image processing world.

There are three formats, for binary, grayscale and RGB images. Both come in human-readable ASCII and binary format. Here's a sample for ASCII grayscale borrowed from pgm manpage:

       P2
       # feep.pgm
       24 7
       15
       0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
       0  3  3  3  3  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15 15 15 15  0
       0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0 15  0
       0  3  3  3  0  0  0  7  7  7  0  0  0 11 11 11  0  0  0 15 15 15 15  0
       0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0  0  0
       0  3  0  0  0  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15  0  0  0  0
       0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

The header is the format magic (P2 is ASCII grayscale), arbitraty comment lines starting with '#', image dimensions and the maximum grayscale value. The latter is rather nice property as it allows for flexible grayscales. The header is followed by ASCII image value payload, formatted according to the header specs.

Here's a short Common Lisp code example.

(defun image-to-pgm (image filename)
  (with-open-file (f filename :direction :output
                      :if-exists :overwrite :if-does-not-exist :create)
    (let ((y (first (array-dimensions image)))
      (x (second (array-dimensions image))))
      (format f "P2~%~D ~D~%255" y x)
      (loop for i from 0 below y do
       (terpri f)
       (loop for j from 0 below x do
        (format f "~D " (- 255 (aref image i j))))))))

Now the impracticality of it in every day use is clear. It is wasteful with bytes (although it should compress just as well as its binary counterpart). The spec also limits the ASCII version to 70 characters per line, although some parsers would accept longer ones. However it is great little format when you need to viusalize smaller things but don't feel like using an image format library.