Issue
I have a 300*500 image. It's is in grayscale and ranges from 0-255. I want to iterate value by value and apply a heat map (say viridis but it doesn't matter) to each value.
My final heatmap image is in Red, Blue, Green and Alpha. I imagine the specific heat map function would take the grayscale values and output three values for each Red, Blue, Green and their appropriate weights.
f(0-255) = weightr(Red), weightb(Blue), weightg(Green).
My ending image would have dimensions (300,500,4) The four channels are r,b,g and an alpha channel.
What is the function that would achieve this? Almost certain it's going to be highly dependent on the specific heat map. Viridis is what I'm after, but I want to understand the concept as well.
The code below reads in a random image (the fact it's from unsplash does not matter) and turns it into a (300,500), 0-255 image called imgarray. I know matplotlib defaults to viridis, but I included the extra step to show what I would like to achieve with my own function.
import matplotlib.pyplot as plt
import requests
from PIL import Image
from io import BytesIO
img_src = 'https://unsplash.it/500/300'
response = requests.get(img_src)
imgarray = Image.open(BytesIO(response.content))
imgarray = np.asarray(imgarray.convert('L'))
from matplotlib import cm
print(cm.viridis(imgarray))
plt.imshow(cm.viridis(imgarray))
Solution
Matplotlib defines the viridis colormap as 256 RGB colors (one for each 8 bit gray scale value), where each color channel is a floating point value from [0, 1]. The definition can be found on github. The following code demonstrates how matplotlib applies the viridis colormap to a gray scale image.
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib._cm_listed import _viridis_data # the colormap look-up table
import requests
from PIL import Image
from io import BytesIO
img_src = 'https://unsplash.it/id/767/500/300'
response = requests.get(img_src)
imgarray = Image.open(BytesIO(response.content))
imgarray = np.asarray(imgarray.convert('L'))
plt.imshow(cm.viridis(imgarray))
plt.show()
# look-up table: from grayscale to RGB
viridis_lut = np.array(_viridis_data)
print(viridis_lut.shape) # (256, 3)
# convert grayscale to RGB using the LUT
img_viridis = viridis_lut.take(imgarray, axis=0, mode='clip')
plt.imshow(img_viridis)
plt.show()
# add an alpha channel
alpha = np.full(imgarray.shape + (1,), 1.) # shape: (300, 500, 1)
img_viridis_alpha = np.concatenate((img_viridis, alpha), axis=2)
assert (cm.viridis(imgarray) == img_viridis_alpha).all() # are both equal
Produces the following image:
The actual magic happens in the np.take(a, indices)
method, which takes values from array a
(the viridis LUT) at the given indices
(gray scale values from 0..255 from the image). To get the same result as from the cm.viridis
function, we just need to add an alpha channel (full of 1.
= full opacity).
For reference, the same conversion happens around here in the matplotlib
source code.
Answered By - asdf
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.