Wireframing from an image using matplotlib - python

I'm trying to make a 3D representation of an image as a surface using wireframes with matplotlib.
ig= mpimg.imread('testIMG.png');
X = np.linspace(0,len(ig[0]),len(ig[0])); #List of discrete x values
Y = np.linspace(0,len(ig[1]),len(ig[1])); #List of discrete y values
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#Plot the wireframe
#I want to plot the image as f(x,y) and I can't understand why wireframe won't let me
ax.plot_wireframe(X, Y, ig[:,:,2], rstride=10, cstride=10)
plt.show()
The imread function gives me an MxNx3 array of M rows, N columns, and an RGB value for each point in the matrix. I don't understand how to use wireframe to plot that data properly. These z values aren't plotting what I expected (a checkerboard pattern), but instead a y=x line alternating between 0 and 1.
What do I need to do here? I want a series of cuboids in a 3D checkerboard pattern.
Image of what I have currently

You may use np.meshgrid(), so:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import mpl_toolkits.mplot3d
ig = mpimg.imread('testIMG.png')
x = np.linspace(0, ig.shape[1], ig.shape[1]) #List of discrete x values
y = np.linspace(0, ig.shape[0], ig.shape[0]) #List of discrete y values
X, Y = np.meshgrid(x, y)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#Plot the wireframe
#I want to plot the image as f(x,y) and I can't understand why wireframe won't let me
ax.plot_wireframe(X, Y, ig[:,:,2], rstride=10, cstride=10)
plt.show()

Related

How to interpolate more points without losing the shape of the image

I am making a polygon shape do the a simulation with matplotlib and using plt.fill to fill in the shape. What I want to do is to increase the number of points for x and y without losing the shape. But when I try to interpolate the shape is lot.
from scipy.interpolate import interp1d
import pandas as pd
import matplotlib.pyplot as plt
var = pd.read_excel("pathtotheexcel\lens1.ods", engine="odf")
x = list(var['x'])
y = list(var['y'])
#Plotting original image
plt.fill(x, y, 'k')
plt.show()
#Doing interpolation
f = interp1d(x, y, kind='nearest')
xnew = np.linspace(min(x), max(x), 1000000)
plt.plot(xnew, f(xnew))
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.plot(x, y)
ax1.set_title('Without Interpolate')
ax2.plot(xnew, f(xnew))
ax2.set_title('With Interpolation')
fig.show()
The attached is the plot of the differences.
As you can see on the right there are some "jiggling" with the interpolation. How can I get a smoother plot with more points? I want to increase the number of points for both x and y coordinates without losing the shape of the plot.
You can find the lens data for plotting here

3D surface with matplotlib is incorrect but contour plot is correct

I am trying to plot a 3D surface with matplotlib for a bunch of math functions. These functions are defined to take in a 1-D numpy array with arbitrary length as input.
When plotted as contours plot, the plot looks correct.
. However, the 3D surface plot shows a surface that has been squashed onto a single line. I am using the same values for plotting, so they should be the same, but that's not what I'm getting and I'm very puzzled by this
Please see my code below:
from mpl_toolkits.mplot3d import Axes3D
# build the meshgrid
x = np.linspace(bounds[0][0],bounds[0][1])
y = np.linspace(bounds[1][0], bounds[1][1])
xv, yv = np.meshgrid(x, y)
# populate z
z = np.zeros_like(xv)
for row_idx in range(xv.shape[0]):
for col_idx in range(xv.shape[1]):
z[row_idx][col_idx] = function(np.array([xv[row_idx][col_idx], yv[row_idx][col_idx]]))
# plot 3D surface
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.plot_surface(x, y, z, cmap='viridis', edgecolor='none')
# plot a contour
ax.contourf(x, y, z, cmap='viridis')
plt.show()
# function that I'm plotting -> bowl shaped for 2D x
def function(x):
return sum(x**2)
I might have this wrong but if you are just wanting the surface mesh you need to plot the gridded data as opposed to the linear one so just change this line:
ax.plot_surface(xv, yv, z, cmap='viridis', edgecolor='none')
Note I'm using xv, yv instead of X,Y.
Here is my output.

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

How to convert scatter plot into a surface plot?

Beginner using python and I have a scatter plot (http://i.stack.imgur.com/sQNHM.png). What I want to do is produce a 3D plot that shows a spike in the Z direction at these points and 0 everywhere else.
This is the code I'm currently using:
plt.scatter(X, Y)
plt.show()
X, Y = np.meshgrid(X, Y)
Z = [1] * len(X)
fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.plot_surface(X, Y, Z)
plt.show()
This gives me a strange result (http://i.stack.imgur.com/7fLeT.png) that I'm not sure what to do to fix it.
You probably don't want to use the x and y values from your 2D plot as the input for meshgrid because you want this plot to be defined for all integer values of x and y in your range. The original x and y should define the location of the spikes if I understand your problem correctly. Here's a way to get a 3D plot with spikes of height 100 at defined locations:
from matplotlib import pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Create X, Y and Z arrays
x = range(0,250)
y = range(0,250)
X, Y = np.meshgrid(x, y)
Z = np.zeros((250,250))
# Locations of the spikes. These are some made up numbers.
dataX = np.array([25,80,90,145,180])
dataY = np.array([170,32,130,10,88])
# Set spikes to 100
Z[dataX,dataY] = 100
# Plot
fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.plot_surface(X, Y, Z)
plt.show()

Python 3d scatterplot colormap issue

I have four dimensional data (x, y, z displacements; and respective voltages) which I wish to plot in a 3d scatterplot in python. I've gotten the 3d plot to render, but I want to have the colour of the points change using a colourmap, dependent upon the magnitude of the point's voltage.
I've tried a few things, but can't seem to get it to work I'm getting the error ValueError: Cannot convert argument type <type 'numpy.ndarray'> to rgba array. I'm not sure exactly how to convert what I need to convert, so if anybody could please offer some help, I'd be most appreciative.
My code is here:
fig = plt.figure()
from mpl_toolkits.mplot3d import Axes3D
cmhot = plt.cm.get_cmap("hot")
ax = fig.add_subplot(111, projection='3d',)
ax.scatter(x, y, z, v, s=50, c = cmhot)
plt.show()
ax.scatter can take a color parameter c which is a sequence (e.g. a list or an array) of scalars, and a cmap parameter to specify a color map. So to make the colors vary according to the magnitude of the voltages, you could define:
c = np.abs(v)
This makes positive and negative voltages have the same color. If instead you wished each color (positive or negative) to have its own color, you could just use c = v.
For example,
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
x, y, z, v = (np.random.random((4,100))-0.5)*15
c = np.abs(v)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
cmhot = plt.get_cmap("hot")
cax = ax.scatter(x, y, z, v, s=50, c=c, cmap=cmhot)
plt.show()

Categories

Resources