Last active
February 15, 2022 09:23
-
-
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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