Juan David
Juan David

Reputation: 2797

Using colormap to create a PIL Image from the luminosity map of an image

I have M vectors containing 4096 data points between 0 and 1 that represent the luminosity map of faces. This is a sample of the actual images.

enter image description here

Now, my purpose is to put them in a plotly visual, but to do so I need to provide a PIL object representing the image, This is my MVC

import PIL.Image as pilim
import matplotlib.cm as cm
import numpy as np

greys = cm.get_cmap('Greys')
num_images = 10
num_faces = faces.shape[0]
sample_images = np.random.choice(num_faces, num_images, replace=False)

for index in sample_images:
     greyscale = np.apply_along_axis(greys, 0, faces[index]).reshape((64, 64, 4))
     im = pilim.fromarray(greyscale, mode='RGBA')
     im.save('test{}.png'.format(index))    greys = cm.get_cmap('Greys')

Faces is a ndarray with 698 samples. Something like the following sample

[[0.01617647 0.01617647 0.01617647 ... 0.         0.         0.        ]
 [0.01617647 0.01617647 0.01617647 ... 0.         0.         0.        ]
 [0.01617647 0.01617647 0.01617647 ... 0.         0.         0.        ]]

and this is my depressing result

enter image description here

Upvotes: 1

Views: 3243

Answers (2)

JohanC
JohanC

Reputation: 80359

PIL works with pixel data, so each of RGBA is a value from 0 to 255. A colormap default generates its RGBA values in the range 0-1. To convert them, you could multiply those by 255 and convert to 8 bit unsigned integers (uint8), like so:

greyscale = np.uint8(cmap(faces[index].reshape(64,64)) * 255)

But, matplotlib's colormaps also support a parameter to directly generate those bytes:

greyscale = cmap(faces[index].reshape(64,64), bytes=True)

You could reshape your arrays afterwards to (64,64,4), but it is easier and more readable to do the conversion before applying the colormap.

There is a choice of several sequential colormaps for this type of images. Appending an _r to the name gives the reverse colormap (so dark and light reversed).

Here is some code to get you started:

import PIL.Image as pilim
import matplotlib.cm as cm
import numpy as np
from matplotlib import pyplot as plt

cmap = cm.get_cmap('copper_r') # 'bone_r', 'Greys', 'copper_r', 'Purple', ...
num_images = 1

faces = np.tile(np.linspace(0,1,4096), 698).reshape(698, 4096)

num_faces = faces.shape[0]
sample_images = np.random.choice(num_faces, num_images, replace=False)
print(sample_images)
for index in sample_images:
     greyscale = cmap(faces[index].reshape(64,64), bytes=True)
     im = pilim.fromarray(greyscale, mode='RGBA')
     im.save(f'test{index}.png')

PS: There is also an imsave function in matplotlib, which would further simplify the code:

for index in sample_images:
     plt.imsave(f'test{index}.png', faces[index].reshape(64,64), cmap=cmap)

If the image would show up upside down, adding origin='lower' to imsave would reverse it.

Upvotes: 2

Juan David
Juan David

Reputation: 2797

The solution is actually pretty simple. There are two steps missing on my code:

  1. re-scale to 0-255 values
  2. Cast to uint8 in order to PIL to understand the array

    greyscale = np.apply_along_axis(greys, 0, faces[index]).reshape((64, 64, 4))*255 greyscale = greyscale.astype(np.uint8) im = pilim.fromarray(greyscale)

https://python-decompiler.com/article/2012-06/how-to-convert-numpy-array-to-pil-image-applying-matplotlib-colormap

Upvotes: 0

Related Questions