395

Alright, I'm toying around with converting a PIL image object back and forth to a numpy array so I can do some faster pixel by pixel transformations than PIL's PixelAccess object would allow. I've figured out how to place the pixel information in a useful 3D numpy array by way of:

pic = Image.open("foo.jpg")
pix = numpy.array(pic.getdata()).reshape(pic.size[0], pic.size[1], 3)

But I can't seem to figure out how to load it back into the PIL object after I've done all my awesome transforms. I'm aware of the putdata() method, but can't quite seem to get it to behave.

kmario23
  • 50,454
  • 13
  • 141
  • 141
akdom
  • 30,241
  • 26
  • 72
  • 79
  • 8
    Note that `pic.size[0]` and `pic.size[1]` should be swapped (ie. `reshape(pic.size[1], pic.size[0], 3)`), since `size` is `width x height` or `x * y`, while matrix ordering is `rows x columns`. – foges Mar 21 '18 at 15:16

8 Answers8

401

You're not saying how exactly putdata() is not behaving. I'm assuming you're doing

>>> pic.putdata(a)
Traceback (most recent call last):
  File "...blablabla.../PIL/Image.py", line 1185, in putdata
    self.im.putdata(data, scale, offset)
SystemError: new style getargs format but argument is not a tuple

This is because putdata expects a sequence of tuples and you're giving it a numpy array. This

>>> data = list(tuple(pixel) for pixel in pix)
>>> pic.putdata(data)

will work but it is very slow.

As of PIL 1.1.6, the "proper" way to convert between images and numpy arrays is simply

>>> pix = numpy.array(pic)

although the resulting array is in a different format than yours (3-d array or rows/columns/rgb in this case).

Then, after you make your changes to the array, you should be able to do either pic.putdata(pix) or create a new image with Image.fromarray(pix).

dF.
  • 71,061
  • 29
  • 127
  • 135
  • 3
    First, shouldn't it be pic.putdata(data)? And numpy.asarray(pic) produces a readonly array, so you need to call numpy.array(pic), and you didn't answer the question... from the link you provided it appears to be pic = Image.fromarray(pix). Fix your answer and I'll accept it. – akdom Dec 23 '08 at 19:10
  • 2
    Thanks for this...`Image.fromarray` is not listed in the PIL documentation (!) so I'd never have found it if it weren't for this. – Nathan Reed Jul 15 '12 at 05:20
  • 28
    That page lists `numpy.asarray(pic)` as the "proper" way to convert, not `numpy.array(pic)`. As per [this answer](http://stackoverflow.com/questions/14415741/numpy-array-vs-asarray) `array` will make a copy whereas `asarray` will not (but then the `asarray` result will be read-only). – Arthur Tacca Nov 30 '16 at 10:13
  • 1
    A warning here (from my own mistake): you need to consider the scale and ranges of the data as well. In many usecases you'd render Images with 0-255 bytes, but you might expect these to get converted to for example 0.0-1.0 in the numpy array. Some unit conversions from uint8 do this, but in this case, it doesn't.. so check it :) – BjornW Jan 21 '19 at 10:15
  • 3
    The second answer is better. – Nathan Jun 02 '20 at 17:42
  • 1
    The link is dead – WinEunuuchs2Unix Jan 28 '21 at 23:49
295

Open I as an array:

>>> I = numpy.asarray(PIL.Image.open('test.jpg'))

Do some stuff to I, then, convert it back to an image:

>>> im = PIL.Image.fromarray(numpy.uint8(I))

Source: Filter numpy images with FFT, Python

If you want to do it explicitly for some reason, there are pil2array() and array2pil() functions using getdata() on this page in correlation.zip.

Josiah Yoder
  • 2,896
  • 4
  • 33
  • 52
endolith
  • 23,532
  • 31
  • 125
  • 187
  • 2
    @ArditS.: Did you `import Image` first? Do you have PIL installed? – endolith Nov 09 '13 at 17:04
  • 7
    Is the `uint8` conversion necessary? – Neil Traft Jan 16 '14 at 05:33
  • 1
    you might need to use numpy.copy to have I be editable: `I = numpy.copy(numpy.asarray(Image.open('test.jpg')))` – Nick Nov 05 '14 at 06:57
  • 1
    Thanks for this, I was using np.array(image.getdata(),...) which is incredibly slow compared to this. – Rebs Nov 25 '14 at 10:08
  • 1
    @NeilTraft No, sorry. Note that if your image is a float array from 0 to 1, you need to scale it first: `Image.fromarray(J*255)` – endolith Dec 27 '14 at 21:04
  • 4
    `numpy.asarray(Image.open(filename))` seems to work for .jpg images but not for .png. The result displays as `array(, dtype=object)`. There seem to be no obviously-named methods of the `PngImagePlugin.PngImageFile` object for solving this. Guess I should ask this as a new question but it's very relevant to this thread. Anybody understand what's going wrong here? – jez Dec 01 '15 at 23:59
  • 5
    @Rebs: here's the reason *why* this is so much faster: `getdata()` returns a sequence like object (http://pillow.readthedocs.io/en/3.4.x/reference/Image.html#PIL.Image.Image.getdata), but a pillow image implements the `__array_interface__` which `numpy` can use to access the raw bytes of an image without having to pass through an iterator (see https://github.com/python-pillow/Pillow/blob/730cf93c32ffb747c018afffe597ef9ae264a20a/PIL/Image.py#L633 and https://docs.scipy.org/doc/numpy/reference/arrays.interface.html). You can even just use `numpy.array(PIL.Image.open('test.jpg'))` – tdp2110 Jul 28 '17 at 17:34
  • 4
    @jez Check if the Image object is closed before you convert it to numpy. The same happened to me and I found I closed the image object somewhere. – Shaohua Li Aug 18 '18 at 14:34
  • @jez I use `numpy.asarray(PIL.Image.open('test.png').convert('RGB'))` which works for even for .png images – Mohit Lamba Aug 08 '20 at 05:17
108

I am using Pillow 4.1.1 (the successor of PIL) in Python 3.5. The conversion between Pillow and numpy is straightforward.

from PIL import Image
import numpy as np
im = Image.open('1.jpg')
im2arr = np.array(im) # im2arr.shape: height x width x channel
arr2im = Image.fromarray(im2arr)

One thing that needs noticing is that Pillow-style im is column-major while numpy-style im2arr is row-major. However, the function Image.fromarray already takes this into consideration. That is, arr2im.size == im.size and arr2im.mode == im.mode in the above example.

We should take care of the HxWxC data format when processing the transformed numpy arrays, e.g. do the transform im2arr = np.rollaxis(im2arr, 2, 0) or im2arr = np.transpose(im2arr, (2, 0, 1)) into CxHxW format.

Daniel
  • 1,937
  • 3
  • 13
  • 23
  • 4
    This is about the cleanest example, including import statements (thanks for that detail). Let's vote this answer up to increase visibility. – David Parks Apr 07 '18 at 15:17
  • 1
    I found that when I converted a PIL drawn image to a numpy array, when using matplotlib imshow on the array, it showed it upside down requiring a `np.flipud` to fix. Although my PIL image was created from scratch using `ImageDraw.Draw`. I think one must be careful where the origin of their coordinates comes from. – CMCDragonkai Apr 13 '18 at 07:21
  • 1
    Bless you!! I have been looking for this answer for half a day. It solves my problem of restoring the original axis after of the plot image to the original one. – Tinkerbell Jun 27 '18 at 10:21
35

You need to convert your image to a numpy array this way:

import numpy
import PIL

img = PIL.Image.open("foo.jpg").convert("L")
imgarr = numpy.array(img) 
Billal Begueradj
  • 17,880
  • 38
  • 105
  • 123
29

Convert Numpy to PIL image and PIL to Numpy

import numpy as np
from PIL import Image

def pilToNumpy(img):
    return np.array(img)

def NumpyToPil(img):
    return Image.fromarray(img)
Kamran Gasimov
  • 955
  • 1
  • 11
  • 10
3

The example, I have used today:

import PIL
import numpy
from PIL import Image

def resize_image(numpy_array_image, new_height):
    # convert nympy array image to PIL.Image
    image = Image.fromarray(numpy.uint8(numpy_array_image))
    old_width = float(image.size[0])
    old_height = float(image.size[1])
    ratio = float( new_height / old_height)
    new_width = int(old_width * ratio)
    image = image.resize((new_width, new_height), PIL.Image.ANTIALIAS)
    # convert PIL.Image into nympy array back again
    return array(image)
Uki D. Lucas
  • 476
  • 6
  • 4
0

If your image is stored in a Blob format (i.e. in a database) you can use the same technique explained by Billal Begueradj to convert your image from Blobs to a byte array.

In my case, I needed my images where stored in a blob column in a db table:

def select_all_X_values(conn):
    cur = conn.cursor()
    cur.execute("SELECT ImageData from PiecesTable")    
    rows = cur.fetchall()    
    return rows

I then created a helper function to change my dataset into np.array:

X_dataset = select_all_X_values(conn)
imagesList = convertToByteIO(np.array(X_dataset))

def convertToByteIO(imagesArray):
    """
    # Converts an array of images into an array of Bytes
    """
    imagesList = []

    for i in range(len(imagesArray)):  
        img = Image.open(BytesIO(imagesArray[i])).convert("RGB")
        imagesList.insert(i, np.array(img))

    return imagesList

After this, I was able to use the byteArrays in my Neural Network.

plt.imshow(imagesList[0])
Charles Vogt
  • 121
  • 1
  • 4
-4
def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

You can transform the image into numpy by parsing the image into numpy() function after squishing out the features( unnormalization)

Thiyagu
  • 39
  • 1
  • 6
  • This is for conversion between numpy and a tensor in e.g. PyTorch. This question is about PIL. – moi Mar 08 '22 at 16:47