Python PIL struggles with uncompressed 16-bit TIFF images

python, python-imaging-library, tiff

Solution

It turns out that Matplotlib handles 16-bit uncompressed TIFF images in two lines of code:

import matplotlib.pyplot as plt
img = plt.imread(filename)

# >>> img
# array([[38948, 41457, 37714, ..., 61511, 61785, 61824],
#       [39704, 38083, 36690, ..., 61419, 60086, 61910],
#       [41449, 39169, 38178, ..., 60192, 60969, 63538],
#       ...,
#       [37963, 39531, 40339, ..., 62351, 62646, 61793],
#       [37462, 37409, 38370, ..., 61125, 62497, 59770],
#       [39753, 36905, 38778, ..., 61922, 59565, 60035]], dtype=uint16)

Et voila. I suppose this doesn't meet my requirements as "lightweight" since Matplotlib is (to me) a heavy module, but it is spectacularly simple to get the image into a Numpy array. I hope this helps someone else find a solution quickly as this wasn't obvious to me.

Problem

My system is Mac OS X v10.8.2. I have several 2560x500 uncompressed 16-bit TIFF images (grayscale, unsigned 16-bit integers). I first attempt to load them using PIL (installed via Homebrew, version 1.7.8): ``` from PIL import Image import numpy as np filename = 'Rocks_2ptCal_750KHz_20ms_1ma_120KV_2013-03-06_20-02-12.tif' img = Image.open(filename) # >>> img # <PIL.TiffImagePlugin.TiffImageFile image mode=I;16B size=2560x500 at 0x10A383C68> img.show() # almost all pixels displayed as white. Not correct. # MatLab, EZ-draw, even Mac Preview show correct images in grayscale. imgdata = list(img.getdata()) # most values negative: # >>> imgdata[0:10] # [-26588, -24079, -27822, -26045, -27245, -25368, -26139, -28454, -30675, -28455] imgarray = np.asarray(imgdata, dtype=np.uint16) # values now correct # >>> imgarray # array([38948, 41457, 37714, ..., 61922, 59565, 60035], dtype=uint16) ``` The negative values are off by 65,536... probably not a coincidence. If I pretend to alter pixels and revert back to TIFF image via PIL (by just putting the array back as an image): ``` newimg = Image.fromarray(imgarray) ``` I get errors: ``` File "/usr/local/lib/python2.7/site-packages/PIL/Image.py", line 1884, in fromarray raise TypeError("Cannot handle this data type") TypeError: Cannot handle this data type ``` I can't find `Image.fromarray()` in the PIL documentation. I've tried loading via `Image.fromstring()`, but I don't understand the PIL documentation and there is little in the way of example. As shown in the code above, PIL seems to "detect" the data as `I;16B`. From what I can tell from the PIL docs, mode `I` is: ``` *I* (32-bit signed integer pixels) ``` Obviously, that is not correct. I find many posts on SX suggesting that PIL doesn't support 16-bit images. I've found suggestions to use pylibtiff, but I believe that is Windows only? I am looking for a "lightweight" way to work with these TIFF images in Python. I'm surprised it is this difficult and that leads me to believe the problem will be obvious to others.

Original source