Display normal vectors with open3d.visualization.O3DVisualizer - python

I'm using the amazing open3d Python libary to visualize some point Cloud. I already know the normal vectors of these points that I attribute directly as follows:
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points)
pcd.normals = o3d.utility.Vector3dVector(normals)
I am also setting a visualizer in which I insert these points as follows:
app = gui.Application.instance
app.initialize()
vis = o3d.visualization.O3DVisualizer("Open3D - 3D Text", 1024, 768)
vis.show_settings = True
vis.add_geometry("my points", pcd)
with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug) as cm:
'''visualize'''
vis.reset_camera_to_default()
app.add_window(vis)
app.run()
Up to now, all of this has run as intended, however I am not able to set the visualizer in such a way that enables me to visualize the normal vectors. Apparently o3d.visualization.Visualizer() has this method get_render_option() that is said to "retrieve a RenderOption" object, and in this RenderOption object there is a point_show_normal property but I couldn't make my code (more complicated than the minimal example above) work with o3d.visualization.Visualizer(): I don't see how to use this o3d.visualization.Visualizer().get_render_option().point_show_normal.
Is there any way to show the normal vectors with with open3d.visualization.O3DVisualizer?

you need add two lines to your code, get the render and set point_show_normal to True:
opt = vis.get_render_option()
opt.point_show_normal = True
You can see in the documentation open3D tutorials and python examples
I hope it helps

I didn't find a solution so far, so I resorted to look at my normal vectors in another window, produced using the mayavi library rather than the open3D library. To do so, I used this simple code snippet:
from mayavi.mlab import *
P = [my list of 3D points]
N = [my list of normal vectors]
x = P[:, 0]
y = P[:, 1]
z = P[:, 2]
points3d(x, y, z, color=(0, 1, 0), scale_factor=0.5)
u = N[:, 0]
v = N[:, 1]
w = N[:, 2]
quiver3d(x, y, z, u, v, w)
show()
And it worked as intended. Ideally I would like to have the normal vectors displayed with the rest of the figure, but this responded to my immediate needs.
I consider this as a workaround rather than the definitive solution, so I put it here as an answer if someone else having the problem finds it useful. But my question still isn't solved.

Related

How do I change the data display format for a imshow plot in matplotlib?

Or more specifically, how can I change the [659] on the upper-right corner to '659 degrees' or something like that ?
I have checked all the threads mentioned in the following reply: matplotlib values under cursor. However, all of them seem to address the x,y location of the cursor. I am interested in changing the data-value. I could not find a reply or related documentation in the api.
I have tried both format_coord(x, y) and format_cursor_data(data) but neither of them seem to be working.
Thanks,
Sarith
PS: My code is in multiple modules and is a part of gui application. I can share relevant sections if that would be of any help in answering this.
One line solution:
ax.format_coord = lambda x, y: 'x={:.2f}, y={:.2f}, z={:.2f}'.format(x,y,data[int(y + 0.5),int(x + 0.5)])
I had the same problem (I wanted to get rid of the data and send it to somewhere else in a tkinter widget).
I figured out the ax.format_coord was'nt being called, the one you have to change is the one at matplotlib.artist.Artist
this worked for me:
def format_cursor_data(self,data):
return 'new_data'
matplotlib.artist.Artist.format_cursor_data=format_cursor_data
By modifying an example from matplotlib I got this code:
This displays x,y, and z value with a degrees after z.
You should be able to easily modify it, or copy the relevant functions to make it work on your side.
You said you already tried format_coord, maybe you forgot to set the funtion? (second last line)
"""
Show how to modify the coordinate formatter to report the image "z"
value of the nearest pixel given x and y
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
X = 10*np.random.rand(5, 3)
fig, ax = plt.subplots()
ax.imshow(X, cmap=cm.jet, interpolation='nearest')
numrows, numcols = X.shape
def format_coord(x, y):
col = int(x + 0.5)
row = int(y + 0.5)
if col >= 0 and col < numcols and row >= 0 and row < numrows:
#get z from your data, given x and y
z = X[row, col]
#this only leaves two decimal points for readability
[x,y,z]=map("{0:.2f}".format,[x,y,z])
#change return string of x,y and z to whatever you want
return 'x='+str(x)+', y='+str(y)+', z='+str(z)+" degrees"
else:
return 'x=%1.4f, y=%1.4f' % (x, y)
#Set the function as the one used for display
ax.format_coord = format_coord
plt.show()
Emmm, Sarith, I am also facing the problem but a little trick helped me out. I still used this function:
your_imshow.format_coord = lambda x, y: 'x={:.5f}, y={:.2f}, amplitude='.format(x,y)
It pretends to add label before the bracket. Yeah, it is an easy but not essential way to change the presentation form, but it works to me. I hope this could also benefit others.

Adding self-edge in a graphical plot with Python's `daft` library

I want to add an edge that link beta to beta, in the following plot:
Is generated the following code using DAFT:
from matplotlib import rc
rc("font", family="serif", size=12)
rc("text", usetex=True)
import daft
pgm = daft.PGM([2.3, 3.05], origin=[0.3, 0.3], observed_style="inner")
# Hierarchical parameters.
pgm.add_node(daft.Node("beta", r"$\beta$", 1.5, 2))
# Latent variable.
pgm.add_node(daft.Node("w", r"$w_n$", 1, 1))
# Data.
pgm.add_node(daft.Node("x", r"$x_n$", 2, 1, observed=True))
# Add in the edges.
pgm.add_edge("beta", "beta") # Attempting to create a self-edge, but no effect!
pgm.add_edge("w", "x")
pgm.add_edge("w", "w")
pgm.add_edge("w", "beta")
pgm.add_edge("beta", "x")
# Render and save.
pgm.render()
pgm.figure.savefig("nogray.pdf")
But why it doesn't work? Especially with this line pgm.add_edge("beta", "beta").
I welcome other suggestions other than Daft, as long as it is under Python.
If you look at the source code here from line 301 (the Edge class) you will see that all lines are given by straight lines based on the coordinates ([x, x + dx], [y, y + dy]) as shown below in the code for an undirected edge:
x, y, dx, dy = self._get_coords(ctx)
# Plot the line.
line = ax.plot([x, x + dx], [y, y + dy], **p)
return line
As such there doesn't appear to be a way of defining a self-edge (as such an edge would need to be curved in order to curve back to the same node).
As far as an alternative library you may want to look at networkx, the following docs show the use with self loops. Alternatively you could raise an issue on the DAFT Github.

Maya Python: Apply Transformation Matrix

I have been looking for thi answer but i don't seem to figure it out anywhere, so i hope i could get my answer here...
I'm in Maya Python API and i want to apply a transformation Matrix to a mesh.
This is how i made the mesh:
mesh = om.MFnMesh()
ShapeMesh = cmds.group(em=True)
parentOwner = get_mobject( ShapeMesh )
meshMObj = mesh.create(NumVerts, len(FaceCount), VertArray, FaceCount, FaceArray ,parentOwner)
cmds.sets( ShapeMesh, e=True,forceElement='initialShadingGroup')
defaultUVSetName = ''
defaultUVSetName = mesh.currentUVSetName(-1)
mesh.setUVs ( UArray, VArray, defaultUVSetName )
mesh.assignUVs ( FaceCount, FaceArray, defaultUVSetName )
This is how i create the TFM:
m = struct.unpack("<16f",f.read(64))
mm = om.MMatrix()
om.MScriptUtil.createMatrixFromList(m,mm)
mt = om.MTransformationMatrix(mm)
Basically i read 16 floats and convert them into a Transformation Matrix, however i don't know how to apply the mt matrix to my mesh...
I managed to get the Position,Rotation and Scale from this though, maybe it helps, this way:
translate = mt.translation(om.MSpace.kWorld)
rotate = mt.rotation().asEulerRotation()
scaleUtil = om.MScriptUtil()
scaleUtil.createFromList([0,0,0],3)
scaleVec = scaleUtil.asDoublePtr()
mt.getScale(scaleVec,om.MSpace.kWorld)
scale = [om.MScriptUtil.getDoubleArrayItem(scaleVec,i) for i in range(0,3)]
Now my last step comes in applying this Matrix to the mesh, but i can't find a good way to do it, does someone know how to do this on maya?
Thanks in advance:
Seyren.
Not sure what you mean by applying the matrix to your mesh, but if you want to update the position of each point by transforming them with that matrix, then here you go for a given MFnMesh mesh and a given MMatrix matrix:
import banana.maya
banana.maya.patch()
from maya import OpenMaya
mesh = OpenMaya.MFnMesh.bnn_get('pCubeShape1')
matrix = OpenMaya.MMatrix()
points = OpenMaya.MPointArray()
mesh.getPoints(points)
for i in range(points.length()):
points.set(points[i] * matrix, i)
mesh.setPoints(points)
If you don't want to directly update the points of the mesh, then you need to apply the matrix to the transformation node by retrieving its parent transform and using the MFnTransform::set() method.
Note that I've used in my code snippet a set of extensions that I've wrote and that might be helpful if you're using the Maya Python API. The code is available on GitHub and it also comes with a documentation to give you an idea.

annotating many points with text in mayavi using mlab

I'm trying to annotate points plotted with the points3d() function using mayavi.mlab.
Each point is associated with a label which I would like to plot next to the points using the text3d() function. Plotting the points is fast, however the mlab.text3d() function does not seem to accept arrays of coordinates, so I have to loop over the points and plot the text individually, which is very slow:
for i in xrange(0, self.n_labels):
self.mlab_data.append(
mlab.points3d( pX[self.labels == self.u_labels[i], 0],
pX[self.labels == self.u_labels[i], 1],
pX[self.labels == self.u_labels[i], 2],
color=self.colours[i],
opacity=1,
scale_mode="none",
scale_factor=sf ) )
idcs, = np.where(self.labels == self.u_labels[i])
for n in idcs.flatten():
mlab.text3d( pX[n, 0],
pX[n, 1],
pX[n, 2],
"%d" % self.u_labels[i],
color=self.colours[i],
opacity=1,
scale=sf )
Any ideas how I could speed this up? Also, is it possible to add a legend (as for instance in matplotlib), I couldn't find anything in the docs.
Thanks,
Patrick
The way you are doing it above will render the scene every time you plot a point or text. This is slow. You can disable the scene rendering, do the plotting and then render the scene by figure.scene.disable_render = True/False:
import scipy
from mayavi import mlab
X = 100 * scipy.rand(100, 3)
figure = mlab.figure('myfig')
figure.scene.disable_render = True # Super duper trick
mlab.points3d(X[:,0], X[:,1], X[:,2], scale_factor=0.4)
for i, x in enumerate(X):
mlab.text3d(x[0], x[1], x[2], str(i), scale=(2, 2, 2))
figure.scene.disable_render = False # Super duper trick
I use this trick and others in Figure class in morphic Viewer https://github.com/duanemalcolm/morphic/blob/master/morphic/viewer.py
Another good trick in the code is to reuse existing objects, i.e., if you've plotted the text already, don't replot them, just update their position and text attributes. This means saving the mlab object. You can see how I do this in morphic.Viewer.

Issues with 2D-Interpolation in Scipy

In my application, the data data is sampled on a distorted grid, and I would like to resample it to a nondistorted grid. In order to test this, I wrote this program with examplary distortions and a simple function as data:
from __future__ import division
import numpy as np
import scipy.interpolate as intp
import pylab as plt
# Defining some variables:
quadratic = -3/128
linear = 1/16
pn = np.poly1d([quadratic, linear,0])
pixels_x = 50
pixels_y = 30
frame = np.zeros((pixels_x,pixels_y))
x_width= np.concatenate((np.linspace(8,7.8,57) , np.linspace(7.8,8,pixels_y-57)))
def data(x,y):
z = y*(np.exp(-(x-5)**2/3) + np.exp(-(x)**2/5) + np.exp(-(x+5)**2))
return(z)
# Generating grid coordinates
yt = np.arange(380,380+pixels_y*4,4)
xt = np.linspace(-7.8,7.8,pixels_x)
X, Y = np.meshgrid(xt,yt)
Y=Y.T
X=X.T
Y_m = np.zeros((pixels_x,pixels_y))
X_m = np.zeros((pixels_x,pixels_y))
# generating distorted grid coordinates:
for i in range(pixels_y):
Y_m[:,i] = Y[:,i] - pn(xt)
X_m[:,i] = np.linspace(-x_width[i],x_width[i],pixels_x)
# Sample data:
for i in range(pixels_y):
for j in range(pixels_x):
frame[j,i] = data(X_m[j,i],Y_m[j,i])
Y_m = Y_m.flatten()
X_m = X_m.flatten()
frame = frame.flatten()
##
Y = Y.flatten()
X = X.flatten()
ipf = intp.interp2d(X_m,Y_m,frame)
interpolated_frame = ipf(xt,yt)
At this point, I have to questions:
The code works, but I get the the following warning:
Warning: No more knots can be added because the number of B-spline coefficients
already exceeds the number of data points m. Probably causes: either
s or m too small. (fp>s)
kx,ky=1,1 nx,ny=54,31 m=1500 fp=0.000006 s=0.000000
Also, some interpolation artifacts appear, and I assume that they are related to the warning - Do you guys know what I am doing wrong?
For my actual applications, the frames need to be around 500*100, but when doing this, I get a MemoryError - Is there something I can do to help that, apart from splitting the frame into several parts?
Thanks!
This problem is most likely related to the usage of bisplrep and bisplev within interp2d. The docs mention that they use a smooting factor of s=0.0 and that bisplrep and bisplev should be used directly if more control over s is needed. The related docs mention that s should be found between (m-sqrt(2*m),m+sqrt(2*m)) where m is the number of points used to construct the splines. I had a similar problem and found it solved when using bisplrep and bisplev directly, where s is only optional.
For 2d interpolation,
griddata
is solid, local, fast.
Take a look at problem-with-2d-interpolation-in-scipy-non-rectangular-grid on SO.
You might want to look at the following interp method in basemap:
mpl_toolkits.basemap.interp
http://matplotlib.sourceforge.net/basemap/doc/html/api/basemap_api.html
unless you really need spline-based interpolation.

Categories

Resources