Skip to content

Instantly share code, notes, and snippets.

@mauigna06
Last active February 15, 2022 09:23
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/4345e60f04c74916f159474fb26086fa to your computer and use it in GitHub Desktop.
Save mauigna06/4345e60f04c74916f159474fb26086fa to your computer and use it in GitHub Desktop.
This scripts allows this feature that let's move the plane indenpendently of the model on Local mode and then lets you transform it in Transform mode is what makes this feature useful for manual registration.
# This scripts allows you to have a widget placed initially in the model centroid and the
# direction matching its principal components. Then two modes are possible "Transform mode"
# and "Local mode" by pressing g key and h key correspondingly.
# You can change the center of rotation by going to Local mode, moving the widget and then
# returning to Transform mode. This feature that let's move the plane indenpendently of the
# model on Local mode and then lets you transform it in Transform mode is what makes this
# feature useful for manual registration.
# Code Starts here
# 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('fibula')
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)
try:
displayNode.HandlesInteractiveOn()
displayNode.RotationHandleVisibilityOn()
displayNode.TranslationHandleVisibilityOn()
displayNode.ScaleHandleVisibilityOff()
except:
pass
localToWorldTransformNode = slicer.vtkMRMLLinearTransformNode()
localToWorldTransformNode.SetName(slicer.mrmlScene.GetUniqueNameByString("localToWorld"))
slicer.mrmlScene.AddNode(localToWorldTransformNode)
# Save the inverse of the initial transform of the plane
transformNode_World = slicer.vtkMRMLLinearTransformNode()
transformNode_World.SetName(slicer.mrmlScene.GetUniqueNameByString("transform_world"))
slicer.mrmlScene.AddNode(transformNode_World)
model.SetAndObserveTransformNodeID(transformNode_World.GetID())
localToWorldTransformNode.SetAndObserveTransformNodeID(transformNode_World.GetID())
# function to update the model's position/orientation interactively
def onWidgetModifiedEditLocal(sourceNode=None,event=None):
widgetToWorldMatrix = vtk.vtkMatrix4x4()
try:
sourceNode.GetObjectToWorldMatrix(widgetToWorldMatrix)
except:
sourceNode.GetPlaneToWorldMatrix(widgetToWorldMatrix)
#
inverseTransform_WorldMatrix = vtk.vtkMatrix4x4()
transformNode_World.GetMatrixTransformToParent(inverseTransform_WorldMatrix)
inverseTransform_WorldMatrix.Invert()
newLocalToWorldTransform = vtk.vtkTransform()
newLocalToWorldTransform.PostMultiply()
newLocalToWorldTransform.Concatenate(widgetToWorldMatrix)
newLocalToWorldTransform.Concatenate(inverseTransform_WorldMatrix)
localToWorldTransformNode = getNode('localToWorld')
localToWorldTransformNode.SetMatrixTransformToParent(newLocalToWorldTransform.GetMatrix())
def onWidgetModifiedEditTransform(sourceNode,event=None):
widgetToWorldMatrix = vtk.vtkMatrix4x4()
try:
sourceNode.GetObjectToWorldMatrix(widgetToWorldMatrix)
except:
sourceNode.GetPlaneToWorldMatrix(widgetToWorldMatrix)
#
localToWorldTransformNode = getNode('localToWorld')
worldToLocalMatrix = vtk.vtkMatrix4x4()
localToWorldTransformNode.GetMatrixTransformToParent(worldToLocalMatrix)
worldToLocalMatrix.Invert()
newTransform_World = vtk.vtkTransform()
newTransform_World.PostMultiply()
newTransform_World.Concatenate(worldToLocalMatrix)
newTransform_World.Concatenate(widgetToWorldMatrix)
transformNode_World = model.GetParentTransformNode()
transformNode_World.SetMatrixTransformToParent(newTransform_World.GetMatrix())
def onWidgetModifiedEditLocal(sourceNode=None,event=None):
widgetToWorldMatrix = vtk.vtkMatrix4x4()
try:
sourceNode.GetObjectToWorldMatrix(widgetToWorldMatrix)
except:
sourceNode.GetPlaneToWorldMatrix(widgetToWorldMatrix)
#
inverseTransform_WorldMatrix = vtk.vtkMatrix4x4()
transformNode_World.GetMatrixTransformToParent(inverseTransform_WorldMatrix)
inverseTransform_WorldMatrix.Invert()
newLocalToWorldTransform = vtk.vtkTransform()
newLocalToWorldTransform.PostMultiply()
newLocalToWorldTransform.Concatenate(widgetToWorldMatrix)
newLocalToWorldTransform.Concatenate(inverseTransform_WorldMatrix)
localToWorldTransformNode = getNode('localToWorld')
localToWorldTransformNode.SetMatrixTransformToParent(newLocalToWorldTransform.GetMatrix())
def onCameraModifiedEditWidget(sourceNode,event=None):
#print('cameraModifiedEvent')
widgetToWorldMatrix = vtk.vtkMatrix4x4()
try:
planeNode.GetObjectToWorldMatrix(widgetToWorldMatrix)
except:
planeNode.GetPlaneToWorldMatrix(widgetToWorldMatrix)
#
cameraPosition = np.zeros(3)
sourceNode.GetPosition(cameraPosition)
#
cameraFocalPoint = np.zeros(3)
sourceNode.GetFocalPoint(cameraFocalPoint)
#
cameraViewUp = np.zeros(3)
sourceNode.GetViewUp(cameraViewUp)
#
cameraDOPDistance = np.linalg.norm(cameraFocalPoint - cameraPosition)
#
cameraDOP = (cameraFocalPoint - cameraPosition) / cameraDOPDistance
cameraViewRight = np.zeros(3)
vtk.vtkMath.Cross(cameraDOP, cameraViewUp, cameraViewRight)
#
planeNode.DisableModifiedEventOn()
planeNode.SetAxes(cameraViewRight,cameraDOP,cameraViewUp)
planeNode.DisableModifiedEventOff()
planeNode.GetDisplayNode().Modified()
#
widgetToWorldMatrix = vtk.vtkMatrix4x4()
try:
planeNode.GetObjectToWorldMatrix(widgetToWorldMatrix)
except:
planeNode.GetPlaneToWorldMatrix(widgetToWorldMatrix)
#
inverseTransform_WorldMatrix = vtk.vtkMatrix4x4()
transformNode_World.GetMatrixTransformToParent(inverseTransform_WorldMatrix)
inverseTransform_WorldMatrix.Invert()
newLocalToWorldTransform = vtk.vtkTransform()
newLocalToWorldTransform.PostMultiply()
newLocalToWorldTransform.Concatenate(widgetToWorldMatrix)
newLocalToWorldTransform.Concatenate(inverseTransform_WorldMatrix)
localToWorldTransformNode = getNode('localToWorld')
localToWorldTransformNode.SetMatrixTransformToParent(newLocalToWorldTransform.GetMatrix())
observerEditTransform = 0
observerEditLocal = 0
observerCamera = 0
layoutManager = slicer.app.layoutManager()
threeDWidget = layoutManager.threeDWidget(0)
cameraNode = slicer.modules.cameras.logic().GetViewActiveCameraNode(
threeDWidget.mrmlViewNode()
)
def setUpTransformMode():
global observerEditTransform, observerEditLocal
if observerEditTransform != 0:
return
if observerEditLocal != 0:
planeNode.RemoveObserver(observerEditLocal)
observerEditLocal = 0
observerEditTransform = planeNode.AddObserver(slicer.vtkMRMLMarkupsNode.PointModifiedEvent,onWidgetModifiedEditTransform)
print('Transform Mode')
def setUpLocalMode():
global observerEditTransform, observerEditLocal
if observerEditLocal != 0:
return
if observerEditTransform != 0:
planeNode.RemoveObserver(observerEditTransform)
observerEditTransform = 0
observerEditLocal = planeNode.AddObserver(slicer.vtkMRMLMarkupsNode.PointModifiedEvent,onWidgetModifiedEditLocal)
print('Local Mode')
def toogleCameraPlaneMode():
global observerCamera
if observerCamera != 0:
cameraNode.RemoveObserver(observerCamera)
observerCamera = 0
print('Camera Plane Mode disabled')
else:
setUpTransformMode()
observerCamera = cameraNode.AddObserver(
vtk.vtkCommand.ModifiedEvent,onCameraModifiedEditWidget
)
cameraNode.Modified()
print('Camera Plane Mode enabled')
def stopInteractions():
global observerEditTransform, observerEditLocal
if observerEditTransform != 0:
planeNode.RemoveObserver(observerEditTransform)
observerEditTransform = 0
if observerEditLocal != 0:
planeNode.RemoveObserver(observerEditLocal)
observerEditLocal = 0
if observerEditLocal != 0:
cameraNode.RemoveObserver(observerCamera)
observerCamera = 0
print('Interactions stopped')
editTransformModeActivate = qt.QShortcut(qt.QKeySequence("g"), mainWindow())
editTransformModeActivate.connect("activated()", setUpTransformMode)
editLocalModeActivate = qt.QShortcut(qt.QKeySequence("h"), mainWindow())
editLocalModeActivate.connect("activated()", setUpLocalMode)
cameraPlaneModeActivate = qt.QShortcut(qt.QKeySequence("c"), mainWindow())
cameraPlaneModeActivate.connect("activated()", toogleCameraPlaneMode)
onWidgetModifiedEditLocal(planeNode)
setUpTransformMode()
#If you want to stop interactions for plane, do this:
#stopInteractions()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment