Transforms can also be used to align two meshes¶
import shapeworks as sw
import numpy as np
import pyvista as pv
import matplotlib
pv.set_jupyter_backend('static')
DATA = "../Data"
Mesh alignment¶
A mesh transformation can be computed rigidly, using similarity, or as an affine matrix. The alignment is computed using the iterative closest point (ICP) method for the specified number of iterations (default: a similarity transform with 10 iterations).
filename1 = DATA + "/ellipsoid_1mode/meshes/ellipsoid_05.vtk"
filename2 = DATA + "/ellipsoid_1mode/meshes/ellipsoid_07.vtk"
Load meshes¶
mesh1 = sw.Mesh(filename1)
mesh2 = sw.Mesh(filename2)
Visualize original meshes¶
pv_mesh1_orig = sw.sw2vtkMesh(mesh1)
pv_mesh2_orig = sw.sw2vtkMesh(mesh2)
# used to maintain bounds even when meshes' positions change
a = pv.UniformGrid()
a.dimensions = np.array([9,8,8])
a.origin = (-15,-5,-15)
a.spacing = (10, 10, 10)
outline = a.outline()
p = pv.Plotter(shape=(2,2), border=False)
p.subplot(0,0)
p.add_text("xy")
p.add_mesh(outline)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', opacity=1.0)
p.camera_position = 'xy'
p.subplot(0,1)
p.add_text("yz")
p.add_mesh(outline)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', opacity=1.0)
p.camera_position = 'yz'
p.subplot(1,0)
p.add_text("zx")
p.add_mesh(outline)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', opacity=1.0)
p.camera_position = 'zx'
p.add_text("Original Meshes", position='lower_edge')
p.subplot(1,1)
p.add_text("persp")
p.add_mesh(outline)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', opacity=1.0)
p.camera_position = [100,35,70]
p.set_viewup([0,1,0])
p.show_bounds(grid='front', location='outer', all_edges=False, )
p.show()
Rigid transformation¶
create transforms¶
xform_1_to_2 = mesh1.createTransform(mesh2, sw.Mesh.AlignmentType.Rigid)
xform_2_to_1 = mesh2.createTransform(mesh1, sw.Mesh.AlignmentType.Rigid)
apply transforms¶
mesh1.applyTransform(xform_1_to_2)
mesh2.applyTransform(xform_2_to_1)
visualize results¶
Notice how the original meshes do not change shape. This is very different from affine transformations below.
pv_mesh1 = sw.sw2vtkMesh(mesh1)
pv_mesh2 = sw.sw2vtkMesh(mesh2)
Mesh 1 -> Mesh 2¶
# used to maintain bounds even when meshes' positions change
a = pv.UniformGrid()
a.dimensions = np.array([9,8,8])
a.origin = (-15,-5,-15)
a.spacing = (10, 10, 10)
outline = a.outline()
p = pv.Plotter(shape=(2,2), border=False)
p.subplot(0,0)
p.add_text("xy")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'xy'
p.subplot(0,1)
p.add_text("yz")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'yz'
p.subplot(1,0)
p.add_text("zx")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'zx'
p.add_text("Rigid Transforms", position='lower_edge')
p.subplot(1,1)
p.add_text("persp")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.75)
p.camera_position = [100,35,70]
p.set_viewup([0,1,0])
p.show_bounds(grid='front', location='outer', all_edges=False, )
p.show()
Mesh 2 -> Mesh 1¶
# used to maintain bounds even when meshes' positions change
a = pv.UniformGrid()
a.dimensions = np.array([9,8,8])
a.origin = (-15,-5,-15)
a.spacing = (10, 10, 10)
outline = a.outline()
p = pv.Plotter(shape=(2,2), border=False)
p.subplot(0,0)
p.add_text("xy")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'xy'
p.subplot(0,1)
p.add_text("yz")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'yz'
p.subplot(1,0)
p.add_text("zx")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'zx'
p.add_text("Rigid Transforms", position='lower_edge')
p.subplot(1,1)
p.add_text("persp")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.75)
p.camera_position = [100,35,70]
p.set_viewup([0,1,0])
p.show_bounds(grid='front', location='outer', all_edges=False, )
p.show()
Similarity transformation¶
This type of transformation is very similar to rigid transformations above.
Reload meshes¶
mesh1 = sw.Mesh(filename1)
mesh2 = sw.Mesh(filename2)
create transforms¶
xform_1_to_2 = mesh1.createTransform(mesh2, sw.Mesh.AlignmentType.Similarity)
xform_2_to_1 = mesh2.createTransform(mesh1, sw.Mesh.AlignmentType.Similarity)
apply transforms¶
mesh1.applyTransform(xform_1_to_2)
mesh2.applyTransform(xform_2_to_1)
visualize results¶
Notice how the original meshes do not change shape (or orientation wrt the origin? How are these different from affine?)
pv_mesh1 = sw.sw2vtkMesh(mesh1)
pv_mesh2 = sw.sw2vtkMesh(mesh2)
Mesh 1 -> Mesh 2¶
# used to maintain bounds even when meshes' positions change
a = pv.UniformGrid()
a.dimensions = np.array([9,8,8])
a.origin = (-15,-5,-15)
a.spacing = (10, 10, 10)
outline = a.outline()
p = pv.Plotter(shape=(2,2), border=False)
p.subplot(0,0)
p.add_text("xy")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'xy'
p.subplot(0,1)
p.add_text("yz")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'yz'
p.subplot(1,0)
p.add_text("zx")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'zx'
p.add_text("Similarity Transforms", position='lower_edge')
p.subplot(1,1)
p.add_text("persp")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.75)
p.camera_position = [100,35,70]
p.set_viewup([0,1,0])
p.show_bounds(grid='front', location='outer', all_edges=False, )
p.show()
Mesh 2 -> Mesh 1¶
# used to maintain bounds even when meshes' positions change
a = pv.UniformGrid()
a.dimensions = np.array([9,8,8])
a.origin = (-15,-5,-15)
a.spacing = (10, 10, 10)
outline = a.outline()
p = pv.Plotter(shape=(2,2), border=False)
p.subplot(0,0)
p.add_text("xy")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'xy'
p.subplot(0,1)
p.add_text("yz")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'yz'
p.subplot(1,0)
p.add_text("zx")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'zx'
p.add_text("Similarity Transforms", position='lower_edge')
p.subplot(1,1)
p.add_text("persp")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.75)
p.camera_position = [100,35,70]
p.set_viewup([0,1,0])
p.show_bounds(grid='front', location='outer', all_edges=False, )
p.show()
Affine transformation¶
This type of transformation warps the points of one mesh into the other as well as translating its position.
Reload meshes¶
mesh1 = sw.Mesh(filename1)
mesh2 = sw.Mesh(filename2)
create transforms¶
xform_1_to_2 = mesh1.createTransform(mesh2, sw.Mesh.AlignmentType.Affine)
xform_2_to_1 = mesh2.createTransform(mesh1, sw.Mesh.AlignmentType.Affine)
apply transforms¶
mesh1.applyTransform(xform_1_to_2)
mesh2.applyTransform(xform_2_to_1)
visualize results¶
Notice how the original meshes do not change shape (or orientation wrt the origin? How are these different from affine?)
pv_mesh1 = sw.sw2vtkMesh(mesh1)
pv_mesh2 = sw.sw2vtkMesh(mesh2)
Mesh 1 -> Mesh 2¶
# used to maintain bounds even when meshes' positions change
a = pv.UniformGrid()
a.dimensions = np.array([9,8,8])
a.origin = (-15,-5,-15)
a.spacing = (10, 10, 10)
outline = a.outline()
p = pv.Plotter(shape=(2,2), border=False)
p.subplot(0,0)
p.add_text("xy")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'xy'
p.subplot(0,1)
p.add_text("yz")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'yz'
p.subplot(1,0)
p.add_text("zx")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'zx'
p.add_text("Affine Transforms", position='lower_edge')
p.subplot(1,1)
p.add_text("persp")
p.add_mesh(outline)
p.add_mesh(pv_mesh1, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh2_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.75)
p.camera_position = [100,35,70]
p.set_viewup([0,1,0])
p.show_bounds(grid='front', location='outer', all_edges=False, )
p.show()
Mesh 2 -> Mesh 1¶
# used to maintain bounds even when meshes' positions change
a = pv.UniformGrid()
a.dimensions = np.array([9,8,8])
a.origin = (-15,-5,-15)
a.spacing = (10, 10, 10)
outline = a.outline()
p = pv.Plotter(shape=(2,2), border=False)
p.subplot(0,0)
p.add_text("xy")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'xy'
p.subplot(0,1)
p.add_text("yz")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'yz'
p.subplot(1,0)
p.add_text("zx")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.5)
p.camera_position = 'zx'
p.add_text("Affine Transforms", position='lower_edge')
p.subplot(1,1)
p.add_text("persp")
p.add_mesh(outline)
p.add_mesh(pv_mesh2, show_scalar_bar=False, color='#4fb080', opacity=1.0)
p.add_mesh(pv_mesh1_orig, show_scalar_bar=False, color='#ced175', style='wireframe', opacity=0.75)
p.camera_position = [100,35,70]
p.set_viewup([0,1,0])
p.show_bounds(grid='front', location='outer', all_edges=False, )
p.show()