How to set absolute position of GLMeshItem in 3D pyqtgraph implementation - python

I am building a visualizer for some data and want to use 3D spheres plotted in pyqtgraphs 3D OpenGL components to represent targets identified within the data provided.
I am able to generate the spheres and move them using GLMeshItem.translate() commands, however I am not able to find a convenient way of setting coordinates of the sphere without first getting the current position of said spheres through a call to .transform() and then generating a translate command from it's current position to the new absolute coordinates I would like it to be moved to. It may be that that is the only way to accomplish this, I just suspect there is a more direct set the mesh items absolute coordinates that I just can't seem to identify.
The following code shows a basic framework for what I am doing, and also the current method I am using to move the sphere.
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import pyqtgraph.opengl as gl
import numpy as np
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.showMaximized()
w.setWindowTitle('pyqtgraph example: GLMeshItem')
w.setCameraPosition(distance=40)
g = gl.GLGridItem()
g.scale(2,2,1)
w.addItem(g)
verts = np.array([
[0, 0, 0],
[2, 0, 0],
[1, 2, 0],
[1, 1, 1],
])
faces = np.array([
[0, 1, 2],
[0, 1, 3],
[0, 2, 3],
[1, 2, 3]
])
colors = np.array([
[1, 0, 0, 0.3],
[0, 1, 0, 0.3],
[0, 0, 1, 0.3],
[1, 1, 0, 0.3]
])
md = gl.MeshData.sphere(rows=4, cols=4)
colors = np.ones((md.faceCount(), 4), dtype=float)
colors[::2,0] = 0
colors[:,1] = np.linspace(0, 1, colors.shape[0])
md.setFaceColors(colors)
m3 = gl.GLMeshItem(meshdata=md, smooth=False)#, shader='balloon')
w.addItem(m3)
target = gl.MeshData.sphere(4,4,10)
targetMI = gl.GLMeshItem(meshdata = target, drawFaces = True,smooth = False)
w.addItem(targetMI)
while(1):
targetMI.translate(0.1,0,0)
app.processEvents()
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
As can be seen in this example. translate works fine for moving relative to the current position. I am just curious as to whether there is a method for doing an absolute position move on the GLMeshItem (in this case targetMI) such that I could make it move to a coordinate without having to first get the transform and then calculating the translate required to move to the desired coordinates.

An option is to reset the item’s transform to an identity transformation by resetTransform(), before you set the absolute position by translate(). e.g:
targetMI.resetTransform()
targetMI.translate(10, 0, 0)

Related

python function to count nonzero patches in array

For a given array (1 or 2-dimensional) I would like to know, how many "patches" there are of nonzero elements. For example, in the array [0, 0, 1, 1, 0, 1, 0, 0] there are two patches.
I came up with a function for the 1-dimensional case, where I first assume the maximal number of patches and then decrease that number if a neighbor of a nonzero element is nonzero, too.
def count_patches_1D(array):
patches = np.count_nonzero(array)
for i in np.nonzero(array)[0][:-1]:
if (array[i+1] != 0):
patches -= 1
return patches
I'm not sure if that method works for two dimensions as well. I haven't come up with a function for that case and I need some help for that.
Edit for clarification:
I would like to count connected patches in the 2-dimensional case, including diagonals. So an array [[1, 0], [1, 1]] would have one patch as well as [[1, 0], [0, 1]].
Also, I am wondering if there is a build-in python function for this.
The following should work:
import numpy as np
import copy
# create an array
A = np.array(
[
[0, 1, 1, 1, 0, 1],
[0, 0, 1, 0, 0, 0],
[1, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 1],
[0, 0, 1, 0, 0, 1]
]
)
def isadjacent(pos, newpos):
"""
Check whether two coordinates are adjacent
"""
# check for adjacent columns and rows
return np.all(np.abs(np.array(newpos) - np.array(pos)) < 2):
def count_patches(A):
"""
Count the number of non-zero patches in an array.
"""
# get non-zero coordinates
coords = np.nonzero(A)
# add them to a list
inipatches = list(zip(*coords))
# list to contain all patches
allpatches = []
while len(inipatches) > 0:
patch = [inipatches.pop(0)]
i = 0
# check for all points adjacent to the points within the current patch
while True:
plen = len(patch)
curpatch = patch[i]
remaining = copy.deepcopy(inipatches)
for j in range(len(remaining)):
if isadjacent(curpatch, remaining[j]):
patch.append(remaining[j])
inipatches.remove(remaining[j])
if len(inipatches) == 0:
break
if len(inipatches) == 0 or plen == len(patch):
# nothing added to patch or no points remaining
break
i += 1
allpatches.append(patch)
return len(allpatches)
print(f"Number of patches is {count_patches(A)}")
Number of patches is 5
This should work for arrays with any number of dimensions.

How to plot band structure?

I'm running a Vasp calculation with pyiron. I can easily plot the total density of states by accessing the ElectronicStructure and Dos objects, e.g.
from pyiron.project import Project
pr = Project('tmp')
pr.remove_jobs(recursive=True)
vasp = pr.create_job(pr.job_type.Vasp, 'vasp')
vasp.structure = pr.create_ase_bulk('Al')
vasp.run()
dos = vasp.get_electronic_structure().get_dos()
dos.plot_total_dos()
Is there a similarly convenient way of plotting the band structure hiding somewhere?
Although there isn't a direct plot function, the band structure can be plot using the eigenvalue matrix
import matplotlib.pylab as plt
plt.plot(vasp.get_electronic_structure().eigenvalue_matrix);
Or to plot it manually you could use:
# The trace is system dependent, in this example we use:
trace = np.array([[0, 0, 0], # Gamma
[1, 0, 0], # X
[1, 1, 0], # M
[0, 0, 0], # Gamma
[0, 0, 1], # Z
[1, 0, 1], # R
[1, 1, 1], # A
[0, 0, 1]]) # Z
label_ticks = ['$\Gamma$', 'X', 'M', '$\Gamma$', 'Z', 'R', 'A', 'Z']
energy = ham['output/electronic_structure/eig_matrix']
E_f = ham_chg['output/electronic_structure/efermi']
energy -= E_f
n_kpoints = len(energy)
n_trace = int(n_kpoints / (len(trace)-1))
normal_ticks = [i*n_trace for i in range(len(trace))]
plt.axhline(y=0, ls='--', color='k')
plt.plot(energy, 'r-')
plt.xlim(normal_ticks[0], normal_ticks[-1])
plt.xticks(normal_ticks, label_ticks)
plt.grid(axis='x')
plt.ylabel("Energy - $E_F$ [eV]")
plt.ylim(-1, 1);
This requires two VASP calculation, first you calculate the charge density:
ham_1.write_charge_density = True
And after this job is executed you use the charge density to calculate the band structure by restarting from the previous job:
ham_2 = ham.restart_from_charge_density(job_name="job_band", icharg=11)
But to my knowledge we currently have no automated functionality for this.
If you are using VASP to calculate the band structure, a friendly tool named VASPKIT will help you plot it easily! Here's a link!
After your installation is complete, open vaspkit in your folder, then type: 21
211) Band-Structure
212) Projected Band-Structure of Only-One-Selected Atom
213) Projected Band-Structure of Each Element
214) Projected Band-Structure of Selected Atoms
215) Projected Band-Structure by Element-Weights
216) The Sum of Projected Band for Selected Atoms and Orbitals
Then you can follow the prompts to automatically draw the energy band diagram.

Create matplotlib-style 3d scattergraph z-axis in Vispy

I am conducting PhD research into extremely high density lidar point clouds(1M points +), and am having real difficulty plotting these files on a 3D scattergraph. Matplotlib isn't optimised for datasets that large, so I am attempting to use Vispy to achieve this. Right now, I am having real trouble trying to get a scattergraph-style plot up and running, so I can start building my pipeline.
I'm after something like this:
matplotlib 3D scattergraph
Using Vispy, I am finding it difficult to apply a z-axis and I am having trouble finding what part of documentation can help me with this. Here's my code:
"""
This example demonstrates the use of the SurfacePlot visual.
"""
import sys
import numpy as np
from vispy import app, scene
from vispy.util.filter import gaussian_filter
canvas = scene.SceneCanvas(keys='interactive', bgcolor='w')
view = canvas.central_widget.add_view()
view.camera = scene.TurntableCamera(up='z', fov=60)
# Simple surface plot example
# x, y values are not specified, so assumed to be 0:50
z = np.random.normal(size=(250, 250), scale=200)
z[100, 100] += 50000
z = gaussian_filter(z, (10, 10))
print("This is z {0}".format(z))
p1 = scene.visuals.SurfacePlot(z=z, color=(0.3, 0.3, 1, 1))
p1.transform = scene.transforms.MatrixTransform()
p1.transform.scale([1/249., 1/249., 1/249.])
p1.transform.translate([-0.5, -0.5, 0])
view.add(p1)
# p1._update_data() # cheating.
# cf = scene.filters.ZColormapFilter('fire', zrange=(z.max(), z.min()))
# p1.attach(cf)
xax = scene.Axis(pos=[[-0.5, -0.5], [0.5, -0.5]], tick_direction=(0, -1),
font_size=16, axis_color='k', tick_color='k', text_color='k',
parent=view.scene)
xax.transform = scene.STTransform(translate=(0, 0, -0.2))
yax = scene.Axis(pos=[[-0.5, -0.5], [-0.5, 0.5]], tick_direction=(-1, 0),
font_size=16, axis_color='k', tick_color='k', text_color='k',
parent=view.scene)
yax.transform = scene.STTransform(translate=(0, 0, -0.2))
zax = scene.Axis(pos=[[1.0, 1.0], [-1.0, 1.0]], tick_direction=(-1, 0),
font_size=16, axis_color='k', tick_color='k', text_color='k',
parent=view.scene)
zax.transform = scene.STTransform(translate=(0.0, 0.0, -0.2))
# Add a 3D axis to keep us oriented
axis = scene.visuals.XYZAxis(parent=view.scene)
if __name__ == '__main__':
canvas.show()
if sys.flags.interactive == 0:
app.run()
And it makes a graph that looks like this:
vispy_example_plot
You can see in the above image my problem. I want to orient the third axis perpendicular to the others, and a grid to the to the walls of the plot, so the data is more defined.
Unfortunately sub-sampling and other tricks to display the data using matplotlib, are not optimal for me because I have to display the original data, rather than showing a subset. Of course, if there are other strategies I can use to display my data as I would like, then I am all ears.
Thanks in advance, hopefully someone can help
i faced the same issue doing research on camera depth images and cfar radar data. since vispy is supporting 2d only for axes, you could rotate the z-axis after creation. here, a slightly modified version of your code:
import sys
import numpy as np
from vispy import app, scene
from vispy.util.filter import gaussian_filter
canvas = scene.SceneCanvas(keys='interactive', bgcolor='w')
view = canvas.central_widget.add_view()
view.camera = scene.TurntableCamera(up='z', fov=60)
z = np.random.normal(size=(250, 250), scale=200)
z[100, 100] += 50000
z = gaussian_filter(z, (10, 10))
p1 = scene.visuals.SurfacePlot(z=z, color=(0.3, 0.3, 1, 1))
p1.transform = scene.transforms.MatrixTransform()
p1.transform.scale([1/249., 1/249., 1/249.])
view.add(p1)
xax = scene.Axis(pos=[[0, 0], [1, 0]], tick_direction=(0, -1), axis_color='r', tick_color='r', text_color='r', font_size=16, parent=view.scene)
yax = scene.Axis(pos=[[0, 0], [0, 1]], tick_direction=(-1, 0), axis_color='g', tick_color='g', text_color='g', font_size=16, parent=view.scene)
zax = scene.Axis(pos=[[0, 0], [-1, 0]], tick_direction=(0, -1), axis_color='b', tick_color='b', text_color='b', font_size=16, parent=view.scene)
zax.transform = scene.transforms.MatrixTransform() # its acutally an inverted xaxis
zax.transform.rotate(90, (0, 1, 0)) # rotate cw around yaxis
zax.transform.rotate(-45, (0, 0, 1)) # tick direction towards (-1,-1)
if __name__ == '__main__':
canvas.show()
if sys.flags.interactive == 0:
app.run()
Regards

VTK rendering 2D mesh in python

so i'm trying to render a 2D mesh using vtk (in python). I have a list
of tuples containing all the points and also a list of tuples containing the
points of each cell. Just to experiment, I tried to create a polydata object
of a square with 4 elements and render it, but i ended up with this:
I would like it to show the lines connecting the nodes (like a wireframe)
instead of solid square..
This is the code to produce the image above:
def main2():
#Array of vectors containing the coordinates of each point
nodes = np.array([[0, 0, 0], [1, 0, 0], [2, 0, 0], [2, 1, 0], [2, 2, 0],
[1, 2, 0], [0, 2, 0], [0, 1, 0], [1, 1, 0]])
#Array of tuples containing the nodes correspondent of each element
elements = np.array([(0, 1, 8, 7), (7, 8, 5, 6), (1, 2, 3, 8), (8, 3, 4,
5)])
#Make the building blocks of polyData attributes
Mesh = vtk.vtkPolyData()
Points = vtk.vtkPoints()
Cells = vtk.vtkCellArray()
#Load the point and cell's attributes
for i in range(len(nodes)):
Points.InsertPoint(i, nodes[i])
for i in range(len(elements)):
Cells.InsertNextCell(mkVtkIdList(elements[i]))
#Assign pieces to vtkPolyData
Mesh.SetPoints(Points)
Mesh.SetPolys(Cells)
#Mapping the whole thing
MeshMapper = vtk.vtkPolyDataMapper()
if vtk.VTK_MAJOR_VERSION <= 5:
MeshMapper.SetInput(Mesh)
else:
MeshMapper.SetInputData(Mesh)
#Create an actor
MeshActor = vtk.vtkActor()
MeshActor.SetMapper(MeshMapper)
#Rendering Stuff
camera = vtk.vtkCamera()
camera.SetPosition(1,1,1)
camera.SetFocalPoint(0,0,0)
renderer = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(renderer)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
renderer.AddActor(MeshActor)
renderer.SetActiveCamera(camera)
renderer.ResetCamera()
renderer.SetBackground(1,1,1)
renWin.SetSize(300,300)
#Interact with data
renWin.Render()
iren.Start()
main2()
I would also like to know if it's possible to have a gridline as the
background of the render window, instead of a black color, just like this:
Thanks in advance!
You can use MeshActor.GetProperty().SetRepresentationToWireframe() (https://www.vtk.org/doc/nightly/html/classvtkProperty.html#a2a4bdf2f46dc499ead4011024eddde5c) to render the actor as wireframe, or MeshActor.GetProperty().SetEdgeVisibility(True) to render it as solid with edges rendered as lines.
Regarding the render window background, I don't know.
Thanks to #MafiaSkafia I created what I was looking for, 2D grid for 3D purposes, maybe someone will be looking for something like this too.
# plane
planeSource = vtk.vtkPlaneSource()
planeSource.SetOrigin(-100.0, -100.0, 0.0)
# planeSource.SetNormal(0.0, 0.0, 1.0)
planeSource.SetResolution(100,100)
planeSource.SetPoint1(100.0,-100.0,0.0)
planeSource.SetPoint2(-100.0,100.0,0.0)
planeSource.Update()
plane = planeSource.GetOutput()
# Create a mapper and actor
mapperP = vtk.vtkPolyDataMapper()
mapperP.SetInputData(plane)
actorP = vtk.vtkActor()
actorP.SetMapper(mapperP)
actorP.GetProperty().SetColor(0,0,0)
actorP.GetProperty().EdgeVisibilityOn() # showing mesh
actorP.GetProperty().SetEdgeColor(1,1,1)
actorP.GetProperty().SetOpacity(0.2) # transparency
...
renderer.AddActor(actorP)

Coding a circular filter in Python

I found a code snippet for making a circular filter using scipy and I'd like to understand how it works. I know there's a better one in skimage, but I'm interested in what's going on in this one.
from scipy.ndimage.filters import generic_filter as gf
# Define physical shape of filter mask
def circular_filter(image_data, radius):
kernel = np.zeros((2*radius+1, 2*radius+1))
y, x = np.ogrid[-radius:radius+1, -radius:radius+1]
mask = x**2 + y**2 <= radius**2
kernel[mask] = 1
filtered_image = gf(image_data, np.median, footprint = kernel)
return filtered_image
But I'm not sure I understand perfectly what's going on. In particular, what exactly do the lines
y, x = np.ogrid[-radius:radius+1, -radius:radius+1]
mask = x**2 + y**2 <= radius**2
kernel[mask] = 1
do?
I posted this as an answer to one of my previous questions, but it wasn't replied to, so I'm posting it as a new question.
Looking at your code in detail:
kernel = np.zeros((2*radius+1, 2*radius+1))
y, x = np.ogrid[-radius:radius+1, -radius:radius+1]
mask = x**2 + y**2 <= radius**2
kernel[mask] = 1
The first line:
kernel = np.zeros((2*radius+1, 2*radius+1))
creates a 2-d array of zeros, with a center point and "radius" points on either side. For radius = 2, you would get:
# __r__ +1 __r__
[ 0, 0, 0, 0, 0, ] #\
[ 0, 0, 0, 0, 0, ] #_} r
[ 0, 0, 0, 0, 0, ] # +1
[ 0, 0, 0, 0, 0, ] #\
[ 0, 0, 0, 0, 0, ] #_} r
Next, you get two arrays from the open mesh grid created by numpy.ogrid. Mesh grids are a "trick" in numpy that involves storing a "parallel" array or matrix that holds the x or y coordinate of a particular cell at the location of that cell.
For example, a y-mesh grid might look like this:
[ 0, 0, 0, 0, 0, ]
[ 1, 1, 1, 1, 1, ]
[ 2, 2, 2, 2, 2, ]
[ 3, 3, 3, 3, 3, ]
[ 4, 4, 4, 4, 4, ]
And an x-mesh grid might look like this:
[ 0, 1, 2, 3, 4, ]
[ 0, 1, 2, 3, 4, ]
[ 0, 1, 2, 3, 4, ]
[ 0, 1, 2, 3, 4, ]
[ 0, 1, 2, 3, 4, ]
If you look at them, you'll realize that Y_grid[x][y] == y and X_grid[x][y] == x which is so often useful that it has more than one numpy function to support it. ;-)
An open mesh grid is similar to a closed one, except that it only has "one dimension." That is, instead of a pair of (for example) 5x5 arrays, you get a 1x5 array and a 5x1 array. That's what ogrid does - it returns two open grids. The values are from -radius to radius+1, according to python rules (meaning the radius+1 is left out):
y, x = np.ogrid[-radius:radius+1, -radius:radius+1]
So y is a numpy array storing from e.g., -2..2 (inclusive), and x is an array from -2..2 inclusive. The next step is to build a boolean mask - that is, an array full of boolean values. As you know, when you operate on a numpy array, you get another numpy array. So involving two arrays in an expression with a constant produces another array:
mask = x**2 + y**2 <= radius**2
The value of mask is going to be a 2-color bitmap, where one color is "True" and the other color is "False." The bitmap will describe a solid circle or disk. (Because of the <= relation. Remember that x and y contain -2..2, not 0..4.)
Finally, you convert from type Boolean to int by using the masking array as an overlay on the kernel array (of zeroes), setting the zeroes to ones whenever the mask is "True":
kernel[mask] = 1
At this point, kernel looks like:
# __r__ +1 __r__
[ 0, 0, 1, 0, 0, ] #\
[ 0, 1, 1, 1, 0, ] #_} r
[ 1, 1, 1, 1, 1, ] # +1
[ 0, 1, 1, 1, 0, ] #\
[ 0, 0, 1, 0, 0, ] #_} r
I'm not familiar with SciPy but I'll give it a shot trying to explain the basic concepts.
This entire function's purpose is to alter the original image by applying a filter. This filter could do a lot of things, from changing the contrast of the image, or adding special effects, etc.
Let's go through the different lines:
kernel = np.zeros((2*radius+1, 2*radius+1))
In this line, a copy of the image data is being created, but with all the data being zeros (hence the zeros function is being used). This is so the mask can be applied later onto it.
y, x = np.ogrid[-radius:radius+1, -radius:radius+1]
This is creating what is known as a "meshgrid" or a multi-dimensional grid. This is to create the circular "mask". Just like how on a graph, x and y axes have evenly spaced scaling, the same is necessary here in the meshgrid.
The x and y variables in this case store evenly spaced values that serve as the axes' scaling.
mask = x**2 + y**2 <= radius**2
Here, a "mask" is being created. A mask will serve as the region in the image to be protected from the filter, so as to not alter any original data. Notice how x and y variables are used here in a Pythagorean inequality (important to see that it's not just a circle but a disk), just like how they would be in a mathematical sense. This will create a disk with the given radius that is now considered the mask. The mask variable now contains all coordinates (x,y) where the original data values should not be altered.
kernel[mask] = 1
This is where the mask is now applied to the copy of the image that was created earlier. Now, there is a perfect copy of the image (i.e. same dimensions) but with a disk-like "mask" that "protects" the original data from being altered. This is why all the points covered by the disk is set to 1. Also, notice how the dimensions of kernel and mask match. Both are multi-dimensional. The rest of values in the image copy are still set to zero, as was done in the first line.
filtered_image = gf(image_data, np.median, footprint = kernel)
This is final part where everything is pieced together. There is the original data stored in image_data and there is the kernel, which is the image copy with the mask applied on it indicating where the data should not be altered. Both of them are passed as parameters into the actual filter function gf (stands for generic filter) and the output is a new filtered image.
This is a core concept in image filtering and if you want to learn more about it, I suggest starting out by learning basic signal processing concepts. Signal processing courses cover the mathematics of how these concepts work, but are usually explained in really abstract mathematics because this concept can be applied to numerous different examples.

Categories

Resources