3D deformation grids visualisation. #450
-
Hello ! I am trying to implement a tool or API to visualize deformation grids in 3D. In 2D the visualization is straight forward. You can see some examples in one of my repository jupyters. I apologize in advance for the blurriness of my question but I am a bit lost here. After battling with matplotlib, I got the advice from my supervisors to try other libraries as the 3D of matplotlib isn't really 3D. I spotted vedo, was amazed by the beautiful plots and tried to wrap my head around it. I would like to plot different transparent surfaces as in the thinplate example, but I cannot find how to do it from a given numpy array. Adding some steamlines may also add some understanding of the deformation. I am working with deformations that are functions in the PyTorch convention, and can be generated like so : # Dimension definition
Tau,D,H,W = (1,100,150,200) # we will use a temporal vector field at a given time
x,y,z = np.mgrid[:D,:H,:W]
grid = np.stack([x,y,z],axis=-1)[None]
v = np.zeros((Tau,D,H,W,3)) # vector field initialization
coef_mat = np.exp(-5*((grid[0,...,0])**2 +
(grid[0,...,1])**2 +
(grid[0,...,2])**2))[...,None]
v[0] = coef_mat*np.stack(
(grid[0,...,1],
-grid[0,...,2],
np.ones(grid[0,...,0].shape)),
axis=3)
deformation = (grid + v[0][None]) Thank you for reading ! |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 4 replies
-
Hi Anton, from vedo import *
import numpy as np
xcoords = np.arange(0, 2, 0.2)
ycoords = np.arange(0, 1, 0.2)
sqrtx = sqrt(xcoords) # to make them non-uniform
grid1 = Grid(sx=sqrtx, sy=ycoords).lineWidth(3)
print(grid1.points())
grid2 = Grid(sx=sqrtx, sy=ycoords).lineWidth(3)
grid2.rotateY(15).shift(0,0.5,0.8)
arrows = Arrows(grid1, grid2, s=0.3).lighting('off')
grid = merge(grid1, grid2) # optionally fuse them into a single object
plt = show(grid, arrows, axes=1) # returns a Plotter object For deformations you need to specify where specific points in space displace to interpolate all space displacement. It does not need to be a point of the Mesh. Hope this helps. |
Beta Was this translation helpful? Give feedback.
-
You need to find the mapping between your from vedo import *
import numpy as np
# Generating a given 3D deformation
Tau,D,H,W = (1,10,15,20) # we will use a temporal vector field at a given time
x,y,z = np.meshgrid(np.linspace(-1,1,H),
np.linspace(-1,1,D),
np.linspace(-1,1,W))
grid = np.stack([x,y,z],axis=-1)[None]
v = np.zeros((Tau,D,H,W,3)) # vector field initialization
coef_mat = np.exp(-5*((grid[0,...,0])**2 +
(grid[0,...,1])**2 +
(grid[0,...,2])**2))[...,None]
v[0] = coef_mat*np.stack(
(grid[0,...,1],
-grid[0,...,2],
np.ones(grid[0,...,0].shape)), axis=3)
deformation = (grid + v[0][None]) # deformation shape is [1,D,H,W,3]
# Plots
points = deformation[0,5].reshape((H*W,3))
faces = [[0,1,21,20], [1,2,22,21], [21,22,42,41], [83,84,104,103]]
deformed_grid = Mesh([points, faces]).lineWidth(1)
show(deformed_grid,
Points(points, c='red6', r=5),
deformed_grid.labels('id', rotX=-45, scale=.02).c('yellow'),
deformed_grid.labels('id', rotX=-90, scale=.03, justify='center', cells=1).shift(dy=.005),
bg='k', bg2='lg', axes=9,
) this loop might be useful (or might try to do something more clever in numpy): Line 2216 in 263fbf7 PS: if you need triangular faces just add |
Beta Was this translation helpful? Give feedback.
-
Cool ! that was fast :) for cosmetics you may add |
Beta Was this translation helpful? Give feedback.
-
Hello again, So here is an implementation of the viewer as I described it in the question above. I am sure we can still improve it. What I have noted is :
Don't hesitate to give me some feedback on how to improve the class :) import vedo
import numpy as np
vedo.embedWindow('ipyvtk')
class deformation_grid3D_vedo:
def __init__(self,max_resolution=30,color='yellow',alpha=0.5):
""" When you initialize the class you have the opportunity
to select some constants that will define the plot.
:param max_resolution: int or 3sized tuple of int. Maximum number of
mesh summits in each dimension. If type(max_resolution) == int then
its value will be the maximum in each dimension.
:param color: Color of the surfaces
:param alpha: alpha value of the surfaces
"""
self.max_resolution = max_resolution
self.color = color
self.alpha = alpha
def _construct_surface_coord(self, deformation_slice):
""" takes a deformation slice of the selected dimensions and
prepare it to be plotted by vedo
:param defomation_slice: [dim_1,dim_2,3]
:return: an array with point coordinates and face links.
"""
if deformation_slice.shape[-1] != 3:
raise TypeError("deformation slice must have shape (dim_1,dim_2,3) got "+
str(deformation_slice.shape))
dim_1,dim_2,_ = deformation_slice.shape
points = deformation_slice.reshape((dim_1*dim_2,3))
coord = np.arange(dim_1*dim_2).reshape((dim_1,dim_2))
faces = np.zeros((dim_1*dim_2,4))
for d in range(dim_1-1):
faces[d*dim_2:d*dim_2 + (dim_2-1),:] = np.stack(
[coord[d,:-1],coord[d,1:],coord[d+1,1:],coord[d+1,:-1]],
axis=1
)
return [points,faces]
def _slicer(self, deformation, dim, ind):
""" Slice the deformation in the given index at the given dimension.
It also subsample the deformation at the max_resolution set by the user
in init, default value is 30.
:param deformation: [1,D,H,W,3] numpy array
:param dim: int \in \{0,1,2\}
:param ind: int
:return: sliced deformation
"""
_,D,H,W,_ =deformation.shape
if type(self.max_resolution) == np.int:
self.max_resolution = (self.max_resolution,)*3
if dim == 0 :
h_sampler = np.linspace(0,H-1,min(H,self.max_resolution[1]),dtype=np.long)
w_sampler = np.linspace(0,W-1,min(W,self.max_resolution[2]),dtype=np.long)
return deformation[0,ind,h_sampler,:,:][:,w_sampler,:]
elif dim == 1:
d_sampler = np.linspace(0,D-1,min(D,self.max_resolution[0]),dtype=np.long)
w_sampler = np.linspace(0,W-1,min(W,self.max_resolution[2]),dtype=np.long)
return deformation[0,d_sampler,ind,:,:][:,w_sampler,:]
elif dim == 2:
d_sampler = np.linspace(0,D-1,min(D,self.max_resolution[0]),dtype=np.long)
h_sampler = np.linspace(0,H-1,min(H,self.max_resolution[1]),dtype=np.long)
return deformation[0,d_sampler,:,ind,:][:,h_sampler,:]
else:
raise IndexError("dim has to be {0,1,2} got "+str(dim))
def forward(self,deformation,dim=0,n_surf=5):
if len(deformation.shape) != 5 and deformation.shape[-1] !=3:
raise TypeError("deformation shape must be of the form [1,D,H,W,3]",
"got array of dim "+ str(deformation.shape))
if deformation.shape[0] > 1:
deformation = deformation[0][None]
print("Warning, only first Batch dimension will be considered")
_,D,H,W,_ =deformation.shape
surf_indexes = np.linspace(0,deformation.shape[dim+1]-1,n_surf,
dtype=np.long)
surfaces = [None] * n_surf
for i,ind in enumerate(surf_indexes):
mesh_pts = self._construct_surface_coord(
self._slicer(deformation,dim,ind)
)
surfaces[i] = vedo.Mesh(mesh_pts)
surf_meshes = vedo.merge(surfaces).lineWidth(1).alpha(self.alpha).computeNormals().c(self.color)
# plots
plot = vedo.Plotter()
plt = plot.show(surf_meshes,
# Points(surf_meshes.points(), c='red6', r=5),
# deformed_grid.c('yellow'),
# bg='k', bg2='lg',
axes=1,
).addCutterTool(surf_meshes,'box') # comment this line for using the class in jupyter notebooks
return plt
def deformation_grid3D_surfaces(deformation,dim =0,n_surf=5):
""" function API for `deformation_grid3D_vedo` class
:param deformation: [1,D,H,W,3] numpy array
:param dim: Dimension you want to make the surface along.
:param n_surf: Numbers of surfaces you want to plot.
:return:
"""
return deformation_grid3D_vedo().forward(deformation,dim=dim,n_surf=n_surf)
# ______________ TEST _________________________
# Generating a given 3D deformation
Tau,D,H,W = (1,50,150,200) # we will use a temporal vector field at a given time
x,y,z = np.meshgrid(np.linspace(-1,1,H),
np.linspace(-1,1,D),
np.linspace(-1,1,W))
grid = np.stack([x,y,z],axis=-1)[None]
v = np.zeros((Tau,D,H,W,3)) # vector field initialization
coef_mat = np.exp(-5*((grid[0,...,0])**2 +
(grid[0,...,1])**2 +
(grid[0,...,2])**2))[...,None]
v[0] = coef_mat*np.stack(
(.3*np.ones(grid[0,...,0].shape),
np.sin(grid[0,...,2]),
np.cos(grid[0,...,1])),
axis=3)
deformation = (grid + v[0][None]) # deformation shape is [1,D,H,W,3]
deformation_grid3D_surfaces(deformation,dim=1,n_surf=8) |
Beta Was this translation helpful? Give feedback.
Hi Anton,
Thanks for your nice words! You can create a Mesh by defining vertices and faces with
Mesh([pts,faces])
. There are classes that simplify this for simple objects ike "Grid". E.g.:For deformation…