Reputation: 11922
I have an array (numpy) of shape (10000,1296) that I want to save as 10,000 images of 36x36 size. All values in the array are in the range (0,255)
. So I have used this code to do that (which worked well):
for i, line in enumerate(myarray):
img = Image.new('L',(36,36))
img.putdata(line)
img.save(str(i)+'.png')
I wanted to replace this code using the Image.fromarray
method, but the resulting pictures are distorted beyond recognition compared to the original method. First I tried this:
myarray = myarray.reshape(10000,36,36)
for i, line in enumerate(myarray):
img = Image.fromarray(line, 'L')
img.save(str(i)+'.png')
Which didn't work. So to debug I thought I would try just one item and did this:
Image.fromarray(myarray[0].reshape(36,36), 'L').save('test.png')
And again - garbled distorted image.
So I figured either fromarray
isn't working like I thought it should, or my reshape
is too naive and messes up the data, but I am not able to fix this. Any ideas are welcome.
Upvotes: 3
Views: 3215
Reputation: 879103
PIL's L
mode is a grayscale mode for data that represents luminance
(brightness). The data is expected to be ints from 0 to 255. If you create a PIL Image by passing a NumPy array to Image.fromarray
with mode='L'
, the dtype of the array should be uint8
. Therefore use
myarray = myarray.astype('uint8')
to ensure that the arrays passed to Image.fromarray
have dtype uint8
.
uint8
s are unsigned 8-bit ints. float32
s are 32-bit floats. They are 4 times as wide as a uint8
. Image.fromarray
views the underlying data in the NumPy array as uint8
s, reads enough bytes to fill the image, and ignores the rest. So each 32-bit float becomes four 8-bit ints and each of those 8-bit ints colors a different pixel.
In other words, the following assert
passes:
import numpy as np
from PIL import Image
line = np.arange(256).reshape(16, 16).astype('float32')
img = Image.fromarray(line, 'L')
line2 = np.asarray(img)
assert (line.view('uint8').ravel()[:256].reshape(16, 16) == line2).all()
This is why using myarray
without converting to uint8
s creates a garbled image.
Alternatively, instead of converting myarray
to uint8
s you could
read the data in mode='F'
(float mode):
import numpy as np
from PIL import Image
line = np.arange(256).reshape(16, 16).astype('float32')
img = Image.fromarray(line, 'F').convert('L')
img.save('/tmp/out.png')
which yields
See this page for a list of all the possible PIL modes.
Upvotes: 6