This post is to explain the BoneReconstructionPlanner main transform, that is the mandibleToFibula transform.
The mandibleToFibula transform is used to create the fibulaPlanes from the mandiblePlanes like this:
fibulaPlaneToWorld = mandibleToFibula*mandiblePlaneToWorld
Also the bone pieces of the fibula are transformed to the mandible using the inverse of that transform, the fibulaToMandible transform:
reconstructedFibulaPieceMeshPoints = fibulaToMandible*fibulaPieceMeshPoints
So the mandibleToFibula transform is the most important transform on BoneReconstructionPlanner as Virtual Surgical Planning would be impossible without it.
The mandibleToFibula transform is a kind of registration transform and it is not a change of frame transform.
Because the mandibleToFibula transform is not a change of frame transform it cannot be calculated as mandibleToFibula = worldToFibulaFrame*mandibleFrameToWorld
Please look at this example of a registration transform to know more and ask if you have questions:
#simple registration of two cts of the same patient on Slicer Stable Release
#the second ct will be simulated as a transformed version from the first ct
#first load two equals cts
import SampleData
sampleDataLogic = SampleData.SampleDataLogic()
image1 = sampleDataLogic.downloadMRHead()
image1.SetName('image1')
image2 = sampleDataLogic.downloadMRHead()
image2.SetName('image2')
#set up different colors to each scalarVolume
image1DisplayNode = image1.GetDisplayNode()
image1DisplayNode.SetAndObserveColorNodeID("vtkMRMLColorTableNodeGreen")
image2DisplayNode = image2.GetDisplayNode()
image2DisplayNode.SetAndObserveColorNodeID("vtkMRMLColorTableNodeBlue")
image1_points_world = slicer.mrmlScene.CreateNodeByClass("vtkMRMLMarkupsFiducialNode")
image1_points_world.SetName("image1_points_world")
slicer.mrmlScene.AddNode(image1_points_world)
slicer.modules.markups.logic().AddNewDisplayNodeForMarkupsNode(image1_points_world)
image2_points_world = slicer.mrmlScene.CreateNodeByClass("vtkMRMLMarkupsFiducialNode")
image2_points_world.SetName("image2_points_world")
slicer.mrmlScene.AddNode(image2_points_world)
slicer.modules.markups.logic().AddNewDisplayNodeForMarkupsNode(image2_points_world)
pointNose = [-2.324420213699341, -108.38597106933594, -14.390022277832031]
pointTipOfTheEar = [83.80633544921875, 33.72900390625, -15.804760932922363]
pointTipOfTheHead = [-2.6361961364746094, 47.51253890991211, 90.47916412353516]
#You can change this points for any you would like but there has to be 3 points
pointList = [pointNose,pointTipOfTheEar,pointTipOfTheHead]
for i in range(len(pointList)):
image1_points_world.AddControlPoint(vtk.vtkVector3d(pointList[i]))
image2_points_world.AddControlPoint(vtk.vtkVector3d(pointList[i]))
#transform image2 and image2_points_world with a randomTransform
randomTransform = vtk.vtkTransform()
randomTransform.PostMultiply()
#angleDegrees, rotationAxis and position can be changed to any you would like
angleDegrees = 49
rotationAxis = [0.27000401, 0.83251236, -0.48375718]
position = [40,50,60]
randomTransform.RotateWXYZ(angleDegrees, rotationAxis)
randomTransform.Translate(position)
randomTransformNode = slicer.vtkMRMLLinearTransformNode()
randomTransformNode.SetName("randomTransform")
slicer.mrmlScene.AddNode(randomTransformNode)
randomTransformNode.SetMatrixTransformToParent(randomTransform.GetMatrix())
#apply and harden the transform
image2.SetAndObserveTransformNodeID(randomTransformNode.GetID())
image2.HardenTransform()
image2_points_world.SetAndObserveTransformNodeID(randomTransformNode.GetID())
image2_points_world.HardenTransform()
#Now we have two cts that need to be registered and we have fiducials marking
# the same anatomical points on them, so we can do a simple registration
#For the registration we need one frame created from each of the markupPointsLists
#To create the frames we will use planes
image1_plane = slicer.mrmlScene.CreateNodeByClass("vtkMRMLMarkupsPlaneNode")
image1_plane.SetName("image1_plane")
slicer.mrmlScene.AddNode(image1_plane)
slicer.modules.markups.logic().AddNewDisplayNodeForMarkupsNode(image1_plane)
image2_plane = slicer.mrmlScene.CreateNodeByClass("vtkMRMLMarkupsPlaneNode")
image2_plane.SetName("image2_plane")
slicer.mrmlScene.AddNode(image2_plane)
slicer.modules.markups.logic().AddNewDisplayNodeForMarkupsNode(image2_plane)
for i in range(image1_points_world.GetNumberOfControlPoints()):
point = [0,0,0]
image1_points_world.GetNthControlPointPosition(i,point)
image1_plane.AddControlPoint(vtk.vtkVector3d(point))
for i in range(image2_points_world.GetNumberOfControlPoints()):
point = [0,0,0]
image2_points_world.GetNthControlPointPosition(i,point)
image2_plane.AddControlPoint(vtk.vtkVector3d(point))
image1FrameToWorldMatrix = vtk.vtkMatrix4x4()
image1_plane.GetPlaneToWorldMatrix(image1FrameToWorldMatrix)
image2FrameToWorldMatrix = vtk.vtkMatrix4x4()
image2_plane.GetPlaneToWorldMatrix(image2FrameToWorldMatrix)
# Now we calculate the transform needed for registration
worldToImage1FrameMatrix = vtk.vtkMatrix4x4()
worldToImage1FrameMatrix.DeepCopy(image1FrameToWorldMatrix)
worldToImage1FrameMatrix.Invert()
registrationTransform = vtk.vtkTransform()
registrationTransform.PostMultiply()
registrationTransform.Concatenate(worldToImage1FrameMatrix)
registrationTransform.Concatenate(image2FrameToWorldMatrix)
registrationTransformNode = slicer.vtkMRMLLinearTransformNode()
registrationTransformNode.SetName("registrationTransform")
slicer.mrmlScene.AddNode(registrationTransformNode)
registrationTransformNode.SetMatrixTransformToParent(registrationTransform.GetMatrix())
#apply the transform to image1 and image1_points_world to see if they overlap with the
# image2 counterparts.
image1.SetAndObserveTransformNodeID(registrationTransformNode.GetID())
image1_points_world.SetAndObserveTransformNodeID(registrationTransformNode.GetID())
#user: view both volumes, one as foreground, the other as background and check that they
# overlap.
#We can confirm that the registrationTransform is indeed correct because it is equal to the
# randomTransform we applied to image1 at the start
#So we know what the registration transform does. So we can call it image1ToImage2Transform
registrationTransformNode.SetName("image1ToImage2Transform")
#Some interesting detail: the registration transform was not calculated as
# image1FrameToImage2Frame = worldToImage2*image1FrameToWorld
# the main reason for this is that the markup points are in world coordinates, not in
# image1Frame or image2Frame coordinates