What is causing dimension-dependent AttributeError in PIL fromarray function?

attributeerror, numpy, python, python-imaging-library

Solution

I can reproduce on ubuntu 12.04 with Python 3.2.3, numpy 1.6.1, and PIL 1.1.7-for-Python 3 at http://www.lfd.uci.edu/~gohlke/pythonlibs/#pil. The difference happens because the array_interface of x doesn't have a strides value but y's and z's do:

>>> x.__array_interface__['strides']
>>> y.__array_interface__['strides']
(9, 1)
>>> z.__array_interface__['strides']
(9, 3)

and so a different branch is taken here:

if strides is not None:
    obj = obj.tobytes()

The documentation mentions `tostring`, not `tobytes`:

# If obj is not contiguous, then the tostring method is called
# and {@link frombuffer} is used.

And the Python 2 source of PIL 1.1.7 uses `tostring`:

if strides is not None:
    obj = obj.tostring()

so I suspect that this was an error introduced during a 2-to-3 conversion in which str/bytes changes were made. Simply replace `tobytes()` by `tostring()` in `Image.py` and it should work:

Python 3.2.3 (default, May  3 2012, 15:54:42) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> from PIL import Image
>>> 
>>> a = np.ones( ( 3,3,3), dtype='uint8' )
>>> x = a[1,:,:]
>>> y = a[:,1,:]
>>> z = a[:,:,1]
>>> 
>>> imx = Image.fromarray(x)  # ok
>>> imy = Image.fromarray(y)  # now no error!
>>> imz = Image.fromarray(z)  # now no error!
>>> 

Problem

I'm getting an error from the following Python3 code, in the indicated lines. x, y, and z are all plain 2D numpy arrays identical but for size, and ought to work the same. Yet they act differently, with y and z crashing while x works fine. ``` import numpy as np from PIL import Image a = np.ones( ( 3,3,3), dtype='uint8' ) x = a[1,:,:] y = a[:,1,:] z = a[:,:,1] imx = Image.fromarray(x) # ok imy = Image.fromarray(y) # error imz = Image.fromarray(z) # error ``` but this works ``` z1 = 1*z imz = Image.fromarray(z1) # ok ``` The error is: ``` Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Python3\lib\site-packages\PIL\Image.py", line 1918, in fromarray obj = obj.tobytes() AttributeError: 'numpy.ndarray' object has no attribute 'tobytes' ``` So what's different between x, y, z, z1? Nothing that I can tell. ``` >>> z.dtype dtype('uint8') >>> z1.dtype dtype('uint8') >>> z.shape (3, 4) >>> z1.shape (3, 4) ``` I'm using Python 3.2.3 on a Windows 7 Enterprise machine, with everything 64 bit.

Original source