mayavi 3d object in matplotlib Axes3D - python

I sometimes find myself frustrated with the lack of certain rendering features in matplotlib's mplot3d. In most of these cases, I do find that I can get what I want in mayavi, but still the matplotlib 3d axes are preferable, if only for aesthetics, like LaTeX-ified labels and visual consistency with my other figures.
My question here is about the obvious hack: is it possible to draw some 3d object (a surface or 3d scatter plot or whatever) in mayavi without axes, export that image, then place it in a matplotlib Axes3D of correct size, orientation, coordinate projection, etc.? Can anyone think of an outline of what would be needed to accomplish this, or perhaps even offer a skeleton solution?
I fiddled around with this some time ago and found I had no trouble in exporting a transparent background mayavi figure and placing it in an empty matplotlib Axes3D (with ticks, labels, and so on), but I didn't get far in getting the camera configurations of mayavi and matplotlib to match. Simply setting the three common parameters of azimuth, elevation, and distance equal in both environments didn't do the trick; presumably what's needed is some consideration of the perspective (or other) transformations going on to render the whole scene, and I'm fairly clueless in that area.
It seems like this might be useful:
http://docs.enthought.com/mayavi/mayavi/auto/example_mlab_3D_to_2D.html

I produced a proof-of-concept solution for Mayavi -> PGFPlots using the mlab_3D_to_2D.py example and the "Support for External Three-Dimensional Graphics" section of the PGFPlots manual.
Procedure:
Run the modified mlab_3D_to_2D.py with Mayavi to generate img.png. Four random points are printed to the console, copy these to the clipboard. Note the figure size and resolution are hard-coded into the script, these shoud be edited or automatically extracted for different image sizes.
Paste the points into mlab_pgf.tex.
Run LaTeX on mlab_pgf.tex.
Result:
Modified mlab_3D_to_2D.py:
# Modified mlab_3D_to_2D.py from https://docs.enthought.com/mayavi/mayavi/auto/example_mlab_3D_to_2D.html
# Original copyright notice:
# Author: S. Chris Colbert <sccolbert#gmail.com>
# Copyright (c) 2009, S. Chris Colbert
# License: BSD Style
from __future__ import print_function
# this import is here because we need to ensure that matplotlib uses the
# wx backend and having regular code outside the main block is PyTaboo.
# It needs to be imported first, so that matplotlib can impose the
# version of Wx it requires.
import matplotlib
# matplotlib.use('WXAgg')
import pylab as pl
import numpy as np
from mayavi import mlab
from mayavi.core.ui.mayavi_scene import MayaviScene
def get_world_to_view_matrix(mlab_scene):
"""returns the 4x4 matrix that is a concatenation of the modelview transform and
perspective transform. Takes as input an mlab scene object."""
if not isinstance(mlab_scene, MayaviScene):
raise TypeError('argument must be an instance of MayaviScene')
# The VTK method needs the aspect ratio and near and far clipping planes
# in order to return the proper transform. So we query the current scene
# object to get the parameters we need.
scene_size = tuple(mlab_scene.get_size())
clip_range = mlab_scene.camera.clipping_range
aspect_ratio = float(scene_size[0])/float(scene_size[1])
# this actually just gets a vtk matrix object, we can't really do anything with it yet
vtk_comb_trans_mat = mlab_scene.camera.get_composite_projection_transform_matrix(
aspect_ratio, clip_range[0], clip_range[1])
# get the vtk mat as a numpy array
np_comb_trans_mat = vtk_comb_trans_mat.to_array()
return np_comb_trans_mat
def get_view_to_display_matrix(mlab_scene):
""" this function returns a 4x4 matrix that will convert normalized
view coordinates to display coordinates. It's assumed that the view should
take up the entire window and that the origin of the window is in the
upper left corner"""
if not (isinstance(mlab_scene, MayaviScene)):
raise TypeError('argument must be an instance of MayaviScene')
# this gets the client size of the window
x, y = tuple(mlab_scene.get_size())
# normalized view coordinates have the origin in the middle of the space
# so we need to scale by width and height of the display window and shift
# by half width and half height. The matrix accomplishes that.
view_to_disp_mat = np.array([[x/2.0, 0., 0., x/2.0],
[ 0., -y/2.0, 0., y/2.0],
[ 0., 0., 1., 0.],
[ 0., 0., 0., 1.]])
return view_to_disp_mat
def apply_transform_to_points(points, trans_mat):
"""a function that applies a 4x4 transformation matrix to an of
homogeneous points. The array of points should have shape Nx4"""
if not trans_mat.shape == (4, 4):
raise ValueError('transform matrix must be 4x4')
if not points.shape[1] == 4:
raise ValueError('point array must have shape Nx4')
return np.dot(trans_mat, points.T).T
def test_surf():
"""Test surf on regularly spaced co-ordinates like MayaVi."""
def f(x, y):
sin, cos = np.sin, np.cos
return sin(x + y) + sin(2 * x - y) + cos(3 * x + 4 * y)
x, y = np.mgrid[-7.:7.05:0.1, -5.:5.05:0.05]
z = f(x, y)
s = mlab.surf(x, y, z)
#cs = contour_surf(x, y, f, contour_z=0)
return x, y, z, s
if __name__ == '__main__':
f = mlab.figure()
f.scene.parallel_projection = True
N = 4
# x, y, z, m = test_mesh()
x, y, z, s = test_surf()
mlab.move(forward=2.0)
# now were going to create a single N x 4 array of our points
# adding a fourth column of ones expresses the world points in
# homogenous coordinates
W = np.ones(x.flatten().shape)
hmgns_world_coords = np.column_stack((x.flatten(), y.flatten(), z.flatten(), W))
# applying the first transform will give us 'unnormalized' view
# coordinates we also have to get the transform matrix for the
# current scene view
comb_trans_mat = get_world_to_view_matrix(f.scene)
view_coords = \
apply_transform_to_points(hmgns_world_coords, comb_trans_mat)
# to get normalized view coordinates, we divide through by the fourth
# element
norm_view_coords = view_coords / (view_coords[:, 3].reshape(-1, 1))
# the last step is to transform from normalized view coordinates to
# display coordinates.
view_to_disp_mat = get_view_to_display_matrix(f.scene)
disp_coords = apply_transform_to_points(norm_view_coords, view_to_disp_mat)
# at this point disp_coords is an Nx4 array of homogenous coordinates
# where X and Y are the pixel coordinates of the X and Y 3D world
# coordinates, so lets take a screenshot of mlab view and open it
# with matplotlib so we can check the accuracy
img = mlab.screenshot(figure=f, mode='rgba', antialiased=True)
pl.imsave("img.png", img)
pl.imshow(img)
# mlab.close(f)
idx = np.random.choice(range(disp_coords[:, 0:2].shape[0]), N, replace=False)
for i in idx:
# print('Point %d: (x, y) ' % i, disp_coords[:, 0:2][i], hmgns_world_coords[:, 0:3][i])
a = hmgns_world_coords[:, 0:3][i]
a = str(list(a)).replace('[', '(').replace(']', ')').replace(' ',',')
# See note below about 298.
b = np.array([0, 298]) - disp_coords[:, 0:2][i]
b = b * np.array([-1, 1])
# Important! These values are not constant.
# The image is 400 x 298 pixels, or 288 x 214.6 pt.
b[0] = b[0] / 400 * 288
b[1] = b[1] / 298 * 214.6
b = str(list(b)).replace('[', '(').replace(']', ')').replace(' ',',')
print(a, "=>", b)
pl.plot([disp_coords[:, 0][i]], [disp_coords[:, 1][i]], 'ro')
pl.show()
# you should check that the printed coordinates correspond to the
# proper points on the screen
mlab.show()
#EOF
mlab_pgf.py:
\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}
\begin{document}
\begin{tikzpicture}
\begin{axis}[
grid=both,minor tick num=1,
xlabel=$x$,ylabel=$y$,zlabel=$z$,
xmin=-7,
xmax=7,
ymin=-5,
ymax=5,
zmin=-3,
zmax=3,
]
\addplot3 graphics [
points={% important, paste points generated by `mlab_3D_to_2D.py`
(5.100000000000001, -3.8, 2.9491697063900895) => (69.82857610254948, 129.60245304203693)
(-6.2, -3.0999999999999996, 0.6658335107904079) => (169.834990346303, 158.6375879061911)
(-1.7999999999999998, 0.4500000000000002, -1.0839565197346115) => (162.75120267070378, 103.53696636434113)
(-5.3, -4.9, 0.6627774166307937) => (147.33354714145847, 162.93938533017257)
},
] {img.png};
\end{axis}
\end{tikzpicture}
\end{document}

Related

Generating a 3D object (e.g. via Mayavi) and exporting it as 3D image stack (e.g. tiff)

My current task is to generate a 3D image space where there are 3D objects (of iso-surfaces) that I designed and export it as an image stack (numpy or tiff).
I came down to using Mayavi to generate 3D iso-surfaces. I know Mayavi is originally designed to provide 3D visualizations on its own, but I would like to find a way that I can export a 3D object to a 3D image stack as in a numpy form of (z,y,x). My initial idea was to iteratively take snapshots of the sliced volume from the Mayavi mlab object along z-axis but I am not sure if there's any option to save a sliced image of an iso-surface as a snapshot.
The best case scenario would be to export a 3D image stack (tiff) exactly from what I see from a Mayavi window. Otherwise, I will take any suggestions to carry out this task in general.
Here's an example code.
import numpy as np
from mayavi import mlab
# Produce some nice data.
n_mer, n_long = 6, 11
pi = np.pi
dphi = pi/1000.0
phi = np.arange(0.0, 2*pi + 0.5*dphi, dphi, 'd')
mu = phi*n_mer
x = np.cos(mu)*(1+np.cos(n_long*mu/n_mer)*0.5)
y = np.sin(mu)*(1+np.cos(n_long*mu/n_mer)*0.5)
z = np.sin(n_long*mu/n_mer)*0.5
# Init plot
source = mlab.points3d(x, y, z)
You might go for the vtk class vtkImplicitModeller.
E.g.:
import numpy as np
from vedo import Points, Volume
n_mer, n_long = 6, 11
dphi = np.pi/1000.0
phi = np.arange(0.0, 2*np.pi + 0.5*dphi, dphi, 'd')
mu = phi*n_mer
x = np.cos(mu)*(1+np.cos(n_long*mu/n_mer)*0.5)
y = np.sin(mu)*(1+np.cos(n_long*mu/n_mer)*0.5)
z = np.sin(n_long*mu/n_mer)*0.5
source = Points([x, y, z], r=4)
modl = source.implicitModeller(
distance=0.15,
res=(60,60,30),
bounds=(-1.8,1.8,-1.8,1.8,-0.7,0.7),
)
modl.smoothLaplacian().computeNormals()
modl.c("blue9").lw(1).lighting("metallic").show(axes=1)
#######################################################
import vtk
imp = vtk.vtkImplicitModeller()
imp.SetInputData(source.polydata())
imp.SetSampleDimensions(50,50,30)
imp.SetModelBounds(-1.8,1.8,-1.8,1.8,-0.7,0.7)
imp.Update()
vol = Volume(imp.GetOutput())
arr = np.clip(vol.getDataArray(), 0, 1.2)
print(arr.shape)

Plotting parametric objects as a grid in PyVista

I am stuck with probably a simple problem but after reading pyvista docs I am still looking for an answer. I am trying to plot a grid in which each cell will be a mesh defined as a parametric shape i.e. supertorus. In an early version of pyvista, I defined "my own" supertorus as below:
def supertorus(yScale, xScale, Height, InternalRadius, Vertical, Horizontal,
deltaX=0, deltaY=0, deltaZ=0):
# initial range for values used in parametric equation
n = 100
u = np.linspace(-np.pi, np.pi, n)
t = np.linspace(-np.pi, np.pi, n)
u, t = np.meshgrid(u, t)
# a1: Y Scale <0, 2>
a1 = yScale
# a2: X Scale <0, 2>
a2 = xScale
# a3: Height <0, 5>
a3 = Height
# a4: Internal radius <0, 5>
a4 = InternalRadius
# e1: Vertical squareness <0.25, 1>
e1 = Vertical
# e2: Horizontal squareness <0.25, 1>
e2 = Horizontal
# Definition of parametric equation for supertorus
x = a1 * (a4 + np.sign(np.cos(u)) * np.abs(np.cos(u)) ** e1) *\
np.sign(np.cos(t)) * np.abs(np.cos(t)) ** e2
y = a2 * (a4 + np.sign(np.cos(u)) * np.abs(np.cos(u)) ** e1) *\
np.sign(np.sin(t)) * np.abs(np.sin(t)) ** e2
z = a3 * np.sign(np.sin(u)) * np.abs(np.sin(u)) ** e1
grid = pyvista.StructuredGrid(x + deltaX + 5, y + deltaY + 5, z + deltaZ)
return grid
I could manipulate with deltaX, deltaY and deltaZ to position supertori at the location of my choice.
Unfortunately, this approach was not efficient and I am planning to use PyVista provided supertoroidal meshes (https://docs.pyvista.org/examples/00-load/create-parametric-geometric-objects.html?highlight=supertoroid). My question is: how I can place multiple meshes (like supertori) at the location defined by coordinates x, y, z?
I believe what you're looking for are glyphs. You can pass your own dataset as a glyph geometry that will in turn plot the dataset in each point of the supermesh. Without going into details of orienting your glyphs, colouring them according to scalars and whatnot, here's a simple "alien invasion" scenario as an example:
import numpy as np
import pyvista as pv
# get dataset for the glyphs: supertoroid in xy plane
saucer = pv.ParametricSuperToroid(ringradius=0.5, n2=1.5, zradius=0.5)
saucer.rotate_y(90)
# saucer.plot() # <-- check how a single saucer looks like
# get dataset where to put glyphs
x,y,z = np.mgrid[-1:2, -1:2, :2]
mesh = pv.StructuredGrid(x, y, z)
# construct the glyphs on top of the mesh
glyphs = mesh.glyph(geom=saucer, factor=0.3)
# glyphs.plot() # <-- simplest way to plot it
# create Plotter and add our glyphs with some nontrivial lighting
plotter = pv.Plotter(window_size=(1000, 800))
plotter.add_mesh(glyphs, color=[0.2, 0.2, 0.2], specular=1, specular_power=15)
plotter.show()
I've added some strong specular lighting to make the saucers look more menacing:
But the key point for your problem was creating the glyphs from your supermesh by passing it as the geom keyword of mesh.glyph. The other keywords such as orient and scale are useful for arrow-like glyphs where you can use the glyph to denote vectorial information of your dataset.
You've asked in comments whether it's possible to vary the glyphs along the dataset. I was certain that this was not possible, however the VTK docs clearly mention the possibility to define a collection of glyphs to use:
More than one glyph may be used by creating a table of source objects, each defining a different glyph. If a table of glyphs is defined, then the table can be indexed into by using either scalar value or vector magnitude.
It turns out that PyVista doesn't expose this functionality (yet), but the base vtk package lets us get our hands dirty. Here's a proof of concept based on DataSetFilters.glyph, which I'll float by the PyVista devs to see if there's interest in exposing this functionality.
import numpy as np
import pyvista as pv
from pyvista.core.filters import _get_output # just for this standalone example
import vtk
pyvista = pv # just for this standalone example
# below: adapted from core/filters.py
def multiglyph(dataset, orient=True, scale=True, factor=1.0,
tolerance=0.0, absolute=False, clamping=False, rng=None,
geom_datasets=None, geom_values=None):
"""Copy a geometric representation (called a glyph) to every point in the input dataset.
The glyphs may be oriented along the input vectors, and they may be scaled according to scalar
data or vector magnitude.
Parameters
----------
orient : bool
Use the active vectors array to orient the glyphs
scale : bool
Use the active scalars to scale the glyphs
factor : float
Scale factor applied to sclaing array
tolerance : float, optional
Specify tolerance in terms of fraction of bounding box length.
Float value is between 0 and 1. Default is 0.0. If ``absolute``
is ``True`` then the tolerance can be an absolute distance.
absolute : bool, optional
Control if ``tolerance`` is an absolute distance or a fraction.
clamping: bool
Turn on/off clamping of "scalar" values to range.
rng: tuple(float), optional
Set the range of values to be considered by the filter when scalars
values are provided.
geom_datasets : tuple(vtk.vtkDataSet), optional
The geometries to use for the glyphs in table mode
geom_values : tuple(float), optional
The value to assign to each geometry dataset, optional
"""
# Clean the points before glyphing
small = pyvista.PolyData(dataset.points)
small.point_arrays.update(dataset.point_arrays)
dataset = small.clean(point_merging=True, merge_tol=tolerance,
lines_to_points=False, polys_to_lines=False,
strips_to_polys=False, inplace=False,
absolute=absolute)
# Make glyphing geometry
if not geom_datasets:
arrow = vtk.vtkArrowSource()
arrow.Update()
geom_datasets = arrow.GetOutput(),
geom_values = 0,
# check if the geometry datasets are consistent
if not len(geom_datasets) == len(geom_values):
raise ValueError('geom_datasets and geom_values must have the same length!')
# TODO: other kinds of sanitization, e.g. check for sequences etc.
# Run the algorithm
alg = vtk.vtkGlyph3D()
if len(geom_values) == 1:
# use a single glyph
alg.SetSourceData(geom_datasets[0])
else:
alg.SetIndexModeToScalar()
# TODO: index by vectors?
# TODO: SetInputArrayToProcess for arbitrary arrays, maybe?
alg.SetRange(min(geom_values), max(geom_values))
# TODO: different Range?
for val, geom in zip(geom_values, geom_datasets):
alg.SetSourceData(val, geom)
if isinstance(scale, str):
dataset.active_scalars_name = scale
scale = True
if scale:
if dataset.active_scalars is not None:
if dataset.active_scalars.ndim > 1:
alg.SetScaleModeToScaleByVector()
else:
alg.SetScaleModeToScaleByScalar()
else:
alg.SetScaleModeToDataScalingOff()
if isinstance(orient, str):
dataset.active_vectors_name = orient
orient = True
if rng is not None:
alg.SetRange(rng)
alg.SetOrient(orient)
alg.SetInputData(dataset)
alg.SetVectorModeToUseVector()
alg.SetScaleFactor(factor)
alg.SetClamping(clamping)
alg.Update()
return _get_output(alg)
def example():
"""Small glyph example"""
rng = np.random.default_rng()
# get dataset for the glyphs: supertoroid in xy plane
# use N random kinds of toroids over a mesh with 27 points
N = 5
values = np.arange(N) # values for scalars to look up glyphs by
geoms = [pv.ParametricSuperToroid(n1=n1, n2=n2) for n1,n2 in rng.uniform(0.5, 2, size=(N, 2))]
for geom in geoms:
# make the disks horizontal for aesthetics
geom.rotate_y(90)
# get dataset where to put glyphs
x,y,z = np.mgrid[-1:2, -1:2, -1:2]
mesh = pv.StructuredGrid(x, y, z)
# add random scalars
mesh.point_arrays['scalars'] = rng.integers(0, N, size=x.size)
# construct the glyphs on top of the mesh; don't scale by scalars now
glyphs = multiglyph(mesh, geom_datasets=geoms, geom_values=values, scale=False, factor=0.3)
# create Plotter and add our glyphs with some nontrivial lighting
plotter = pv.Plotter(window_size=(1000, 800))
plotter.add_mesh(glyphs, specular=1, specular_power=15)
plotter.show()
if __name__ == "__main__":
example()
The multiglyph function in the above is mostly the same as mesh.glyph, but I've replaced the geom keyword with two keywords, geom_datasets and geom_values. These define an index -> geometry mapping that is then used to look up each glyph based on array scalars.
You asked whether you can colour the glyphs independently: you can. In the above proof of concept the choice of glyph is tied to the scalars (choosing vectors would be equally easy; I'm not so sure about arbitrary arrays). However you can easily choose what arrays to colour by when you call pv.Plotter.add_mesh, so my suggestion is to use something other than the proper scalars to colour your glyphs.
Here's a typical output:
I kept the scalars for colouring to make it easier to see the differences between the glyphs. You can see that there are five different kinds of glyphs being chosen randomly based on the random scalars. If you set non-integer scalars it will still work; I suspect vtk chooses the closest scalar or something similar for lookup.

Mayavi Contour 3d

If I plot a 3d data using contour3d option of mayavi, there are 3 default contours but how they spaced. I understand the number of contours can be changed, but can they be at user specified values (I would surely guess that is possible). I would like to know how are the default 3 contours drawn. Depending on maximum value of scalar and how is it distributed.
As it happens I just had the same problem and found a solution.
Here is some sample code:
import numpy as np
from mayavi import mlab
from mayavi.api import Engine
def fun(x, y, z):
return np.cos(x) * np.cos(y) * np.cos(z)
# create engine and assign figure to it
engine = Engine()
engine.start()
fig = mlab.figure(figure=None, engine=engine)
contour3d = mlab.contour3d(x, y, z, fun, figure=fig)
scene = engine.scenes[0]
# get a handle for the plot
iso_surface = scene.children[0].children[0].children[0]
# the following line will print you everything that you can modify on that object
iso_surface.contour.print_traits()
# now let's modify the number of contours and the min/max
# you can also do these steps manually in the mayavi pipeline editor
iso_surface.compute_normals = False # without this only 1 contour will be displayed
iso_surface.contour.number_of_contours = 2
iso_surface.contour.minimum_contour = -1.3
iso_surface.contour.maximum_contour = 1.3
Now about the meaning of the contours. Well, the number obviously says how many contours are created. Then the values for min/max will define a linear space over which the contours will be spread. The value should basically influence the shrinkage/expansion along the surface normals.
Edit: Here's a tip. When you got your plot window, click on the mayavi pipeline icon in the top left. There you can modify your object (usually lowest in the tree). When you press the red record button and start modifying things it will give you the corresponding lines of code.

Python: using X and Y values to draw a picture

I have a series of methods that take an image 89x22 pixels (although the size, theoretically, is irrelevant) and fits a curve to each row of pixels to find the location of the most significant signal. At the end, I have a list of Y-values, one for each row of pixels, and a list of X-values, the location of the most significant peak for each row.
I would like to test different types of curves to see which models the data better, and in order to do so, I would like to be able to print out a new image, also 89x22 pixels, with the location of the most significant peak marked with a single red pixel for each line. A have attached an input example and a (poorly drawn) example of what I expect a good output to look like:
Any suggestions on which modules to start looking in?
class image :
def importImage (self) :
"""Open an image and sort all pixel values into a list of lists"""
from PIL import Image #imports Image from PIL library
im = Image.open("testTop.tif") #open the file
size = im.size #size object is a tuple with the pixel width and pixel height
width = size[0] #defines width object as the image width in pixels
height = size[1] #defines the height object as the image height in pixels
allPixels = list(im.getdata()) #makes a list of all pixels values
pixelList = [allPixels[width*i : width * (i+1)] for i in range(height)] #takes mega-list and makes a list of lists by row
return(pixelList) #returns list of lists
def fitCurves (self) :
"""
Iterate through a list of lists and fit a curve to each list of integers.
Append the position of the list and the location of the vertex to a growing list.
"""
from scipy.optimize import curve_fit
import numpy as np
from matplotlib import pyplot as pp
from scipy.misc import factorial
image = self.importImage()
xList = []
yList = []
position = 0
for row in image :
#Gaussian fit equations kindly provided by user mcwitt
x = np.arange(len(row))
ffunc = lambda x, a, x0, s: a*np.exp(-0.5*(x-x0)**2/s**2) # define function to fit
p, _ = curve_fit(ffunc, x, row, p0=[100,5,2]) # fit with initial guess a=100, x0=5, s=2
x0 = p[1]
yList.append(position)
position = position + 1
xList.append(x0)
print(yList)
print(xList)
newImage = image()
newImage.fitCurves()
Mabye:
import numpy as np
from matplotlib import pyplot as plt
from scipy import ndimage
from scipy import optimize
%matplotlib inline
# just a gaussian (copy paste from lmfit, another great package)
def my_gaussian(p,x):
amp = p[0]
cen = p[1]
wid = p[2]
return amp * np.exp(-(x-cen)**2 /wid)
# I do like to write a cost function separately. For the leastsquare algorithm it should return a vector.
def my_cost(p,data):
return data - my_gaussian(p,data)
# i load the image and generate the x values
image = ndimage.imread('2d_gaussian.png',flatten=True)
x = np.arange(image.shape[1])
popt = []
# enumerate is a convenient way to loop over an iterable and keep track of the index.
y = []
for index,data in enumerate(image):
''' this is the trick to make the algorithm robust.
I do plug the index of the maximum value of the current row as
initial guess for the center. Maybe it would be enough to do
just that and the fit is unnecessary. Haven`t checked that.
'''
max_index = np.argmax(data)
# initial guess.
x0 = [1.,max_index,10]
# call to the solver
p,_ = optimize.leastsq(my_cost, x0, args = data)
popt.append(p)
y.append(index)
'''
I do transpose the data.
As a consequence the values are stored row, not columnwise.
It is often easier to store the reusults inside a loop and
convert the data later into a numpy array.
'''
gaussian_hat = np.array(popt).T
# without the transpose, it would be center = gaussian_hat[:,1]
center = gaussian_hat[1]
y = np.array(y)
''' i do like to use an axis handle for the plot.
Not necessary, but gives me the opportunity to add new axis if necessary.
'''
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.imshow(image)
# since it is just a plot, I can plot the x, y coordinates
ax.plot(center,y,'k-')
# fitt of a 3th order polynomial
poly = np.polyfit(y,center,3)
# evaluation at points y
x_hat = np.polyval(poly,y)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.imshow(image)
ax.plot(x_hat,y,'k-')
plt.savefig('2d_gaussian_fit.png')

How to remove the red frame and white arrow in scalar_cut_plane when using mayavi?

Hi I want to use mayavi to visualize the data in structured grid in a cut plane.
To exemplify this, I have the following code obtained from http://docs.enthought.com/mayavi/mayavi/auto/example_structured_grid.html written by Eric Jones
#!/usr/bin/env python
import numpy as np
from numpy import cos, sin, pi
from tvtk.api import tvtk
from mayavi import mlab
def generate_annulus(r=None, theta=None, z=None):
# Find the x values and y values for each plane.
x_plane = (cos(theta)*r[:,None]).ravel()
y_plane = (sin(theta)*r[:,None]).ravel()
# Allocate an array for all the points. We'll have len(x_plane)
# points on each plane, and we have a plane for each z value, so
# we need len(x_plane)*len(z) points.
points = np.empty([len(x_plane)*len(z),3])
# Loop through the points for each plane and fill them with the
# correct x,y,z values.
start = 0
for z_plane in z:
end = start + len(x_plane)
# slice out a plane of the output points and fill it
# with the x,y, and z values for this plane. The x,y
# values are the same for every plane. The z value
# is set to the current z
plane_points = points[start:end]
plane_points[:,0] = x_plane
plane_points[:,1] = y_plane
plane_points[:,2] = z_plane
start = end
return points
# Make the data.
dims = (51, 25, 25)
# The coordinates
theta = np.linspace(0, 2*np.pi, dims[0])
# 'y' corresponds to varying 'r'
r = np.linspace(1, 10, dims[1])
z = np.linspace(0, 5, dims[2])
pts = generate_annulus(r, theta, z)
# Make the grid
sgrid = tvtk.StructuredGrid(dimensions=dims)
sgrid.points = pts
s = np.sqrt(pts[:,0]**2 + pts[:,1]**2 + pts[:,2]**2)
sgrid.point_data.scalars = np.ravel(s.copy())
sgrid.point_data.scalars.name = 'scalars'
d = mlab.pipeline.add_dataset(sgrid)
mlab.pipeline.scalar_cut_plane(d)
mlab.show()
However, I would like to get rid of the annoying red frame and the white arrow in when saving the plot. How do I do this?
I first tried to use the module mlab.pipeline.scalar_field to do this, but I got an error saying that I needed to specify the data as an array.
I've also searched the gui to see if there is somewhere I can turn this off, but i cannot seem to find it
You can simply disable the widget. Note however that this means that you cannot drag around your plane anymore (but it sounds like you do not want to have this functionality)
In last line, change
mlab.pipeline.scalar_cut_plane(d)
with
cut = mlab.pipeline.scalar_cut_plane(d)
cut.implicit_plane.widget.enabled = False
It is also possible to do this in the GUI.
Go to the ScalarCutPlane in the pipeline menu, then disable the widget by unchecking "enable" in the tab "ImplicitPlane".
...and there you go
You can make it nicer by adding:
cut = mlab.pipeline.scalar_cut_plane(d)
input('Press any key to snap in . . .')
cut.implicit_plane.widget.enabled = False
Now you can place in desired position first.

Categories