I have 16-bit PGM images that I am trying to read in Python. It seems (?) like PIL does not support this format?
import Image
im = Image.open('test.pgm')
im.show()
Shows roughly the image, but it isn't right. There are dark bands throughout and img is reported to have mode=L
. I think this is related to an early question I had about 16-bit TIFF files. Is 16-bit that rare that PIL just does not support it? Any advice how I can read 16-bit PGM files in Python, using PIL or another standard library, or home-grown code?
You need a mode of
"L;16"
; however it looks like PIL has a mode of"L"
hardcoded into File.c when loading a PGM. You’d have to write your own decoder if you want to be able to read a 16-bit PGM.However, 16-bit image support still seems flaky:
I think PIL is capable of reading images with 16 bits, but actually storing and manipulating them is still experimental.
See, it just interpreted the
0xCAFE
value as0xFE
, which isn’t exactly correct.The following depends only on numpy to load the image, which can be 8-bit or 16-bit raw PGM/PPM. I also show a couple different ways to view the image. The one that uses PIL (
import Image
) requires that the data first be converted to 8-bit.Usage notes
I eventually figured out "how it decides about endianness" -- it is actually storing the image in memory as big-endian (rather than native). This scheme might slow down any non-trivial image processing -- although other performance issues with Python may relegate this concern to a triviality (see below).
I asked a question related to the endianness concern here. I also ran into some interesting confusion related to endianness with this because I was testing by preprocessing the image with
pnmdepth 65535
which is not good (by itself) for testing endianness since the low and high bytes might end up being the same (I didn't notice right away becauseprint(array)
outputs decimal). I should have also appliedpnmgamma
to save myself some confusion.Because Python is so slow,
numpy
tries to besneakyclever about how it applies certain operations (see broadcasting). The first rule of thumb for efficiency withnumpy
is let numpy handle iteration for you (or put another way don't write your ownfor
loops). The funny thing in the code above is that it only partially follows this rule when doing the "example image processing", and therefore the performance of that line has an extreme dependency on the parameters that were given toreshape
.The next big
numpy
endianness mystery: Why doesnewbyteorder()
seem to return an array, when it's documented to return adtype
. This is relevant if you want to convert to native endian withdst.pixels=dst.pixels.byteswap(True).newbyteorder()
.Hints on porting to Python 3: binary input with an ASCII text header, read from stdin
Here's a generic PNM/PAM reader based on NumPy and an undocumented function in PyPNG.
Of course writing this image format generally doesn't require the assistance of a library...