Setting parallel prospective in mlab/mayavi/python - python

I have a code in python to render a few spheres in python that looks like this:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import random
import mayavi
from mayavi import mlab
N = 4;
diams = .4*np.ones([N]);
xvals = np.arange(N);
yvals = np.zeros(N);
zvals = np.zeros(N);
pts = mlab.points3d(xvals, yvals, zvals, diams, scale_factor=1,transparent=True)
mlab.show()
The default view of the figure adds distortion based on the camera position (farther spheres smaller). I'd like to set the to parallel projection (farther spheres same size) by some command so it renders like this automatically.
I didn't find a straightforward solution with google or the documentation. Thanks!

Try setting fig.scene.parallel_projection = True or mlab.gcf().scene.parallel_projection = True in your case.
As a quick example, compare (zoomed in to magnify differences):
import numpy as np
from mayavi import mlab
np.random.seed(1977)
x, y, z = np.random.random((3, 10))
mlab.points3d(x, y, z)
mlab.show()
And when we set an orthogonal projection:
import numpy as np
from mayavi import mlab
np.random.seed(1977)
x, y, z = np.random.random((3, 10))
mlab.points3d(x, y, z)
mlab.gcf().scene.parallel_projection = True
mlab.show()

In addition to the accepted answer, I discovered that when we use the figure.scene.parallel_projection = True mode, the parameters returned by mlab.view() are no longer sufficient to describe the camera view completely. There is another parameter that comes into play:
figure.scene.camera.parallel_scale
So, if one wishes to set the view to be the same every time, then they have to (1) set mlab.view(..) and also (2) set figure.scene.camera.parallel_scale = 5.0 for example.
(Background story: my script plots a surface, then I had set the camera using only mlab.view(..), and saw that the rendered images had inconsistent scaling. The reason is: as I plot, TVTK updates the camera's parameters, so they might be different if the plots are not identical. These parameters include parallel_scale, which affects the projection — it's basically a zoom — but is independent of mlab.view().)

Related

Python - Visualize Dynamic Changes in Graph with Mayavi

I am trying to visualize a 3D graph using Mayavi.
During the program run, some nodes or edges in the graph become unavailable and I want to visualize dynamically that they become inaccessible in the visualization scene. How can I achieve this?
I'm still a newbie in python but it seems that the Mayavi scene can't be changed through the program once it is displayed.
It can be changed in many ways. You can add and remove elements, change background and foreground colors, and animate stuff. For example (from this link):
from __future__ import absolute_import, division, print_function
from mayavi import mlab
import numpy as np
import math
alpha = np.linspace(0, 2*math.pi, 100)
xs = np.cos(alpha)
ys = np.sin(alpha)
zs = np.zeros_like(xs)
mlab.points3d(0,0,0)
plt = mlab.points3d(xs[:1], ys[:1], zs[:1])
#mlab.animate(delay=100)
def anim():
f = mlab.gcf()
while True:
for (x, y, z) in zip(xs, ys, zs):
print('Updating scene...')
plt.mlab_source.set(x=x, y=y, z=z)
yield
anim()
mlab.show()
, will return you an animation where two sphere exist and one is having it's position changed every step of time:
The Mayavi documentation isn't exactly brilliant from my point of view but you can get some information both from the examples and chapters. For example remove an object from Mayavi pipeline.

How to update 3D arrow animation in matplotlib

I am trying to reproduce the left plot of this animation in python using matplotlib.
I am able to generate the vector arrows using the 3D quiver function, but as I read here, it does not seem possible to set the lengths of the arrows. So, my plot does not look quite right:
So, the question is: how do I generate a number of 3D arrows with different lengths? Importantly, can I generate them in such a way so that I can easily modify for each frame of the animation?
Here's my code so far, with the not-so-promising 3D quiver approach:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d
ax1 = plt.subplot(111,projection='3d')
t = np.linspace(0,10,40)
y = np.sin(t)
z = np.sin(t)
line, = ax1.plot(t,y,z,color='r',lw=2)
ax1.quiver(t,y,z, t*0,y,z)
plt.show()
As Azad suggests, an inelegant, but effective, solution is to simply edit the mpl_toolkits/mplot3d/axes3d.py to remove the normalization. Since I didn't want to mess with my actual matplotlib installation, I simply copied the axes3d.py file to the same directory as my other script and modified the line
norm = math.sqrt(u ** 2 + v ** 2 + w ** 2)
to
norm = 1
(Be sure to change the correct line. There is another use of "norm" a few lines higher.) Also, to get axes3d.py to function correctly when it's outside of the mpl directory, I changed
from . import art3d
from . import proj3d
from . import axis3d
to
from mpl_toolkits.mplot3d import art3d
from mpl_toolkits.mplot3d import proj3d
from mpl_toolkits.mplot3d import axis3d
And here is the nice animation that I was able to generate (not sure what's going wrong with the colors, it looks fine before I uploaded to SO).
And the code to generate the animation:
import numpy as np
import matplotlib.pyplot as plt
import axes3d_hacked
ax1 = plt.subplot(111,projection='3d')
plt.ion()
plt.show()
t = np.linspace(0,10,40)
for index,delay in enumerate(np.linspace(0,1,20)):
y = np.sin(t+delay)
z = np.sin(t+delay)
if delay > 0:
line.remove()
ax1.collections.remove(linecol)
line, = ax1.plot(t,y,z,color='r',lw=2)
linecol = ax1.quiver(t,y,z, t*0,y,z)
plt.savefig('images/Frame%03i.gif'%index)
plt.draw()
plt.ioff()
plt.show()
Now, if I could only get those arrows to look prettier, with nice filled heads. But that's a separate question...
EDIT: In the future, matplotlib will not automatically normalize the arrow lengths in the 3D quiver per this pull request.
Another solution is to call ax.quiever on each arrow, individually - with each call having an own length attribute. This is not very efficient but it will get you going.
And there's no need to change MPL-code

Python Matplotlib 3D reduce scale of z axis

I try to render the classic rice.png image for an image processing class but I cannot get the result I want.
import matplotlib.pyplot as plt
import numpy as np
from scipy import misc
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
# rice.png is the image one can find on the internet
rice = misc.imread('rice.png')
height, width = rice.shape
fig = plt.figure(2)
ax = fig.gca(projection='3d')
X = np.arange(0, height, 1)
Y = np.arange(0, width, 1)
X, Y = np.meshgrid(X, Y)
surf = ax.plot_surface(X, Y, rice, cmap=cm.coolwarm, linewidth=0)
plt.title('3D representation of image')
plt.show()
But that gives me this :
I tried using set_zticks but the ticks overflow as in the image above, I tried also the solutions one can see here but it overflows also and/or give poor result.
My goal is to have something like what can be seen in the paragraph how can I do so ?
[edit] I have already seen this question that gives a less complete answer that what can be found in the other link I gave earlier (idea of overriding the proj fonction).
However, I am not happy with the results. First because it means I have to change a functions in the matplotlib library (if I follow the SO solution) and as I will share my code with other students from my class, I do not want to do so. Then because it does not give me the same result (see later) it does not center the image, it just change the scale then cut the above part.
[edit2] update of the code

equivalent from MATLAB to Python

I just have started with Python and I would translate this example from MATLAB to Python, but I have not found the equivalent in Python.
https://www.mathworks.com/help/matlab/ref/surface.html
load clown
surface(peaks,flipud(X),...
'FaceColor','texturemap',...
'EdgeColor','none',...
'CDataMapping','direct')
colormap(map)
view(-35,45)
Thanks!
Matplotlib offers nearly all plotting options Matlab does. Surface plots can be done as well: http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#surface-plots
To load images scipy has a PIL-wrapper (no clown included, sorry), which loads matplotlib-compatible numpy arrays.
To sum up, you want the following packages: numpy, scipy, matplotlib and PIL. The combination of those four libraries should give you all you need. Also check out the pylab interface of these libraries, as it is very similar to Matlab.
Example that does what I believe you want to do:
from mpl_toolkits.mplot3d import Axes3D
from scipy.misc import imread
from matplotlib.pyplot import figure, show
from numpy import linspace, meshgrid, sqrt, sin, mean, flipud
clown = imread('clown.png')
fig = figure()
ax = fig.gca(projection='3d')
X = linspace(-5, 5, clown.shape[0])
Y = linspace(-5, 5, clown.shape[1])
X, Y = meshgrid(X, Y)
R = sqrt(X**2 + Y**2)
Z = sin(R)
clown = clown.swapaxes(0,1) / 255. # meshgrid orients axes the other way around, scaling of rgb to [0-1]
ax.plot_surface(X, Y, Z, facecolors=flipud(clown))
ax.view_init(45,-35) # swapped wrt matlab
show()

Display array as raster image in python

I've got a numpy array in Python and I'd like to display it on-screen as a raster image. What is the simplest way to do this? It doesn't need to be particularly fancy or have a nice interface, all I need to do is to display the contents of the array as a greyscale raster image.
I'm trying to transition some of my IDL code to Python with NumPy and am basically looking for a replacement for the tv and tvscl commands in IDL.
Depending on your needs, either matplotlib's imshow or glumpy are probably the best options.
Matplotlib is infinitely more flexible, but slower (animations in matplotlib can be suprisingly resource intensive even when you do everything right.). However, you'll have a really wonderful, full-featured plotting library at your disposal.
Glumpy is perfectly suited for the quick, openGL based display and animation of a 2D numpy array, but is much more limited in what it does. If you need to animate a series of images or display data in realtime, it's a far better option than matplotlib, though.
Using matplotlib (using the pyplot API instead of pylab):
import matplotlib.pyplot as plt
import numpy as np
# Generate some data...
x, y = np.meshgrid(np.linspace(-2,2,200), np.linspace(-2,2,200))
x, y = x - x.mean(), y - y.mean()
z = x * np.exp(-x**2 - y**2)
# Plot the grid
plt.imshow(z)
plt.gray()
plt.show()
Using glumpy:
import glumpy
import numpy as np
# Generate some data...
x, y = np.meshgrid(np.linspace(-2,2,200), np.linspace(-2,2,200))
x, y = x - x.mean(), y - y.mean()
z = x * np.exp(-x**2 - y**2)
window = glumpy.Window(512, 512)
im = glumpy.Image(z.astype(np.float32), cmap=glumpy.colormap.Grey)
#window.event
def on_draw():
im.blit(0, 0, window.width, window.height)
window.mainloop()
Using ipython in the pylab interactive mode, you could do:
$ ipython pylab
In [1]: imshow(your_array)
or not in pylab mode:
$ ipython
In [1]: from pylab import *
In [2]: imshow(your_array)
In [3]: pylab.show()
or without the pylab namespace thing:
$ ipython
In [1]: import matplotlib.pyplot as pyplot
In [2]: pyplot.imshow(your_array)
In [3]: pyplot.show()
Quick addition: for displaying with matplotlib, if you want the image to appear "raster", i.e., pixelized without smoothing, then you should include the option interpolation='nearest' in the call to imshow.

Categories