How to create surface plot from greyscale image with Matplotlib? - python

Let's say I have a greyscale image (size: 550x150 px). I load the image with matplolib
import matplotlib.pyplot as plt
import matplotlib.image as mp_img
image = mp_img.imread("my-cat.png")
plt.imshow(image)
plt.show()
Now, plt.imshow displays the image on the screen. But what I want is a surface plot of the greyscale values, something like this:
.Colour is not really a necessity, but it would be helpful for the height lines. I know, that I need a function of the form f(x,y) -> z to create the surface plot. So, I want to use the greyscale value at (x_pixel,y_pixel) in my image to get the value of f. This leads to my problem:
I'd like to do some interpolation (e.g. smoothing) of my image values during plotting. This depends also on the size of my meshgrid, so how do I control this? And,
how do I make a surface plot of the greyscale values from my image?

So this is pretty straightforward. Load the data, build the plot:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# generate some sample data
import scipy.misc
lena = scipy.misc.lena()
# downscaling has a "smoothing" effect
lena = scipy.misc.imresize(lena, 0.15, interp='cubic')
# create the x and y coordinate arrays (here we just use pixel indices)
xx, yy = np.mgrid[0:lena.shape[0], 0:lena.shape[1]]
# create the figure
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(xx, yy, lena ,rstride=1, cstride=1, cmap=plt.cm.gray,
linewidth=0)
# show it
plt.show()
Result:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import cv2
# generate some sample data
import scipy.misc
lena = cv2.imread("./data/lena.png", 0)
# downscaling has a "smoothing" effect
lena = cv2.resize(lena, (100,100))
# create the x and y coordinate arrays (here we just use pixel indices)
xx, yy = np.mgrid[0:lena.shape[0], 0:lena.shape[1]]
# create the figure
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(xx, yy, lena ,rstride=1, cstride=1, cmap=plt.cm.jet,
linewidth=0)
# show it
plt.show()
If you want to get color plot, change the code to: "cmap=plt.cm.jet".
So you can get something like this:
color plot

Related

Matplotlib - Aligning grids

I'm plotting histograms below the images using a Matplotlib.GridSpec as we can see on code below:
import imageio
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
plt.close('all')
plt.style.use('ggplot')
img = imageio.imread('imageio:page.png')
y = np.bincount(img.ravel(), minlength=256)
y = y/np.sum(y)
x = np.arange(len(y))
fig = plt.figure(figsize=(6,8))
gs = gridspec.GridSpec(2, 1, height_ratios=[6,1], width_ratios=[1])
ax0 = plt.subplot(gs[0])
ax0.imshow(img, cmap='gray')
ax0.xaxis.set_visible(False)
ax0.yaxis.set_visible(False)
ax1 = plt.subplot(gs[1])
ax1.fill_between(x, y)
ax1.yaxis.set_visible(False)
ax1.set_xlim([0,255])
fig.tight_layout()
plt.show()
When we pick the correct figure size the image is nicely aligned as in
But if the figure size isn't correctly chosen the histogram is shown too large for image size or too far away as we can see below
or
Is there any way to tell matplotlib to align correctly, that is, put the histogram a fixed amount of pixels below the image and never stretch the histogram larger than image width.

matplotlib plot_surface 3D plot with non-linear color map

I have this following python code, which displays the following 3D plot.
My code is:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
# Generate data example
X,Y = np.meshgrid(np.arange(-99,-90), np.arange(-200,250,50))
Z = np.zeros_like(X)
Z[:,0] = 100.
Z[4][7] = 10
# Normalize to [0,1]
Z = (Z-Z.min())/(Z.max()-Z.min())
colors = cm.viridis(Z)
rcount, ccount, _ = colors.shape
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rcount=rcount, ccount=ccount,
facecolors=colors, shade=False)
surf.set_facecolor((0,0,0,0))
plt.show()
I want to color the irregularities on the XY plane in a different color. I want to be able to highlight the bumps on the XY plane.
How do I do that?
The problem is that the grid is not very dense. The bump consist of a single pixel. So there are 4 cells in the grid, 3 of which have their lower left corner at 0, and would hence not receive a different color according to their value. Only the one pixel which actually is the bump gets colorized.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
X,Y = np.meshgrid(np.arange(-99,-90), np.arange(-200,250,50))
Z = np.zeros_like(X)
Z[:,0] = 100.
Z[4][7] = 10
norm = plt.Normalize(Z.min(),Z.min()+10 )
colors = cm.viridis(norm(Z))
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, facecolors=colors, shade=False)
surf.set_facecolor((0,0,0,0))
plt.show()
Now you may expand the colorized part of the plot, e.g. using scipy.ndimage.grey_dilation, such that all pixels that are adjacent also become yellow.
from scipy import ndimage
C = ndimage.grey_dilation(Z, size=(2,2), structure=np.ones((2, 2)))
norm = plt.Normalize(Z.min(),Z.min()+10 )
colors = cm.viridis(norm(C))

Matplotlib: Plot 2d array radially to make 3d Scatterplot

I am looking to make a plot similar to the one found here, with the simple difference that I would like to to set the distance from the center for each point. Ie, given a slice of the plot is a circle, where I would like each of the points to be at a definable distance from the center.
What I a starting with, given a simple modification of the previously mentioned answer:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib
import numpy as np
from scipy.interpolate import interp1d
from matplotlib import cm
from matplotlib import pyplot as plt
step = 0.04
maxval = 1.0
fig = plt.figure()
ax = Axes3D(fig)
# u here would define the desired distance from radial axis
# u=np.array([0,1,2,1,0,2,4,6,4,2,1])
v=np.array([4,4,6,3,6,4,1,4,4,4,4])
r=np.array([0,1,2,3,4,5,6,7,8,9,10])
f=interp1d(r,u)
# walk along the circle
p = np.linspace(0,2*np.pi,len(r))
R,P = np.meshgrid(r,p)
# transform them to cartesian system
X,Y = R*np.cos(P),R*np.sin(P)
Z=f(R)
ax.scatter(X, Y, Z)#, rstride=1, cstride=1, cmap=cm.jet)
ax.set_xticks([])
fig.savefig(str(output_prefix + '3d..png'), dpi=(200))
What I would like to plot (apologies for the blurred sketch):
I have tried using interp2d to add the u variable commented out above, but no luck. Changing Z to the array u threw the error that X, Y, and Z must be the same size ("Argument 'zs' must be of same size as 'xs' ", understandably as X and Y are now interpolated) What do I need to do? Any tips would be appreciated!
I don't know exactly what you meant in your question.
I made v to be the offset of the center of the circles in x-axis.
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from scipy.interpolate import interp1d
from matplotlib import pyplot as plt
step = 0.04
maxval = 1.0
fig = plt.figure()
ax = Axes3D(fig)
# v here would define the desired distance from radial axis
u=np.array([0,1,2,1,0,2,4,6,4,2,1])
v=np.array([4,4,6,3,6,4,1,4,4,4,4])
r=np.array([0,1,2,3,4,5,6,7,8,9,10])
f=interp1d(r,u)
# walk along the circle
V = np.tile(v, (len(u), 1))
p = np.linspace(0,2*np.pi,len(r))
R,P = np.meshgrid(r,p)
# transform them to cartesian system
X,Y = V + R*np.cos(P),R*np.sin(P)
Z=f(R)
ax.scatter(X, Y, Z)#, rstride=1, cstride=1, cmap=cm.jet)
ax.set_xticks([])
plt.show()

3D plot of Meshgrid vs. Labeling Matrix

Suppose that we want to plot an n-by-n meshgrid using Axes3d. if we have an n-by-n labeling matrix containing 0 and 1, how can we set the meshgrid pixel colors according to the labeling matrix that we have in a 3D plot?
(Edited). There is a tutorial on matplotlib: http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html. Look up plot_surface function. All you need to do is to convert your labeling mask to favorite colors. You can either do it via tuple method described in the tutorial, or through pregenerated char matrices as I've done below:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import matplotlib.pyplot as plt
import numpy as np
N=10 # matrix size
c1=np.random.randint(2,size=(N,N)) # your mask of 0 and 1
c2=1-c1 # inverted mask
char1 = np.chararray((N, N))
char1[:] = 'r' # color1
char2=np.chararray((N, N))
char2[:]='b' # color2
colors=c1*char1+c2*char2 # color mask for plotting
data=np.random.rand(N,N)*5 # data you are plotting
X=linspace(-3,3,N)
Y=linspace(0,4,N)
X,Y=meshgrid(X,Y)
Z=data
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=colors,linewidth=0, antialiased=False)
plt.show()

Python - color a 3d line plot

I have a 3d line plot of the solar spectrum, which I plotted using the command,
from mpl_toolkits.mplot3d.axes3d import Axes3D
from matplotlib.collections import PolyCollection, LineCollection
from matplotlib.colors import colorConverter, ListedColormap, BoundaryNorm
import matplotlib.cm as cm
fig = plt.figure(figsize(15, 8))
ax = fig.gca(projection='3d')
x = SpectrumDF['Wavelength']
z = SpectrumDF['DNI']
y = SpectrumDF['TESTNUM']
ax.plot(x, y, z)
ax.set_xlabel('Wavelength')
ax.set_ylabel('Test Number')
ax.set_zlabel('Intensity')
The resultant plot is solid blue and takes whichever individual color I give in the function: plot( ).
I have been trying to create a color gradient along the z-axis, intensity, without any success.
I have around 500 test numbers, each has 744 data points.
Thank you for the help!
This wouldn't let me post images because I don't have enough reputation. Anyway, here's the link to the plot I get using this code https://plus.google.com/106871046257785761571/posts/fMYsDF5wAQa
Using the example - Line colour of 3D parametric curve in python's matplotlib.pyplot - I got a scatter plot with color gradient along the z axis - here's the link to the image of that plot - https://plus.google.com/u/0/106871046257785761571/posts/SHTsntgQxTw?pid=6133159284332945618&oid=106871046257785761571
I used the following command:
fig = plt.figure(figsize(15,8))
ax = fig.gca(projection='3d')
x = FilteredDF['Wavelength']
z = FilteredDF['DNI']
y = FilteredDF['TESTNUM']
ax.scatter(x, y, z, c=plt.cm.jet(z/max(z)))
ax.set_xlabel('Wavelength')
ax.set_ylabel('Test Number')
ax.set_zlabel('Intensity')
plt.show()
I am still working on getting a colored line plot because I have a lot of points, which makes scatter plot very slow to work with.
Thank you

Categories

Resources