Using matplotlib is it possible to take a 2D image of something and place it in a 3D figure? I'd like to take a 2D image and place it at z position of 0. I want to then move the other pixels in the image along the z-axis separately based on a calculation I am making.
Look for example: https://matplotlib.org/gallery/mplot3d/2dcollections3d.html
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
# Plot a sin curve using the x and y axes.
x = np.linspace(0, 1, 100)
y = np.sin(x * 2 * np.pi) / 2 + 0.5
ax.plot(x, y, zs=0, zdir='z', label='curve in (x,y)')
# Plot scatterplot data (20 2D points per colour) on the x and z axes.
colors = ('r', 'g', 'b', 'k')
# Fixing random state for reproducibility
np.random.seed(19680801)
x = np.random.sample(20 * len(colors))
y = np.random.sample(20 * len(colors))
c_list = []
for c in colors:
c_list.extend([c] * 20)
# By using zdir='y', the y value of these points is fixed to the zs value 0
# and the (x,y) points are plotted on the x and z axes.
ax.scatter(x, y, zs=0, zdir='y', c=c_list, label='points in (x,z)')
# Make legend, set axes limits and labels
ax.legend()
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.set_zlim(0, 1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# Customize the view angle so it's easier to see that the scatter points lie
# on the plane y=0
ax.view_init(elev=20., azim=-35)
plt.show()
If your image is a coloured image you must first ensure that it is an indexed image. This means that you can only have 2d matrix (and not 3 matricies for the RGB components). Command rgb2ind can help.
Then you can directly show you image in a 3D way. Use the mesh or surf command.
You can also adjust perspective with angle and azimuth.
Related
To illustrate an optimization problem, I want all of this in the same 3D plot:
A surface.
A curve in the xy-plane.
A curve/path on the surface which marks out the points on the surface that lies directly above the curve in the xy-plane.
This is my code so far:
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import numpy as np
from mpl_toolkits import mplot3d
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
X = np.linspace(-5,5,100)
Y = X
X, Y = np.meshgrid(X, Y)
Z = 50 - X**2 - Y**2
#Plotting curve on the surface
ax = plt.axes(projection='3d')
yline = np.linspace(-5,5,100)
xline = -np.sqrt(4/(2+yline**2)) #the x-values of the curve in the xy-plane
zline = 50 - xline**2 - yline**2
ax.plot3D(xline, yline, zline, "black")
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm)
ax.set_zlim(0, 50)
#Plotting curve in xy-plane
a = 5
g = 1 - 2*X - X*Y**2
plt.contour(X,Y,g, [a], offset=0)
plt.show()
Here is the plot from two different angles:
Some problems:
First of all, it seems like the axes have been numbered twice. Is that because I make a meshgrid, and later on use ax.plot3D? That I use two different ways of plotting something, and as a consequence make the 3D space twice?
The path on the surface appears weakly. Is there a way to make the path more visible?
From the picture in bird perspective, we see that the path does not lie directly above the curve in the xy-plane. What would be easier, was if Python had a built-in function who could project the curve in the xy-plane directly onto the surface. Am I missing something here? Does anyone know of such a function?
These questions might be dummy questions, but answers and tips are highly appreciated!
The code creates two axes objects (both assigned to the ax variable) in the same figure. This is not needed and results in double ticks marks.
To make the path on the surface more visible, plot it with a higher zorder.
The curve on the surface does not overlap with the curve on the xy plane because these are different curves. To plot the projection of the surface curve on the xy plane, set all z-coordinates of the curve to 0.
Below is the code with these changes.
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import numpy as np
from mpl_toolkits import mplot3d
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
X = np.linspace(-5, 5, 100)
Y = X
X, Y = np.meshgrid(X, Y)
Z = 50 - X**2 - Y**2
yline = np.linspace(-5, 5, 100)
xline = -np.sqrt(4 / (2 + yline**2))
zline = 50 - xline**2 - yline**2
ax.plot3D(xline, yline, zline, "b", zorder=10)
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, alpha=0.7)
ax.set_zlim(0, 50)
#Plotting curve in xy-plane
ax.plot3D(xline, yline, 0, "k")
plt.show()
How to plot a x versus y line? By x versus y, I mean how to plot x vs y line if the x and y axes have already fixed, as if the axes are reversed for this line.
Update:
Some one asked me why not just reverse the arguments and axes labels. Here is my reason: this x vs y line is only a part of a 2D plot (the main plot) and the main axes are for the 2D plot. What's more, there are also y vs x lines in the same 2D plot. I do this because I want to show certain line clearly.
Update:
Here is a example what I want:
I want to plot the black line in the figure which I draw manually (actually I want to draw Gaussian curve). It is time vs voltage. I still want to keep the existed blue line and I should not reverse the time/voltage labels.
You can easily plot multiple curves in the same subplot in matplotlib. As an example see this annotated code:
import matplotlib.pyplot as plt
import numpy as np
# Data for plotting
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)
# Note that using plt.subplots below is equivalent to using
# fig = plt.figure() and then ax = fig.add_subplot(111)
fig, ax = plt.subplots()
#plot sine wave
ax.plot(t, s, label = "sine wave")
#now create y values for the second plot
y = np.linspace(0, 2, 1000)
#calculate the values for the Gaussian curve
x = 2 * np.exp(-0.5 * np.square(-4 * (y - 1)))
#plot the Gaussian curve
ax.plot(x, y, label = "Gaussian curve")
ax.set(xlabel='time (s)', ylabel='voltage (mV)',
title='About as simple as it gets, folks')
ax.grid()
#show the legend
plt.legend()
plt.show()
Output:
How can I make a scatter plot colored by density in matplotlib?
When I plot a colorbar it shows density scale, I want counts/percentage instead. How to convert density estimation to frequency counts?
Expected result is Fig.3 on page 8 of this paper: https://www.atmos-meas-tech.net/9/3293/2016/amt-9-3293-2016.pdf
If anyone can guide me to draw a plot similar to one shown in paper, it will be really helpful. Thank you in advance.
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
# Generate fake data
x = np.random.normal(size=1000)
y = x * 3 + np.random.normal(size=1000)
# Calculate the point density
xy = np.vstack([x,y])
z = gaussian_kde(xy)(xy)
fig, ax = plt.subplots()
cax=ax.scatter(x, y, c=z, s=10, cmap=plt.cm.jet)
cbar = fig.colorbar(cax)
plt.show()
Another Method Tried:
#libraries
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import kde
# create data
x = np.random.normal(size=500)
y = x * 3 + np.random.normal(size=500)
# Evaluate a gaussian kde on a regular grid of nbins x nbins over
nbins=50
k = kde.gaussian_kde([x,y])
xi, yi = np.mgrid[min(x):max(x):nbins*1j, min(y):max(y):nbins*1j]
zi = k(np.vstack([xi.flatten(), yi.flatten()]))
# Add color bar
plt.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=plt.cm.jet)
plt.colorbar()
plt.show()
That's not a histogram, it's just sampling your kde on a grid...
Try this: plt.hist2d(x, y)
you can specify the bins using bins=whatever argument and many more options...
I am trying to draw helix (shape of spring). I was able to draw a single helix using axes3D and matplotlib.
Below is my code:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import rcParams
import matplotlib.pyplot as plt
import numpy as np
theta = np.linspace(-9 * np.pi, 9 * np.pi, 300)
radius = 5.0
x = radius*np.cos(theta)
x=[]
for i in theta:
if (i < 4.5* np.pi):
x.append(radius*np.cos(i))
else:
x.append((radius+2.0) * np.cos(i))
y=[]
for j in theta:
if (j < 4.5* np.pi):
y.append(radius*np.sin(j))
else:
y.append((radius+2.0) * np.sin(j))
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot(x, y, theta,
label = 'Parametric Curve', # label of the curve
color = 'DarkMagenta', # colour of the curve
linewidth = 1, # thickness of the line
linestyle = '-' # available styles - -- -. :
)
rcParams['legend.fontsize'] = 11 # legend font size
ax.legend() # adds the legend
ax.set_xlabel('X axis')
ax.set_xlim(-5, 5)
ax.set_ylabel('Y axis')
ax.set_ylim(-10, 10)
ax.set_zlabel('Z axis')
ax.set_zlim(-9*np.pi, 9*np.pi)
ax.set_title('3D line plot,\n parametric curve', va='bottom')
plt.show() # display the plot
I have two questions:
1) I was able to adjust the radius of my spiral but was not able to adjust the number of pitch. What changes should i make so I can have 19 circular rings, instead of 9.
2) After certain point(ie. end point of helix), I want to increase my radius and create a right-handed helix that goes all the way to bottom to the starting point of my first helix ( my first helix was left-handed helix). I was able to increase my radius but was not able to change the orientation of my helix and was not able to move it downwards.
After reading the documentation of matplotlib I could find:
The example below illustrates a plotting several lines with different format styles in one command using arrays.
import numpy as np
import matplotlib.pyplot as plt
# evenly sampled time at 200ms intervals
t = np.arange(0., 5., 0.2)
# red dashes, blue squares and green triangles
plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
plt.show()
Why cannot I do the same when there are three axes?
I have plain 3-D co-ordinates - a set of (x,y,z). I want a 2-D plot of X vs Y with coordinates having larger value of z colored darker than smaller values of z. How do I do that?
From your description, it sounds like you want scatter.
For example:
import numpy as np
import matplotlib.pyplot as plt
x, y, z = np.random.random((3, 10))
fig, ax = plt.subplots()
scat = ax.scatter(x, y, c=z, s=200, cmap='gray_r')
fig.colorbar(scat)
plt.show()