How to Convert a Pil Image into a Numpy Array

How do I convert a PIL Image into a NumPy array?

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).

How to convert PIL image to numpy array?

If your image dimensions have a 3 for the final axis, that normally means you have a 3-channel RGB image.

If you want a single channel image, you will lose the RGB colour and just have a greyscale image. You can do that like this:

grey = Image.open(fname).convert('L')

PIL image to array (numpy array to array) - Python

I think what you are looking for is:

list(im.getdata())

or, if the image is too big to load entirely into memory, so something like that:

for pixel in iter(im.getdata()):
print pixel

from PIL documentation:

getdata

im.getdata() => sequence

Returns the contents of an image as a sequence object containing pixel
values. The sequence object is flattened, so that values for line one
follow directly after the values of line zero, and so on.

Note that the sequence object returned by this method is an internal
PIL data type, which only supports certain sequence operations,
including iteration and basic sequence access. To convert it to an
ordinary sequence (e.g. for printing), use list(im.getdata()).

Python conversion of PIL image to numpy array very slow

You can just let numpy handle the conversion instead of reshaping yourself.

def pil_image_to_numpy_array(pil_image):
return np.asarray(pil_image)

You are converting image into (height, width, channel) format. That is default conversion numpy.asarray function performs on PIL image so explicit reshaping should not be neccesary.

Preserve 3-dimensionality when converting PIL image to numpy array

I'm guessing numpy does this for performance reasons

Numpy has nothing to do with it, this is your PIL Image having one channel only.
The simplest solution is to just convert everything to RGB:

ims = [np.asarray(r.convert('RGB')).astype('float32') / 255.0) for r in images]

If you then call np.asarray(ims), you'll obtain an array of shape [N,30,30,3] where N is the number of images, which you can then transpose to your desired ordering.

Converting a very high res image to a numpy array

Set MAX_IMAGE_PIXELS to None to disable the limit on image size. (Warning: Do this only if you trust where you are getting the images from)

from PIL import Image
Image.MAX_IMAGE_PIXELS = None

Can't recover the same numpy array from PIL-saved PNG image

As @mateusreis correctly points out, the .png format only supports 16 bits per pixel in grayscale, so either you have to transform the value into a 3x8 24 bits per pixel RGB value, or you should save in an image format that support 32 bits per pixel, like TIFF:

import tempfile
from PIL import Image
import numpy as np

im_np = np.array([[1, 1], [1, 2 ** 16]], dtype=np.int32)
im_pil = Image.fromarray(im_np, mode='I')

with tempfile.TemporaryFile() as f:
im_pil.save(f, 'TIFF')

with Image.open(f) as im:
recovered_im_np = np.array(im)

print(f"Numpy array:\n{im_np}")
print(f"Numpy array receovered from PNG:\n {recovered_im_np}")

Result:

Numpy array:
[[ 1 1]
[ 1 65536]]
Numpy array receovered from PNG:
[[ 1 1]
[ 1 65536]]

The key thing here is to realise you make 2 conversions (in both directions):

  • 1.) numpy array -> 2.) PIL image -> 3.) image file format

Your mode='I' covers 1 -> 2, but you need to pick the right format to preserve the data for 2 -> 3.

PIL image to numpy

The so called "original image" has false colors of "viridis" colormap.

Viridis colormap is the default color map of matplotlib.

Using image = Image.fromarray(input_image,'RGB') is not going to convert the image into false colors - it reinterprets the values as RGB, and the result looks like random noise.

For getting the same colors as matplotlib "viridis" colormap as RGB values, we may use the following stages:

  • Get Viridis colormap (from matplotlib), and convert it to a look up table.

    The size of the look up table is going to be 256 entries.

    The input is value in range [0, 255], and the output is RGB triplet.
  • Convert input image to uint8 (range [0, 255]) using linear transformation.

    The linear transformation passes the minimum (of input_image) to 0, the maximum (of input_image) to 255, and other values are transformed linearly.
  • Pads img through Viridis colormap look up table.

We may select other type of colormap (Viridis is just an example).


Code sample:

from matplotlib import pyplot as plt
import numpy as np
from PIL import Image

# Build arbitrary image for testing
################################################################################
cols, rows = 320, 256
x, y = np.meshgrid(np.arange(cols), np.arange(rows))
input_image = (x*y % 10000).astype(np.uint16)
#image = Image.fromarray(input_image)
#plt.figure()
#plt.imshow(image)
#plt.show(block=True)
################################################################################

# Get viridis colormap, and convert it to look up table:
cmap = plt.get_cmap('viridis')
cmaplist = [cmap(i) for i in range(cmap.N)] # https://stackoverflow.com/q/43384448/4926757
lut = np.array(cmaplist) # The look up table is given in RGBA color space (256 entries).
lut = np.round(lut[:, 0:3]*255).astype(np.uint8) # Convert from RGBA to RGB (get only first 3 elements) and convert to uint8

# Convert input image to uint8 (range [0, 255]) using linear transformation:
minval = input_image.min()
maxval = input_image.max()
img = ((input_image.astype(float) - minval)*(255.0/(maxval-minval)))
img = np.round(img).astype(np.uint8)

# Pads img through viridis colormap look up table
colored_img = lut[img]

plt.figure()
plt.imshow(colored_img)
plt.show(block=True)

Output:

Sample Image



Related Topics



Leave a reply



Submit