25
votes

Say I have a 2D Numpy array of values on the range 0 to 1, which represents a grayscale image. How do I then convert this into a PIL Image object? All attempts so far have yielded extremely strange scattered pixels or black images.

for x in range(image.shape[0]):
    for y in range(image.shape[1]):
        image[y][x] = numpy.uint8(255 * (image[x][y] - min) / (max - min))

#Create a PIL image.
img = Image.fromarray(image, 'L')

In the code above, the numpy array image is normalized by (image[x][y] - min) / (max - min) so every value is on the range 0 to 1. Then it is multiplied by 255 and cast to an 8 bit integer. This should, in theory, process through Image.fromarray with mode L into a grayscale image - but the result is a set of scattered white pixels.

3
Are you using a recent version of Pillow, a maintained fork of PIL, or are you using the original PIL?MattDMo
+MattDMo I'm using the most recent version of Pillow, and I'm using in particular Python 3.4Blake Doeren
Please edit your question and post what you have tried so far, including example input, expected output, the actual output (if any), and the full text of any errors or tracebacks.MattDMo
+MattDMo I edited, but there's not very much information I can add. This is less of a specific issue and more of a general problem.Blake Doeren

3 Answers

26
votes

I think the answer is wrong. The Image.fromarray( ____ , 'L') function seems to only work properly with an array of integers between 0 and 255. I use the np.uint8 function for this.

You can see this demonstrated if you try to make a gradient.

import numpy as np
from PIL import Image

# gradient between 0 and 1 for 256*256
array = np.linspace(0,1,256*256)

# reshape to 2d
mat = np.reshape(array,(256,256))

# Creates PIL image
img = Image.fromarray(np.uint8(mat * 255) , 'L')
img.show()

Makes a clean gradient

vs

import numpy as np
from PIL import Image

# gradient between 0 and 1 for 256*256
array = np.linspace(0,1,256*256)

# reshape to 2d
mat = np.reshape(array,(256,256))

# Creates PIL image
img = Image.fromarray( mat , 'L')
img.show()

Has the same kind of artifacting.

6
votes

If I understood you question, you want to get a grayscale image using PIL.

If this is the case, you do not need to multiply each pixels by 255.

The following worked for me

import numpy as np
from PIL import Image

# Creates a random image 100*100 pixels
mat = np.random.random((100,100))

# Creates PIL image
img = Image.fromarray(mat, 'L')
img.show()
0
votes

im = Image.fromarray(np.uint8(mat), 'L')

or

im = Image.fromarray(np.uint8(mat))

Apparently it accepts type np.uint8(insert array here), also may be able to remove 'L' for conciseness.