How to save a greyscale matplotlib plot to numpy array - python

For example, I plot a figure using matplotlib as follows:
plt.figure(figsize=(10,10))
plt.imshow(output_fig, zorder=0,cmap="gray")
plt.scatter(x,y,color='k')
if I use:
plt.savefig(figname,fotmat=figtype)
I will save it as a figure file. However, I want so save it to a matrix, or numpy array, such that each element saves the scale value of each pixel of the figure.
How can I do this?
I find solutions saving the RGB values. But I hope to save a greyscale figure.
Thank you all for helping me!

Once you have a ploted data (self contained example bellow):
import numpy as np
import matplotlib.pyplot as plt
from skimage import data, color
img = data.camera()
x = np.random.rand(100) * img.shape[1]
y = np.random.rand(100) * img.shape[0]
fig = plt.figure(figsize=(10,10))
plt.imshow(img,cmap="gray")
plt.scatter(x, y, color='k')
plt.ylim([img.shape[0], 0])
plt.xlim([0, img.shape[1]])
The underlying data can be recovered as array by using fig.canvas (the matplotlib's canvas). First trigger its drawing:
fig.canvas.draw()
Get the data as array:
width, height = fig.get_size_inches() * fig.get_dpi()
mplimage = np.fromstring(fig.canvas.tostring_rgb(), dtype='uint8').reshape(height, width, 3)
If you want your array to be the same shape as the original image you will have to play with figsize and dpi properties of plt.figure().
Last, matplotlib returns an RGB image, if you want it grayscale:
gray_image = color.rgb2gray(mplimage)

Related

how to get the elevation of each points(pixel) in image using python

I am trying to get an elevation for each pixel in the image using image processing by python. My first try is by converting the image to grayscale and covert the 2d image to 3d image by using the following code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.pyplot import imread
imageFile = 'D:\Books\Pav Man\PICS\pic (17) - Copy.png'
mat = imread(imageFile)
mat = mat[:,:,0] # get the first channel
#mat = mat - np.full_like(mat , mat.mean()) #Use this to get negative value
rows, cols = mat.shape
xv, yv = np.meshgrid(range(cols), range(rows)[::-1])
blurred = ndimage.gaussian_filter(mat, sigma=(5, 5), order=0)
fig = plt.figure(figsize=(6,6))
ax = fig.add_subplot(221)
ax.imshow(mat, cmap='gray')
ax = fig.add_subplot(222, projection='3d')
ax.elev= 75
ax.plot_surface(xv, yv, mat)
ax = fig.add_subplot(223)
ax.imshow(blurred, cmap='gray')
ax = fig.add_subplot(224, projection='3d')
ax.elev= 75
ax.plot_surface(xv, yv, blurred)
plt.show()
The mat contains x,y,z values for each pixel, x = width coordinate , y = height coordinate , z = grayscale value that ranges from 0 to 1 but it does not include the real elevation.
The second try is by using depth data from 2 images as mentioned in the following link:
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_depthmap/py_depthmap.html
But there is no clear way to estimate or predict the elevation of points in the image.
The following picture describes what I mean:
click here to show picture
My question is how to get the elevation of each point in the image to create a topographic profile?

How to plot only some cells with certain values of an array and others not with matplotlib.pyplot?

I have an image that consists of float values and another one that consist only of ones and zeros. I want to plot the second image over the first one, but I only want to plot the ones from the second image. The zeros shall not be plotted.
Ì have tried the following code and I also changed the alpha of y to 1. The problem is, that either the red windows of y are changed from x (alpha of y = 0.5), or one can not even see the plots of x (alpha of y=1).
import matplotlib.pyplot as plt
import numpy as np
x = np.random.random(size=(20,20))
y = np.random.randint(2, size=(20,20))
fig = plt.figure()
plt.imshow(x, cmap="Greys", alpha = 0.5)
plt.imshow(y, cmap="Reds", alpha = 0.5)
plt.show()
How can I only plot the ones of y?
UPDATE:
Thank you for your answers! But this is not want I am looking for. I will explain again:
The result should be something like: x as background and every position, where y is 1, should be colored pure red.
Following the approach in this answer linked by #ImportanceOfBeingEarnest, the exact solution in your case would look like below. Here, np.ma.masked_where will mask your y array at places where it is 0. The resulting array will only contain 1.
EDIT: The problem of overlaying seems to stem from the choice of cmap. If you don't specify the cmap for the y, you can clearly see below that indeed only 1's are plotted and overlaid on the top of x. In order to have a discrete color (red in your case), you can create a custom color map
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import colors
x = np.random.random(size=(20,20))
y = np.random.randint(2, size=(20,20))
y_new =np.ma.masked_where(y==0, y)
cmap = colors.ListedColormap(['red'])
fig = plt.figure()
plt.imshow(x, cmap="Greys", alpha = 0.5)
plt.imshow(y_new, cmap=cmap, alpha=1)
plt.show()
We can inverse "The result should be [..] x as background and every position, where y is 1, should be colored pure red.", namely to just plot x, masked by y and set the background to red.
import matplotlib.pyplot as plt
import numpy as np
y = np.random.randint(2, size=(20,20))
x = np.random.random(size=(20,20))
X = np.ma.array(x, mask=y)
fig = plt.figure()
plt.imshow(X, cmap="Greys")
plt.gca().set_facecolor("red")
plt.show()
There are of course related Q&As like
Matplotlib imshow: how to apply a mask on the matrix or
How can I plot NaN values as a special color with imshow in matplotlib?
and there is also an example on the matplotlib page: Image masked

Add color scale to matplotlib colorbar according to RGBA image channels

I am trying to plot a RGBA image with a colorbar representing color values.
The RGBA image is generated from raw data, transforming the 2d data array into a 6d-array with x, y, [R, G, B and A] according to the color input. E.g. 'green' will make it fill just the G channel with the values from the 2d-array, leaving R and B = 0 and A = 255. Like this:
All solutions I found would apply a color map or limit the vmin and vmax of the colorbar but what I need is a colorbar that goes from pitch black to the brightest color present in the image. E.g. if I have an image in shades of purple, the color bar should go from 0 to 'full' purple with only shades of purple in it. The closest solution I found was this (https://pelson.github.io/2013/working_with_colors_in_matplotlib/), but it doesn't fit a "general" solution.
An image I'm getting is given below.
import numpy as np
from ImgMath import colorize
import matplotlib.pyplot as plt
import Mapping
data = Mapping.getpeakmap('Au')
# data shape is (10,13) and len(data) is 10
norm_data = data/data.max()*255
color_data = colorize(norm_data,'green')
# color_data shape is (10,13,4) and len(color_data) is 10
fig, ax = plt.subplots()
im = plt.imshow(color_data)
fig.colorbar(im)
plt.show()
You could map your data with a custom, all-green, colormap
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# input 2D array
data = np.random.randint(0,255, size=(10,13))
z = np.zeros(256)
colors = np.linspace(0,1,256)
alpha = np.ones(256)
#create colormap
greencolors = np.c_[z,colors,z,alpha]
cmap = ListedColormap(greencolors)
im = plt.imshow(data/255., cmap=cmap, vmin=0, vmax=1)
plt.colorbar(im)
plt.show()

Combine picture and plot with matplotlib with alpha channel

I have a .png image with alpha channel and a random pattern generated with numpy.
I want to supperpose both images using matplotlib. The bottom image must be the random pattern and over this, I want to see the second image (attached in the end of the post).
The code for both images is the following:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
# Random image pattern
fig = plt.subplots(figsize = (20,4))
x = np.arange(0,2000,1)
y = np.arange(0,284,1)
X,Y = np.meshgrid(x,y)
Z = 0.6+0.1*np.random.rand(284,2000)
Z[0,0] = 0
Z[1,1] = 1
# Plot the density map using nearest-neighbor interpolation
plt.pcolormesh(X,Y,Z,cmap = cm.gray)
The result is the following image:
To import the image, I use the following code:
# Sample data
fig = plt.subplots(figsize = (20,4))
# Plot the density map using nearest-neighbor interpolation
plt.imread("good_image_2.png")
plt.imshow(img)
print(img.shape)
The image is the following:
Thus, the final result that I want is:
You can make an image-like array for Z and then just use imshow to display it before the image of the buttons, etc. Note that this only works because your png has an alpha channel.
Code:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
# Plot the density map using nearest-neighbor interpolation
img = plt.imread("image.png")
(xSize, ySize, cSize) = img.shape
x = np.arange(0,xSize,1)
y = np.arange(0,ySize,1)
X,Y = np.meshgrid(x,y)
Z = 0.6+0.1*np.random.rand(xSize,ySize)
Z[0,0] = 0
Z[1,1] = 1
# We need Z to have red, blue and green channels
# For a greyscale image these are all the same
Z=np.repeat(Z,3).reshape(xSize,ySize,3)
fig = plt.figure(figsize=(20,8))
ax = fig.add_subplot(111)
ax.imshow(Z, interpolation=None)
ax.imshow(img, interpolation=None)
fig.savefig('output.png')
Output:
You can also turn off axes if you prefer.
ax.axis('off')

Matplotlib imshow zoom function?

I have several (27) images represented in 2D arrays that I am viewing with imshow(). I need to zoom in on the exact same spot in every image. I know I can manually zoom, but this is tedious and not precise enough. Is there a way to programmatically specify a specific section of the image to show instead of the entire thing?
You could use plt.xlim and plt.ylim to set the region to be plotted:
import matplotlib.pyplot as plt
import numpy as np
data=np.arange(9).reshape((3,3))
plt.imshow(data)
plt.xlim(0.5, 1.5)
plt.ylim(0.5,1.5)
plt.show()
If you do not need the rest of your image, you can define a function that crop the image at the coordinates you want and then display the cropped image.
Note: here 'x' and 'y' are the visual x and y (horizontal axis and vertical axis on the image, respectively), meaning that it is inverted compared to the real x (row) and y (column) of the NumPy array.
import scipy as sp
import numpy as np
import matplotlib.pyplot as plt
def crop(image, x1, x2, y1, y2):
"""
Return the cropped image at the x1, x2, y1, y2 coordinates
"""
if x2 == -1:
x2=image.shape[1]-1
if y2 == -1:
y2=image.shape[0]-1
mask = np.zeros(image.shape)
mask[y1:y2+1, x1:x2+1]=1
m = mask>0
return image[m].reshape((y2+1-y1, x2+1-x1))
image = sp.lena()
image_cropped = crop(image, 240, 290, 255, 272)
fig = plt.figure()
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
ax1.imshow(image)
ax2.imshow(image_cropped)
plt.show()

Categories

Resources