Skip to content

Instantly share code, notes, and snippets.

@mauigna06
Created November 5, 2021 19:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mauigna06/434784dac9e5704198b80b179e037d0e to your computer and use it in GitHub Desktop.
Save mauigna06/434784dac9e5704198b80b179e037d0e to your computer and use it in GitHub Desktop.
Create a frame for the model in the principal components directions, set up a plane with interaction handles to move it.
# Create a frame for the model in the principal components directions,
# set up a plane with interaction handles to move it.
import numpy as np
#Change this number for more accuracy (more points, take more time to compute)
numberOfSampledPointsOfModel = 2000
model = getNode('mymodel')
if model.GetPolyData().GetNumberOfPoints() > numberOfSampledPointsOfModel:
maskPointsFilter = vtk.vtkMaskPoints()
maskPointsFilter.SetInputData(model.GetPolyData())
#
ratio = round(model.GetPolyData().GetNumberOfPoints()/numberOfSampledPointsOfModel)
#
# This works but the sampling could be biased spatially I think
# If you have vtk9 you should use UNIFORM_SPATIAL_SURFACE option
maskPointsFilter.SetOnRatio(ratio)
maskPointsFilter.RandomModeOn()
maskPointsFilter.Update()
#
polydata = vtk.vtkPolyData()
polydata.ShallowCopy(maskPointsFilter.GetOutput())
#
# Calculate the SVD and mean
from vtk.util.numpy_support import vtk_to_numpy
modelPoints = vtk_to_numpy(polydata.GetPoints().GetData())
else:
from vtk.util.numpy_support import vtk_to_numpy
modelPoints = vtk_to_numpy(model.GetPolyData().GetPoints().GetData())
# Calculate the mean of the points, i.e. the 'center' of the cloud
modelPointsMean = modelPoints.mean(axis=0)
# Do an SVD on the mean-centered data.
uu, dd, rightSingularVectors = np.linalg.svd(modelPoints - modelPointsMean)
# Create a frame for the model
modelZ = np.zeros(3)
modelX = rightSingularVectors[0]
modelY = rightSingularVectors[1]
vtk.vtkMath.Cross(modelX, modelY, modelZ)
modelZ = modelZ/np.linalg.norm(modelZ)
modelOrigin = modelPointsMean
# Create the plane to move the model
planeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsPlaneNode", slicer.mrmlScene.GetUniqueNameByString("modelToWorldPlane"))
slicer.modules.markups.logic().AddNewDisplayNodeForMarkupsNode(planeNode)
displayNode = planeNode.GetDisplayNode()
displayNode.SetGlyphScale(2.5)
displayNode.SetOpacity(0)
displayNode.HandlesInteractiveOn()
planeNode.SetAxes(modelX,modelY,modelZ)
planeNode.SetOrigin(modelOrigin)
planeToWorldTransformNode = slicer.vtkMRMLLinearTransformNode()
planeToWorldTransformNode.SetName(slicer.mrmlScene.GetUniqueNameByString("planeToWorld"))
slicer.mrmlScene.AddNode(planeToWorldTransformNode)
# Save the inverse of the initial transform of the plane
worldToInitialModelTransformNode = slicer.vtkMRMLLinearTransformNode()
worldToInitialModelTransformNode.SetName(slicer.mrmlScene.GetUniqueNameByString("worldToInitialModel"))
slicer.mrmlScene.AddNode(worldToInitialModelTransformNode)
model.SetAndObserveTransformNodeID(worldToInitialModelTransformNode.GetID())
worldToInitialModelTransformNode.SetAndObserveTransformNodeID(planeToWorldTransformNode.GetID())
# function to update the model's position/orientation interactively
def onPlaneModified(sourceNode,event=None):
planeToWorldMatrix = vtk.vtkMatrix4x4()
try:
sourceNode.GetObjectToWorldMatrix(planeToWorldMatrix)
except:
sourceNode.GetPlaneToWorldMatrix(planeToWorldMatrix)
#
planeNodeToWorldTransformNode = model.GetParentTransformNode().GetParentTransformNode()
planeNodeToWorldTransformNode.SetMatrixTransformToParent(planeToWorldMatrix)
onPlaneModified(planeNode)
planeToWorldMatrix = vtk.vtkMatrix4x4()
# This call is done to give the transformNode time to update its matrixToParent
planeNodeToWorldTransformNode = model.GetParentTransformNode().GetParentTransformNode()
planeNodeToWorldTransformNode.GetMatrixTransformToParent(planeToWorldMatrix)
worldToInitialModelMatrix = vtk.vtkMatrix4x4()
worldToInitialModelMatrix.DeepCopy(planeToWorldMatrix)
worldToInitialModelMatrix.Invert()
worldToInitialModelTransformNode.SetMatrixTransformToParent(worldToInitialModelMatrix)
observer = planeNode.AddObserver(slicer.vtkMRMLMarkupsNode.PointModifiedEvent,onPlaneModified)
#If you want to stop interactions for plane i, do this:
#planeNode.RemoveObserver(observer)
#If you want the model to world transform AFTER YOU MOVE IT, do this:
worldToInitialModelTransformNode = model.GetParentTransformNode()
modelToWorldMatrix = vtk.vtkMatrix4x4()
worldToInitialModelTransformNode.GetMatrixTransformToWorld(modelToWorldMatrix)
print(modelToWorldMatrix)
#If you just want to define a frame for the model, do this:
worldToInitialModelTransformNode = model.GetParentTransformNode()
initialModelToWorldMatrix = vtk.vtkMatrix4x4()
worldToInitialModelTransformNode.GetMatrixTransformToParent(initialModelToWorldMatrix)
initialModelToWorldMatrix.Invert()
print(initialModelToWorldMatrix)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment