Differences between Pillow and Pyplot in turning an array into an image

  • Python
  • Thread starter Avatrin
  • Start date
  • #1
244
6

Summary:

Pillow produces different output than Pylot from same array.

Main Question or Discussion Point

The other day I was trying to visualize a field using line integral convolution. I thought I kept failing for days since Pillow was giving me outputs similar to this one (img = Image.fromarray(output_image, 'L')):

EWnLd.png


I thought I was making some mistake until I tried Pyplot (f.figimage(output_image)):

TiwNr.png


Except for being colored, this is the exact output I wanted. That leads me to wonder; Why is this? How do Pillow and Pyplot turn arrays into images that give them so fundamentally different outputs? For Pillow I tried RGB and LA as well, but it still gave me noise. Pyplot gave me the representation I wanted.

Also, I am not referring to the blue-green-yellow coloring of the Pyplot output; I want to know why Pillow did not capture the circular motion that line integral convolution created for the vector field while Pyplot did. The "output_image" array is the same for the two images above (i.e. I created both images during the same run of the code).

Also, output_image contain a two-dimensional array of floating-point number values between zero and one.
 

Answers and Replies

  • #2
33,646
5,315
In your code using the fromarray function in the Pillow Image class, the mode is 'L', which means 8-bit pixels. Since your array is floating point numbers (float type = 32 bits and double type = 64 bits), what you're going to get won't be meaningful, as far as I can tell. Most or all of the modes require data to be integer in type.

For the Pyplot function figimage, I can't tell from the matplotlib documentation what type the array should be. It's possible that the array data should be float, which would explain why your image comes out as you expected.

BTW, both fromarray and figimage take a lot more parameters than you show in your code.
 
  • Like
Likes Avatrin
  • #3
244
6
In your code using the fromarray function in the Pillow Image class, the mode is 'L', which means 8-bit pixels. Since your array is floating point numbers (float type = 32 bits and double type = 64 bits), what you're going to get won't be meaningful, as far as I can tell. Most or all of the modes require data to be integer in type.

For the Pyplot function figimage, I can't tell from the matplotlib documentation what type the array should be. It's possible that the array data should be float, which would explain why your image comes out as you expected.

BTW, both fromarray and figimage take a lot more parameters than you show in your code.
Thank you! That makes a lot of sense. I guess I should try using int(floor(element*256)) for each element in the array to see if it gives me the output I want.

Regarding the parameters. Yeah, figimage takes a lot more parameteres than I show, but I was alright with the default values (for instance, the blue-yellow-green representation doesn't matter.. visualizing the vector field was what I needed to do). Looking through the documentation, fromarray only uses those two parameters, or am I wrong?
 
  • #4
33,646
5,315
Right, fromarray uses only two parameters, so your code example had the right number of parameters. The documentation says that if you don't include a mode parameter, I guess it determines the mode from the image type.

Notice how they do things in the docs for fromarray:
Python:
im = Image.open('hopper.jpg')
a = np.asarray(im)
im = Image.fromarray(a)
First they open the file, then they use the numpy asarray function to convert the image data to an array, and finally they use fromarray to create an image.

With jpg and other image types, the image file header contains information about the size of an image (both in bytes and as a rectangular array of pixels), the number of bytes per pixel, plus a lot of other information.
 
  • #5
244
6
Hmm, this actually didn't work. This is my current output having turned each element in my array to an integer:
pillowwithint.png


However, I am not opening any image; I am just doing this:
Python:
img = Image.fromarray(np.array(output_image), 'LA')
img.save('pillowwithint.png')
The reason for this is that LIC starts with a texture like white noise which I am initializing with Numpy:
Code:
noisy_image = numpy.random.rand(length_image,length_image)
Then I am using LIC to create the motion. Finally, I am turning the result into an image.

Right, fromarray uses only two parameters, so your code example had the right number of parameters. The documentation says that if you don't include a mode parameter, I guess it determines the mode from the image type.
PIL seems to be interpreting 'LA' like 'mode='LA'' as was intended (since other mode strings seem to be producing correct outputs. Also, it's not giving me an error).
 
  • #6
33,646
5,315
Hmm, this actually didn't work. This is my current output having turned each element in my array to an integer:
View attachment 260034

However, I am not opening any image; I am just doing this:
Python:
img = Image.fromarray(np.array(output_image), 'LA')
img.save('pillowwithint.png')
What is output_image? Presumably from the name it's a variable that contains the filename of some image file, such as jpg or whatever. The Numpy array() function (https://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html) takes 6 parameters, of which the last 5 are optional. In your example, it's possible you need to provide some parameters, particularly the ndmin.
Mode "LA" is 8-bit pixels with alpha. That's definitely different from what you got with pyplot's figimage() function.

The reason for this is that LIC starts with a texture like white noise which I am initializing with Numpy:
Code:
noisy_image = numpy.random.rand(length_image,length_image)
[/QUOTE]Again, I think there's a mismatch between the types you actually have and the types you need.
The code above creates a two-dimensional array of floating point numbers (64-bits, I believe) with values between 0.0 and 1.0. Your Pillow fromarray() function is, I believe, trying to process these numbers 8 bits at a time, resulting in garbage output.
Avatrin said:
Then I am using LIC to create the motion. Finally, I am turning the result into an image.


PIL seems to be interpreting 'LA' like 'mode='LA'' as was intended (since other mode strings seem to be producing correct outputs. Also, it's not giving me an error).
 
  • #7
244
6
What is output_image? Presumably from the name it's a variable that contains the filename of some image file, such as jpg or whatever. The Numpy array() function (https://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html) takes 6 parameters, of which the last 5 are optional. In your example, it's possible you need to provide some parameters, particularly the ndmin.
Mode "LA" is 8-bit pixels with alpha. That's definitely different from what you got with pyplot's figimage() function.
Again, I think there's a mismatch between the types you actually have and the types you need.
The code above creates a two-dimensional array of floating point numbers (64-bits, I believe) with values between 0.0 and 1.0. Your Pillow fromarray() function is, I believe, trying to process these numbers 8 bits at a time, resulting in garbage output.
Okay, here's what I do; I stary with noisy_image as described above. I use it with LIC to visualize a vector field and the output is output_image, which, yes, is an array containing floating point numbers between 0.0 and 1.0. Using this with figimage does give me the correct output in my original post. However, that's what didn't work with fromarray.

So, as I wrote yesterday, I was going to run each element in output_image through int(floor(element*256)) to get an integer that can be represented by eight bits. However, using that array with pil.fromarray is what gave me the output in my previous post.
 

Related Threads on Differences between Pillow and Pyplot in turning an array into an image

  • Last Post
Replies
4
Views
14K
Replies
0
Views
2K
  • Last Post
2
Replies
33
Views
2K
Replies
3
Views
3K
Replies
7
Views
2K
  • Last Post
Replies
10
Views
3K
  • Last Post
Replies
1
Views
2K
Top