Converting numpy array to picture - python

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()

Related

Why does plt.imshow flip coordinates compared to plt.scatter?

The coordinate axis directions seem to be inverted between array indices and plots. Does someone have a satisfactory answer as to why matplotlib likes to display images inverted?
Code that demonstrates my question:
import numpy as np
import matplotlib.pyplot as plt
img = np.zeros((10, 10))
img[2, 4] = 1
plt.imshow(img)
plt.scatter(2, 4)
The output image will show a white pixel and a dot at positiones mirrored at the image diagonal.
This effect caused a lot of confusion for me (and others I have talked to) and I was wondering if there is a sensible explanation for it. To me, it seems that matplotlib interprets the first axis of a numpy array as the y-coordinate and the second as x. As this is not aligned with the mathematical x-y right hand notation this is confusing.

3D nrrd image visualization (matplotlib)?

I have 3D image data in nrrd format, where the array is of shape (300, 256, 256) (meaning I have a 256x256 image and 300 slices of it, adding up to a 3d image). The array after reading from nrrd saves the opacity info for each 3D point (e.g. imgarray[x][y][z] would equal a number between 0-255, it's only for opacity, e.g. no rgb colors (this is on purpose)).
I'm trying to visualize this with matplotlib:
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3dfig = plt.figure()
ax = plt.axes(projection='3d')
ax.scatter3D(`what do I put here?`)
However, matplotlib requires me to give x, y and z axes (plus a cmap), but my data is not in such format (if I do imgarray[0] that still contains an array of 256x256 opacity info).
I can easily view a slice of the 3D image by
plt.imshow(imgarray[100])
plt.show()
However, I'd like to view it in 3D. How can I do this?
Napari can render large multi-dimensional arrays using Maximum Intensity Projection, or as isosurfaces. It comes with a GUI that makes it easy to experiment with different display settings. (At time of writing it is still in alpha stage.)
Usage example:
import numpy as np
import napari
from skimage import data, filters # Just to generate some test data (3D blobs).
with napari.gui_qt():
# Generate some test data (smooth 3D blob shapes)
imgarray = filters.gaussian(np.squeeze(np.stack([data.binary_blobs(length=300, n_dim=3, blob_size_fraction=0.1, volume_fraction=0.05)[:, 0:256, 0:256]])).astype(float), sigma=(2.5, 2.5, 2.5))
print(imgarray.shape)
'''
# If imgarray values are bytes (0..255), convert to floats for display.
imgarray = imgarray.astype(float) / 255
'''
# Open viewer (Qt window) with axes = slice, row, column
viewer = napari.Viewer(title='volume test', ndisplay=3, order=(0, 1, 2))
viewer.add_image(data=imgarray, name='blobs', scale=[256/300, 1, 1], colormap='gray_trans', rendering='attenuated_mip', attenuation=2.0, contrast_limits=(0.25, 1))

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