How do I display an image in a plot using Python? - python

I imported matplotlib.pyplot and also NumPy
I wanted to display an image from my desktop to the plot but I get a TypeError.
code :
img = (image) ( here do we need to give the location of the file or the file directly)
imshow(img, extent=[-25,25,-25,25], cmap = cm.bone)
colorbar()
Error: TypeError: Image data can not convert to float
I am using Pycharm as my ide.

You are a bit ambiguous about
here do we need to give the location of the file or the file directly
No you don't. You need to use some imaging library to read an image. img="C:\image.jpg" does not read an image!
For example, to read a 'png' image, you could:
# Copypaste from docs
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
img=mpimg.imread('myimage.png')
# end
# from now on you can use img as an image, but make sure you know what you are doing!
imgplot=plt.imshow(img)
plt.show()
Read more at Image tutorial at matplotlib's doc

Is img a numpy array of the right type?
If you read the image using pillow,etc. and have an Image object you have to get the numpy array from it ( img.getdata() )
X : array_like, shape (n, m) or (n, m, 3) or (n, m, 4)
Display the image in X to current axes. X may be a float array, a
uint8 array or a PIL image. If X is an array, it can have the
following shapes:
MxN – luminance (grayscale, float array only) MxNx3 – RGB (float or
uint8 array) MxNx4 – RGBA (float or uint8 array)
The value for each component of MxNx3 and MxNx4 float arrays should be in the range 0.0 to 1.0;
Either normalize img so it's between 0.0 and 1.0 or convert it to uint8 ( img=np.array(img, dtype=np.uint8) ).

Related

How to correctly convert RGB images into nifty format?

I have RGB images which I'm loading as a numpy array. I would like to convert these images into the nifty format, which I can open using ITK-SNAP (http://www.itksnap.org/pmwiki/pmwiki.php).
Here is what I have tried to do:
import nibabel as nib
import numpy as np
x = load_jpg_image(filename='input.jpg') # --> x is a numpy array containing the RGB image with shape (128, 128, 3)
img = nib.Nifti1Image(x, eye(4))
nib.save(img, filename='output.nii')
However, ITK-SNAP interprets output.nii as a 3D grayscale volume rather than an RGB image. To me, it seems that ITK-SNAP should be able to handle RGB data (see this); however, I don't understand how I should save img to make this possible. I'm using ITK-SNAP 3.6.0.
unfortunately NIfTI was never really overly developed for RGB images. You can see in the latest NIfTI2 spec, the RGB and RGBA voxel types are defined (RGB having 3 bytes per pixel, RGBA 4 bytes) but I'm not aware of any tools that process these images.
The difference with your case is that the dimensions of the images are the number of pixels and the colour channels are within the pixel. It looks like ITK-snap displays colour NIfTI images correctly from version 2 -- I guess they follow this format.
It seems you can create RGB images by casting them in a custom dtype:
import nibabel as nib
import numpy as np
RGB_DTYPE = np.dtype([('R', 'u1'), ('G', 'u1'), ('B', 'u1')])
x = load_jpg_image(filename='input.jpg') # --> x is a numpy array containing the RGB image with shape (128, 128, 3)
# cast to custom type:
x = x.copy().view(dtype=RGB_DTYPE) # copy used to force fresh internal structure
img = nib.Nifti1Image(x, eye(4))
nib.save(img, filename='output.nii')
ITK-SNAP can handle this type of image by right-clicking the image name on the left panel and selecting the option: Multi-Component Display -> RGB.

Convert 1D Numpy Array into a 1D image using PIL

PIL returns IndexError: tuple index out of range when converting a 1D numpy array into an PIL image object.
I am trying to covert a 1D Numpy Array of length 2048 having value between 0 and 255 into an image using PIL. I think this is an issue with my array being 1D. I have also tried converting a random 1D array integer to an image and I get the same error.
Random integer example:
from PIL import Image
import numpy as np
arr = np.random.randint(255, size=(2048))
arr = arr.astype('uint8')
img = Image.fromarray(arr, 'L')
img.show()
I would expect the code to show an image of a singe line of pixels having varying shades of gray.
When I tried to run your code, the problem was just that your array was a 1D array. So try:
arr2d = arr.reshape(-1,1)
Image.fromarray(arr2d,'L').show()
The input array has to be 2D, even if one dimension is 1. You just need to decide if you want the image to be a horizontal or vertical row of pixels, and add a dimension when creating your array.
arr = np.random.randint(255, size=(2048, 1)) # vertical image
arr = np.random.randint(255, size=(2048, 1)) # horizontal image

Preserve 3-dimensionality when converting PIL image to numpy array

I have a list of PIL images: p0, p1, ..., p85999 (a total of 86000 of them). They are all RGB, of size 30x30px.
I need to convert them to normalized numpy arrays, I did the following:
[np.asarray(r).astype('float32') / 255.0) for r in images]
where r is a PIL image.
This gives an array of numpy arrays.
However, these arrays are sometimes of shape (30,30,3) and sometimes of shape (30,30).
I want them always to be of shape (30,30,3).
I'm guessing numpy does this for performance reasons (when RGB is not
needed, eg. white images?).
Anyway, how to get the desired result - get all numpy arrays to be of size (30,30,3)?
Also, ideally I would want my final numpy array to be of size (30, 30, 3, 86000). Is there a shortcut to create such an array straight from PIL images?
I'm guessing numpy does this for performance reasons
Numpy has nothing to do with it, this is your PIL Image having one channel only.
The simplest solution is to just convert everything to RGB:
ims = [np.asarray(r.convert('RGB')).astype('float32') / 255.0) for r in images]
If you then call np.asarray(ims), you'll obtain an array of shape [N,30,30,3] where N is the number of images, which you can then transpose to your desired ordering.

matlplotlib with opencv gives a different image with the same pixel values

I've been trying to modify the pixels of an image slightly, but the colors are getting distorted. So, I multiplied every pixel with 1 and saw the result.
Here's my code
import numpy as np
from matplotlib import pyplot as plt
import cv2
mud1 = cv2.imread('mud.jpeg')
print mud1.shape
mask = np.random.random_integers(1,1,size=(81,81,3))
print mask.shape
print mask[21][21][2]
print mud1[21][21][2]
mud1new = np.multiply(mud1,mask)
print mud1new[21][21][2]
plt.subplot(121),plt.imshow(mud1),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(mud1new),plt.title('Masked')
plt.xticks([]), plt.yticks([])
plt.savefig('problem.jpeg')
plt.show()
The pixels remain unchanged but somehow the image I see is different.
The issue is because np.random.random_integers returns objects that are int64 whereas your loaded image is uint8 so when you multiply the two together, mud1new becomes an int64 array. When using imshow, it expects the following types
MxN – values to be mapped (float or int)
MxNx3 – RGB (float or uint8)
MxNx4 – RGBA (float or uint8)
To fix this, you should cast mud1new as a uint8 prior to display with imshow
mud1new = mud1new.astype(np.unit8)
plt.imshow(mud1new)
You could also convert mud1new to a float but that would require that all of your values should be between 0 and 1 so you'd have to divide everything by 255.
The value for each component of MxNx3 and MxNx4 float arrays should be in the range 0.0 to 1.0.
mud1new_float = mud1new.astype(np.float) / 255.0;
plt.imshow(mud1new_float)

how to save an array as a grayscale image with matplotlib/numpy?

I am trying to save a numpy array of dimensions 128x128 pixels into a grayscale image.
I simply thought that the pyplot.imsave function would do the job but it's not, it somehow converts my array into an RGB image.
I tried to force the colormap to Gray during conversion but eventhough the saved image appears in grayscale, it still has a 128x128x4 dimension.
Here is a code sample I wrote to show the behaviour :
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mplimg
from matplotlib import cm
x_tot = 10e-3
nx = 128
x = np.arange(-x_tot/2, x_tot/2, x_tot/nx)
[X, Y] = np.meshgrid(x,x)
R = np.sqrt(X**2 + Y**2)
diam = 5e-3
I = np.exp(-2*(2*R/diam)**4)
plt.figure()
plt.imshow(I, extent = [-x_tot/2, x_tot/2, -x_tot/2, x_tot/2])
print I.shape
plt.imsave('image.png', I)
I2 = plt.imread('image.png')
print I2.shape
mplimg.imsave('image2.png',np.uint8(I), cmap = cm.gray)
testImg = plt.imread('image2.png')
print testImg.shape
In both cases the results of the "print" function are (128,128,4).
Can anyone explain why the imsave function is creating those dimensions eventhough my input array is of a luminance type?
And of course, does anyone have a solution to save the array into a standard grayscale format?
Thanks!
With PIL it should work like this
from PIL import Image
I8 = (((I - I.min()) / (I.max() - I.min())) * 255.9).astype(np.uint8)
img = Image.fromarray(I8)
img.save("file.png")
There is also an alternative of using imageio. It provides an easy and convenient API and it is bundled with Anaconda. It can save grayscale images as a single color channel file.
Quoting the documentation
>>> import imageio
>>> im = imageio.imread('imageio:astronaut.png')
>>> im.shape # im is a numpy array
(512, 512, 3)
>>> imageio.imwrite('astronaut-gray.jpg', im[:, :, 0])
I didn't want to use PIL in my code and as noted in the question I ran into the same problem with pyplot, where even in grayscale, the file is saved in MxNx3 matrix.
Since the actual image on disk wasn't important to me, I ended up writing the matrix as is and reading it back "as-is" using numpy's save and load methods:
np.save("filename", image_matrix)
And:
np.load("filename.npy")
There is also a possibility to use scikit-image, then there is no need to convert numpy array into a PIL object.
from skimage import io
io.imsave('output.tiff', I.astype(np.uint16))

Categories

Resources