Scipy/Numpy: Apply cmap to actual data - python

I am trying to segment some DICOM images, and was trying to see if it was possible to apply a cmap filter on the actual numpy arrays
The left image is my goal, the right is what I currently have
I am able to get the left image by applying imshow(image, cmap='nipy_spectral')
but that doesn't change the actual numpy array on the right.
How would I go about actually applying cmap=nipy_spectral so it would actually transform the image on the right
Thanks

The colormap functions will accept greyscale and return rgb, which I believe is what you're after.
from matplotlib import pyplot as plt
from skimage.data import coins
coins().shape
>>> (303, 384)
rgb = plt.cm.nipy_spectral(coins())
rgb.shape
>>> (303, 384, 4) # now an RGBA array

In case anybody else is looking, I found my answer here.
You can simply apply a cmap on the numpy array as follows:
colormap=plt.cm.gray
colormapped_image = colormap(image).
But as stated in the link you have to apply normalization to the image beforehand.

Related

Converting numpy array to picture

So I have got a string of characters and I am representing it by a number between 1-5 in a numpy array. Now I want to convert it to a pictorial form by first repeating the string of numbers downwards so the picture becomes broad enough to be visible (since single string will give a thin line of picture). My main problem is how do I convert the array of numbers to a picture?
This would be a minimal working example to visualize with matplotlib:
import numpy as np
import matplotlib.pyplot as plt
# generate 256 by 1 vector of values 1-5
img = np.random.randint(1,6, 256)
# transpose for visualization
img = np.expand_dims(img, 1).T
# force aspect ratio
plt.imshow(img, aspect=100)
# or, alternatively use aspect='auto'
plt.show()
You can force the aspect ratio of the plotted figure by simply setting the aspect option of imshow()

Is there any good color map to convert gray-scale image to colorful ones using python's PIL? [duplicate]

This question already has answers here:
How to convert a NumPy array to PIL image applying matplotlib colormap
(3 answers)
Closed 4 years ago.
Matplotlib has a lot of good color maps, but is bad in performance. I'm writing some code to make gray-scale image colorful where interpolate with color map is a good idea. I wonder whether there are open source color maps available or demo code to use Pillow to convert gray-scale images into colorful ones via colormap?
Clarify:
Matplotlib is good for demo use, but bad performace for thounsands of images.
Matplotlib colormaps
You can map grayscale images to colormaps to get colorful ones.
Demo:
The first image is grayscale, second is mapped in 'jet' cmap, third being 'hot'.
The problem is that I do not know much about colors, and I'd like to achieve such effects in PIL for better performance.
You can use the color maps from matplotlib and apply them without any matplotlib figures etc.
This will make things much faster:
import matplotlib.pyplot as plt
# Get the color map by name:
cm = plt.get_cmap('gist_rainbow')
# Apply the colormap like a function to any array:
colored_image = cm(image)
# Obtain a 4-channel image (R,G,B,A) in float [0, 1]
# But we want to convert to RGB in uint8 and save it:
Image.fromarray((colored_image[:, :, :3] * 255).astype(np.uint8)).save('test.png')
Note:
If your input image is float, the values should be in the interval [0.0, 1.0].
If your input image is integer, the integers should be in the range [0, N) where N is the number of colors in the map. But you can resample the map to any number of values according to you needs:
# If you need 8 color steps for an integer image with values from 0 to 7:
cm = plt.get_cmap('gist_rainbow', lut=8)
I figured out with the duplicate answer mentioned by #ImportanceOfBeingErnest (How to convert Numpy array to PIL image applying matplotlib colormap)
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import timeit
from PIL import Image
def pil_test():
cm_hot = mpl.cm.get_cmap('hot')
img_src = Image.open('test.jpg').convert('L')
img_src.thumbnail((512,512))
im = np.array(img_src)
im = cm_hot(im)
im = np.uint8(im * 255)
im = Image.fromarray(im)
im.save('test_hot.jpg')
def rgb2gray(rgb):
return np.dot(rgb[:,:,:3], [0.299, 0.587, 0.114])
def plt_test():
img_src = mpimg.imread('test.jpg')
im = rgb2gray(img_src)
f = plt.figure(figsize=(4, 4), dpi=128)
plt.axis('off')
plt.imshow(im, cmap='hot')
plt.savefig('test2_hot.jpg', dpi=f.dpi)
plt.close()
t = timeit.timeit(pil_test, number=30)
print('PIL: %s' % t)
t = timeit.timeit(plt_test, number=30)
print('PLT: %s' % t)
The performance result is:
PIL: 1.7473899199976586
PLT: 10.632971412000188
They both give me similar result with hot color map.

How can I prevent Numpy/ SciPy gaussian blur from converting image to grey scale?

I want to perform gaussian blur on an image but I don't want to be convert to grey scale. Is there anyway to perform this operation and keep the color?
from scipy import misc
import scipy
import numpy as np
a = misc.imread('A.jpg')
# A retains its color
misc.imsave('color.jpg', a)
# A_G_Blur gets converted to grey scale, I want to prevent this
a_g_blure = ndimage.uniform_filter(a, size=11)
# I want it to keep it's color
misc.imsave('now_grey.jpg', a)
a is a 3-d array with shape (M, N, 3). The problem is that ndimage.uniform_filter(a, size=11) applies a filter with length 11 to each dimension of a, include the third axis that holds the color channels. When you apply the filter with length 11 to an axis with length 3, the resulting values are all pretty close to the average of the three values, so you get something pretty close to a gray scale. (Depending on the image, you might have some color left.)
What you actually want is to apply a 2-d filter to each color channel separately. You can do this by giving a tuple as the size argument, using a size of 1 for the last axis:
a_g_blure = ndimage.uniform_filter(a, size=(11, 11, 1))
Note: uniform_filter is not a Gaussian blur. For that, you would use scipy.ndimage.gaussian_filter. You might also be interested in the filters provided by scikit-image. In particular, see skimage.filters.gaussian_filter.
For a gaussian blur, I recommend using skimage.filters.gaussian_filter.
from skimage.io import imread
from skimage.filters import gaussian_filter
sigma=5 # blur radius
img = imread('path/to/img')
# this will only return grayscale
grayscale_blur = gaussian_filter(src_img, sigma=sigma)
# passing multichannel param as True returns colors
color_blur = gaussian_filter(src_img, sigma=sigma, multichannel=True)

matplotlib imshow plots different if using colormap or RGB array

I am having the following problem: I am saving 16-bit tiff images with a microscope and I need to analyze them. I want to do that with numpy and matplotlib, but when I want to do something as simple as plotting the image in green (I will later need to superpose other images), it fails.
Here is an example when I try to plot the image either as a RGB array, or with the default jet colormap.
import numpy as np
import matplotlib.pyplot as plt
import cv2
imageName = 'image.tif'
# image as luminance
img1 = cv2.imread(imageName,-1)
# image as RGB array
shape = (img1.shape[0], img1.shape[1], 3)
img2 = np.zeros(shape,dtype='uint16')
img2[...,1] += img1
fig = plt.figure(figsize=(20,8))
ax1 = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2)
im1 = ax1.imshow(img1,interpolation='none')
im2 = ax2.imshow(img2,interpolation='none')
fig.show()
Which to me yields the following figure:
I am sorry if the question is too basic, but I have no idea why the right plot is showing this artifacts. I would like to get with the green scale, something like how the figure looks (imageJ also yields somthing similar to the left plot).
Thank you very much for your collaboration.
I find the right plot much more artistic...
matplotlib is rather complicated when it comes to interpreting images. It goes roughly as follows:
if the image is a NxM array of any type, it is interpreted through the colormap (autoscale, if not indicated otherwise). (In principle, if the array is a float array scaled to 0..1, it should be interpreted as a grayscale image. This is what the documentation says, but in practice this does not happen.)
if the image is a NxMx3 float array, the RGB components are interpreted as RGB components between 0..1. If the values are outside of this range, they are taken with positive modulo 1, i.e. 1.2 -> 0.2, -1.7 -> 0.3, etc.
if the image is a NxMx3 uint8 array, it is interpreted as a standard image (0..255 components)
if the image is NxMx4, the interpretation is as above, but the fourth component is the opacity (alpha)
So, if you give matplotlib a NxMx3 array of integers other than uint8 or float, the results are not defined. However, by looking at the source code, the odd behavour can be understood:
if A.dtype != np.uint8:
A = (255*A).astype(np.uint8)
where A is the image array. So, if you give it uint16 values 0, 1, 2, 3, 4..., you get 0, 255, 254, 253, ... Yes, it will look very odd. (IMHO, the interpretation could be a bit more intuitive, but this is how it is done.)
In this case the easiest solution is to divide the array by 65535., and then the image should be as expected. Also, if your original image is truly linear, then you'll need to make the reverse gamma correction:
img1_corr = (img1 / 65535.)**(1/2.2)
Otherwise your middle tones will be too dark.
I approached this by normalising the image by the maximum value of the given datatype, which said by DrV, for uint16 is 65535. The helper function would look something like:
def normalise_bits(img):
bits = 1.0 # catch all
try:
# Test integer value, e.g. np.uint16
bits = np.iinfo(img.dtype).max
except ValueError:
# Try float maximum, e.g. np.float32
bits = np.finfo(img.dtype).max
return (img / bits).astype(float)
Then the image can be handled by matplotlib as a float [0.0, 1.0]

How to convert a NumPy array to PIL image applying matplotlib colormap

I have a simple problem, but I cannot find a good solution to it.
I want to take a NumPy 2D array which represents a grayscale image, and convert it to an RGB PIL image while applying some of the matplotlib colormaps.
I can get a reasonable PNG output by using the pyplot.figure.figimage command:
dpi = 100.0
w, h = myarray.shape[1]/dpi, myarray.shape[0]/dpi
fig = plt.figure(figsize=(w,h), dpi=dpi)
fig.figimage(sub, cmap=cm.gist_earth)
plt.savefig('out.png')
Although I could adapt this to get what I want (probably using StringIO do get the PIL image), I wonder if there is not a simpler way to do that, since it seems to be a very natural problem of image visualization. Let's say, something like this:
colored_PIL_image = magic_function(array, cmap)
Quite a busy one-liner, but here it is:
First ensure your NumPy array, myarray, is normalised with the max value at 1.0.
Apply the colormap directly to myarray.
Rescale to the 0-255 range.
Convert to integers, using np.uint8().
Use Image.fromarray().
And you're done:
from PIL import Image
from matplotlib import cm
im = Image.fromarray(np.uint8(cm.gist_earth(myarray)*255))
with plt.savefig():
with im.save():
input = numpy_image
np.uint8 -> converts to integers
convert('RGB') -> converts to RGB
Image.fromarray -> returns an image object
from PIL import Image
import numpy as np
PIL_image = Image.fromarray(np.uint8(numpy_image)).convert('RGB')
PIL_image = Image.fromarray(numpy_image.astype('uint8'), 'RGB')
The method described in the accepted answer didn't work for me even after applying changes mentioned in its comments. But the below simple code worked:
import matplotlib.pyplot as plt
plt.imsave(filename, np_array, cmap='Greys')
np_array could be either a 2D array with values from 0..1 floats o2 0..255 uint8, and in that case it needs cmap. For 3D arrays, cmap will be ignored.

Categories