def onDisplayMeshButton(self): logic = PointCloudRegistrationLogic() # Display target points self.targetModelNode = slicer.util.loadModel( self.targetModelSelector.currentPath) blue = [0, 0, 1] # Display aligned source points self.sourceModelNode = slicer.util.loadModel( self.sourceModelSelector.currentPath) points = slicer.util.arrayFromModelPoints(self.sourceModelNode) points[:] = np.asarray(self.sourceData.points) self.sourceModelNode.GetPolyData().GetPoints().GetData().Modified() self.sourceModelNode.SetAndObserveTransformNodeID( self.ICPTransformNode.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(self.sourceModelNode) logic.RAS2LPSTransform(self.sourceModelNode) red = [1, 0, 0] self.sourceModelNode.GetDisplayNode().SetColor(red) self.sourceCloudNode.GetDisplayNode().SetVisibility(False) self.targetCloudNode.GetDisplayNode().SetVisibility(False) sourceLM_vtk = logic.loadAndScaleFiducials( self.sourceFiducialSelector.currentPath, self.scaling) transform_vtk = self.ICPTransformNode.GetMatrixTransformToParent() self.alignedSourceLM_vtk = logic.applyTransform( transform_vtk, sourceLM_vtk) self.alignedSourceLM_np = vtk_np.vtk_to_numpy( self.alignedSourceLM_vtk.GetPoints().GetData()) inputPoints = logic.exportPointCloud(self.alignedSourceLM_np, "Landmarks") green = [0, 1, 0] inputPoints.GetDisplayNode().SetColor(green) logic.RAS2LPSTransform(inputPoints) inputPoints.GetDisplayNode().SetPointLabelsVisibility(True)
def onAlignButton(self): logic = PointCloudRegistrationLogic() self.transformMatrix = logic.estimateTransform( self.sourcePoints, self.targetPoints, self.sourceFeatures, self.targetFeatures, self.voxelSize, self.parameterDictionary) self.ICPTransformNode = logic.convertMatrixToTransformNode( self.transformMatrix, 'Rigid Transformation Matrix') # For later analysis, apply transform to VTK arrays directly transform_vtk = self.ICPTransformNode.GetMatrixTransformToParent() self.alignedSourceSLM_vtk = logic.applyTransform( transform_vtk, self.sourceSLM_vtk) # Display aligned source points using transform that can be viewed/edited in the scene red = [1, 0, 0] self.sourceCloudNode = logic.displayPointCloud(self.sourceSLM_vtk, self.voxelSize / 10, 'Source Pointcloud', red) self.sourceCloudNode.SetAndObserveTransformNodeID( self.ICPTransformNode.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(self.sourceCloudNode) logic.RAS2LPSTransform(self.sourceCloudNode) self.updateLayout() # Enable next step of analysis self.displayMeshButton.enabled = True
def flip(self, volumeNode, transformMatrix): transform = slicer.vtkMRMLTransformNode() transform.SetName('FlipTransformation') slicer.mrmlScene.AddNode(transform) transform.SetMatrixTransformFromParent(transformMatrix) volumeNode.SetAndObserveTransformNodeID(transform.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(volumeNode) slicer.mrmlScene.RemoveNode(transform)
def RAS2LPSTransform(self, modelNode): matrix = vtk.vtkMatrix4x4() matrix.Identity() matrix.SetElement(0, 0, -1) matrix.SetElement(1, 1, -1) transform = vtk.vtkTransform() transform.SetMatrix(matrix) transformNode = slicer.mrmlScene.AddNewNodeByClass( 'vtkMRMLTransformNode', 'RAS2LPS') transformNode.SetAndObserveTransformToParent(transform) modelNode.SetAndObserveTransformNodeID(transformNode.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(modelNode) slicer.mrmlScene.RemoveNode(transformNode)
def register_and_resample(input_node, reference_node, transform_node=None, interpolationMode="Linear"): # when loaded with slicer, the matrix in tfm file is multiplied with LPS_to_RAS transforms from both sides # furthermore the transformNode will be set to FromParent instead of ToParent, which has the same effect # as inverting it before application to the volume node if transform_node: # make a temporary copy of the input node on which the transform can be hardened copy_input_node = slicer.modules.volumes.logic().CloneVolume(slicer.mrmlScene, input_node, "translated") copy_input_node.SetAndObserveTransformNodeID(transform_node.GetID()) logic = slicer.vtkSlicerTransformLogic() logic.hardenTransform(copy_input_node) print("hardened transformation") else: copy_input_node = slicer.modules.volumes.logic().CloneVolume(slicer.mrmlScene, input_node, "copy") # resample volume registered_and_resampled_node = slicer.mrmlScene.AddNewNodeByClass(copy_input_node.GetClassName()) parameters = { "inputVolume": copy_input_node, "referenceVolume": reference_node, "outputVolume": registered_and_resampled_node, "interpolationMode": interpolationMode, "defaultValue": 0.0, } slicer.cli.run(slicer.modules.brainsresample, None, parameters, wait_for_completion=True) slicer.mrmlScene.RemoveNode(copy_input_node) # remove temporary copy of input node registered_and_resampled_node.SetName(input_node.GetName() + "_registered_and_resampled") return registered_and_resampled_node
def harden_transform(polydata, transform, inverse, outdir): polydata_base_path, polydata_name = os.path.split(polydata) output_name = os.path.join(outdir, polydata_name) if os.path.exists(output_name): return check_load, polydata_node = slicer.util.loadModel(str(polydata), 1) if not check_load: print('Could not load polydata file:', polydata) return check_load, transform_node = slicer.util.loadTransform(str(transform), 1) if not check_load: print('Could not load transform file:', transform) return if inverse == "1": transform_node.Inverse() logic = slicer.vtkSlicerTransformLogic() t_node_id = transform_node.GetID() # harden transform polydata_node.SetAndObserveTransformNodeID(t_node_id) logic.hardenTransform(polydata_node) slicer.util.saveNode(polydata_node, output_name)
def export(self, node, extension, writerFilter, allExtensionsFilter = "", transformedFlag = False): if transformedFlag: shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene) itemIDToClone = shNode.GetItemByDataNode(node) clonedItemID = slicer.modules.subjecthierarchy.logic().CloneSubjectHierarchyItem(shNode, itemIDToClone) clonedNode = shNode.GetItemDataNode(clonedItemID) transformNode = slicer.mrmlScene.GetNodeByID(node.GetTransformNodeID()) clonedNode.SetAndObserveTransformNodeID(transformNode.GetID()) transformLogic = slicer.vtkSlicerTransformLogic() transformLogic.hardenTransform(clonedNode) node = clonedNode fileFilter = allExtensionsFilter + ";;" + writerFilter + ";;All files *" fileName = qt.QFileDialog.getSaveFileName(slicer.util.mainWindow(), "Export As...", node.GetName()+extension, fileFilter, None, qt.QFileDialog.DontUseNativeDialog) if not fileName.endswith(extension): fileName = fileName + extension if fileName == "": return writerType = slicer.app.coreIOManager().fileWriterFileType(node) success = slicer.app.coreIOManager().saveNodes(writerType, {"nodeID": node.GetID(), "fileName": fileName}) if success: logging.info(f"Exported {node.GetName()} to {fileName}") else: slicer.util.errorDisplay(f"Could not save {node.GetName()} to {fileName}") if transformedFlag: slicer.mrmlScene.RemoveNode(clonedNode.GetStorageNode()) slicer.mrmlScene.RemoveNode(clonedNode.GetDisplayNode()) slicer.mrmlScene.RemoveNode(clonedNode)
def harden_transform(polydata, transform, inverse, outdir): check_load, polydata_node = slicer.util.loadModel(str(polydata), 1) if not check_load: print 'Could not load polydata file:', polydata return check_load, transform_node = slicer.util.loadTransform(str(transform), 1) if not check_load: print 'Could not load transform file:', transform return if inverse == "1": transform_node.Inverse() logic = slicer.vtkSlicerTransformLogic() t_node_id = transform_node.GetID() # harden transform polydata_node.SetAndObserveTransformNodeID(t_node_id) logic.hardenTransform(polydata_node) polydata_base_path, polydata_name = os.path.split(polydata) output_name = polydata_name slicer.util.saveNode(polydata_node, os.path.join(outdir, output_name))
def onAlignButtonTB(self): self.RWButton.enabled = False self.alignButtonTB.enabled = False self.landmarkTransform = slicer.vtkMRMLTransformNode() slicer.mrmlScene.AddNode(self.landmarkTransform) logic = AlignCrop3DSlicerModuleLogic() if(self.movingFiducialNode.GetNumberOfFiducials() > 2): logic.runAlignmentRegistration(self.landmarkTransform, self.templateFidTB, self.movingFiducialNode, self.placementListTB) else: slicer.util.infoDisplay("At least 3 fiducials required for registration to proceed") #Apply Landmark transform on input Volume & Fiducials and Harden self.inputVolumeTB.SetAndObserveTransformNodeID(self.landmarkTransform.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(self.inputVolumeTB) self.movingFiducialNode.SetAndObserveTransformNodeID(self.landmarkTransform.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(self.movingFiducialNode) #TODO - Align output is incorrect!! Investigate (Jan 17th - 2018) #Set template to foreground in Slice Views applicationLogic = slicer.app.applicationLogic() selectionNode = applicationLogic.GetSelectionNode() selectionNode.SetSecondaryVolumeID(self.templateVolumeTB.GetID()) applicationLogic.PropagateForegroundVolumeSelection(0) #set overlap of foreground & background in slice view sliceLayout = slicer.app.layoutManager() sliceLogicR = sliceLayout.sliceWidget('Red').sliceLogic() compositeNodeR = sliceLogicR.GetSliceCompositeNode() compositeNodeR.SetForegroundOpacity(0.5) sliceLogicY = sliceLayout.sliceWidget('Yellow').sliceLogic() compositeNodeY = sliceLogicY.GetSliceCompositeNode() compositeNodeY.SetForegroundOpacity(0.5) sliceLogicG = sliceLayout.sliceWidget('Green').sliceLogic() compositeNodeG = sliceLogicG.GetSliceCompositeNode() compositeNodeG.SetForegroundOpacity(0.5) #centre slice viewer on image slicer.app.applicationLogic().FitSliceToAll() #Make Atlas Fidcials visible self.templateFidTB.SetDisplayVisibility(1)
def applySkyscanTransform(self, volumeNode): #set up transform node transformNode = slicer.vtkMRMLTransformNode() slicer.mrmlScene.AddNode(transformNode) volumeNode.SetAndObserveTransformNodeID(transformNode.GetID()) #set up Skyscan transform transformMatrixNp = np.array([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]) transformMatrixVTK = vtk.vtkMatrix4x4() for row in range(4): for col in range(4): transformMatrixVTK.SetElement(row, col, transformMatrixNp[row,col]) transformNode.SetMatrixTransformToParent(transformMatrixVTK) #harden and clean up slicer.vtkSlicerTransformLogic().hardenTransform(volumeNode) slicer.mrmlScene.RemoveNode(transformNode)
def onAlignButton(self): self.RWButton.enabled = False #Create Landmark transform placeholder self.LandmarkTrans = slicer.vtkMRMLTransformNode() slicer.mrmlScene.AddNode(self.LandmarkTrans) logic = AValue3DSlicerModuleLogic() #Run fiducial registration if(self.placedLandmarkNode.GetNumberOfFiducials() == 4): logic.runFiducialRegistration(self.rightAtlas.isChecked(), self.LandmarkTrans, self.placedLandmarkNode) else: slicer.util.infoDisplay("4 Fiducials required for registration") #TODO - add appropriate information to help user! #Apply Landmark transform on Atlas Volume then Harden self.atlasVolume.SetAndObserveTransformNodeID(self.LandmarkTrans.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(self.atlasVolume) #Apply Landmark transform on Fiduicals then Harden self.atlasFid.SetAndObserveTransformNodeID(self.LandmarkTrans.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(self.atlasFid) #Set Atlas to foreground in Slice Views applicationLogic = slicer.app.applicationLogic() selectionNode = applicationLogic.GetSelectionNode() selectionNode.SetSecondaryVolumeID(self.atlasVolume.GetID()) applicationLogic.PropagateForegroundVolumeSelection(0) #set overlap of foreground & background in slice view sliceLayout = slicer.app.layoutManager() sliceLogicR = sliceLayout.sliceWidget('Red').sliceLogic() compositeNodeR = sliceLogicR.GetSliceCompositeNode() compositeNodeR.SetForegroundOpacity(0.4) sliceLogicY = sliceLayout.sliceWidget('Yellow').sliceLogic() compositeNodeY = sliceLogicY.GetSliceCompositeNode() compositeNodeY.SetForegroundOpacity(0.4) sliceLogicG = sliceLayout.sliceWidget('Green').sliceLogic() compositeNodeG = sliceLogicG.GetSliceCompositeNode() compositeNodeG.SetForegroundOpacity(0.4) self.defineCropButton.enabled = True # enabled next step "Defining Crop region of interest" self.placedLandmarkNode.SetDisplayVisibility(0) #turn off display of placed landmarks -TODO - Is this needed
def runCleaning(self, projectedLM, sphere, spacingPercentage): # Convert projected surface points to a VTK array for transform p = [0, 0, 0] targetPoints = vtk.vtkPoints() for i in range(projectedLM.GetNumberOfFiducials()): projectedLM.GetMarkupPoint(0, i, p) targetPoints.InsertNextPoint(p) # Set up a transform between the sphere and the points projected to the surface transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(sphere.GetPolyData().GetPoints()) transform.SetTargetLandmarks(targetPoints) transform.SetBasisToR() # Apply the transform to the sphere to create a model with spherical topology transformNode = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLTransformNode", "TPS") transformNode.SetAndObserveTransformToParent(transform) sphere.SetAndObserveTransformNodeID(transformNode.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(sphere) sphere.SetDisplayVisibility(False) # Clean up semi-landmarks within radius filter = vtk.vtkCleanPolyData() filter.SetToleranceIsAbsolute(False) filter.SetTolerance(spacingPercentage / 2) filter.SetInputData(sphere.GetPolyData()) filter.Update() cleanPolyData = filter.GetOutput() # Create a landmark node from the cleaned polyData sphereSampleLMNode = slicer.mrmlScene.AddNewNodeByClass( 'vtkMRMLMarkupsFiducialNode', "PseudoLandmarks") sphereSampleLMNode.CreateDefaultDisplayNodes() for i in range(cleanPolyData.GetNumberOfPoints()): point = cleanPolyData.GetPoint(i) sphereSampleLMNode.AddFiducialFromArray(point) landmarkTypeSemi = True self.setAllLandmarksType(sphereSampleLMNode, landmarkTypeSemi) return sphereSampleLMNode
def createIntermediateHardenModel(self, model): hardenModel = slicer.mrmlScene.GetNodesByName("SurfaceRegistration_" + model.GetName() + "_hardenCopy_" + str( slicer.app.applicationPid())).GetItemAsObject(0) if hardenModel is None: hardenModel = slicer.vtkMRMLModelNode() hardenPolyData = vtk.vtkPolyData() hardenPolyData.DeepCopy(model.GetPolyData()) hardenModel.SetAndObservePolyData(hardenPolyData) hardenModel.SetName( "SurfaceRegistration_" + model.GetName() + "_hardenCopy_" + str(slicer.app.applicationPid())) if model.GetParentTransformNode(): hardenModel.SetAndObserveTransformNodeID(model.GetParentTransformNode().GetID()) hardenModel.HideFromEditorsOn() slicer.mrmlScene.AddNode(hardenModel) logic = slicer.vtkSlicerTransformLogic() logic.hardenTransform(hardenModel) return hardenModel
def transformNode(self, node, matrix): transform = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLLinearTransformNode') transformMatrix = vtk.vtkMatrix4x4() transformMatrix.Zero() for row in range(4): for col in range(4): transformMatrix.SetElement(row, col, matrix[row][col]) transform.SetMatrixTransformToParent(transformMatrix) # Apply transform to node... node.SetAndObserveTransformNodeID(transform.GetID()) # ... and harden it transformLogic = slicer.vtkSlicerTransformLogic() transformLogic.hardenTransform(node) # delete transform node slicer.mrmlScene.RemoveNode(transform)
def createIntermediateHardenModel(self, model): hardenModel = slicer.mrmlScene.GetNodesByName( "SurfaceRegistration_" + model.GetName() + "_hardenCopy_" + str(slicer.app.applicationPid())).GetItemAsObject(0) if hardenModel is None: hardenModel = slicer.vtkMRMLModelNode() hardenPolyData = vtk.vtkPolyData() hardenPolyData.DeepCopy(model.GetPolyData()) hardenModel.SetAndObservePolyData(hardenPolyData) hardenModel.SetName("SurfaceRegistration_" + model.GetName() + "_hardenCopy_" + str(slicer.app.applicationPid())) if model.GetParentTransformNode(): hardenModel.SetAndObserveTransformNodeID( model.GetParentTransformNode().GetID()) hardenModel.HideFromEditorsOn() slicer.mrmlScene.AddNode(hardenModel) logic = slicer.vtkSlicerTransformLogic() logic.hardenTransform(hardenModel) return hardenModel
def isUnderTransform(self, markups): if markups.GetParentTransformNode(): messageBox = ctk.ctkMessageBox() messageBox.setWindowTitle(" /!\ WARNING /!\ ") messageBox.setIcon(messageBox.Warning) messageBox.setText("Your Markup Fiducial Node is currently modified by a transform," "if you choose to continue the program will apply the transform" "before doing anything else!") messageBox.setInformativeText("Do you want to continue?") messageBox.setStandardButtons(messageBox.No | messageBox.Yes) choice = messageBox.exec_() if choice == messageBox.Yes: logic = slicer.vtkSlicerTransformLogic() logic.hardenTransform(markups) return False else: messageBox.setText(" Node not modified") messageBox.setStandardButtons(messageBox.Ok) messageBox.setInformativeText("") messageBox.exec_() return True else: return False
def isUnderTransform(self, markups): if markups.GetParentTransformNode(): messageBox = ctk.ctkMessageBox() messageBox.setWindowTitle(" /!\ WARNING /!\ ") messageBox.setIcon(messageBox.Warning) messageBox.setText( "Your Markup Fiducial Node is currently modified by a transform," "if you choose to continue the program will apply the transform" "before doing anything else!") messageBox.setInformativeText("Do you want to continue?") messageBox.setStandardButtons(messageBox.No | messageBox.Yes) choice = messageBox.exec_() if choice == messageBox.Yes: logic = slicer.vtkSlicerTransformLogic() logic.hardenTransform(markups) return False else: messageBox.setText(" Node not modified") messageBox.setStandardButtons(messageBox.Ok) messageBox.setInformativeText("") messageBox.exec_() return True else: return False
def run(self, templateMesh, templateLM, templateSLM, meshDirectory, lmDirectory, slmDirectory, outDirectory, signedDistanceOption): if (bool(templateSLM) and bool(slmDirectory)): templateLMTotal = self.mergeLandmarks(templateLM, templateSLM) else: templateLMTotal = templateLM #get template points as vtk array templatePoints = vtk.vtkPoints() p = [0, 0, 0] for i in range(templateLMTotal.GetNumberOfMarkups()): templateLMTotal.GetMarkupPoint(0, i, p) templatePoints.InsertNextPoint(p) # write selected triangles to table tableNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLTableNode', 'Mean Mesh Distances') col1 = tableNode.AddColumn() col1.SetName('Subject ID') col2 = tableNode.AddColumn() col2.SetName('Mesh RMSE') tableNode.SetColumnType('Subject ID', vtk.VTK_STRING) tableNode.SetColumnType('Mean Mesh Distance', vtk.VTK_STRING) subjectCount = 0 for meshFileName in os.listdir(meshDirectory): if (not meshFileName.startswith(".")): #get corresponding landmarks lmFilePath, subjectID = self.findCorrespondingFilePath( lmDirectory, meshFileName) if (lmFilePath): currentLMNode = slicer.util.loadMarkupsFiducialList( lmFilePath) if bool(slmDirectory): # add semi-landmarks if present slmFilePath, sID = self.findCorrespondingFilePath( slmDirectory, meshFileName) if (slmFilePath): currentSLMNode = slicer.util.loadMarkupsFiducialList( slmFilePath) currentLMTotal = self.mergeLandmarks( templateLM, currentSLMNode) else: print("problem with reading semi-landmarks") return False else: currentLMTotal = currentLMNode else: print("problem with reading landmarks") return False # if mesh and lm file with same subject id exist, load into scene meshFilePath = os.path.join(meshDirectory, meshFileName) currentMeshNode = slicer.util.loadModel(meshFilePath) #get subject points into vtk array subjectPoints = vtk.vtkPoints() p = [0, 0, 0] for i in range(currentLMTotal.GetNumberOfMarkups()): currentLMTotal.GetMarkupPoint(0, i, p) subjectPoints.InsertNextPoint(p) transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(subjectPoints) transform.SetTargetLandmarks(templatePoints) transform.SetBasisToR() # for 3D transform transformNode = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLTransformNode", "TPS") transformNode.SetAndObserveTransformToParent(transform) currentMeshNode.SetAndObserveTransformNodeID( transformNode.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform( currentMeshNode) distanceFilter = vtk.vtkDistancePolyDataFilter() distanceFilter.SetInputData(0, templateMesh.GetPolyData()) distanceFilter.SetInputData(1, currentMeshNode.GetPolyData()) distanceFilter.SetSignedDistance(signedDistanceOption) distanceFilter.Update() distanceMap = distanceFilter.GetOutput() distanceArray = distanceMap.GetPointData().GetArray('Distance') #meanDistance = np.average(distanceArray) meanDistance = self.rmse(distanceArray) #save output distance map outputNode = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLModelNode", "ouputDistanceMap") outputNode.SetAndObservePolyData(distanceMap) outputFilename = os.path.join(outDirectory, str(subjectID) + '.vtp') slicer.util.saveNode(outputNode, outputFilename) #update table and subjectCount tableNode.AddEmptyRow() tableNode.SetCellText(subjectCount, 0, str(subjectID)) tableNode.SetCellText(subjectCount, 1, str(meanDistance)) subjectCount += 1 # clean up slicer.mrmlScene.RemoveNode(outputNode) slicer.mrmlScene.RemoveNode(transformNode) slicer.mrmlScene.RemoveNode(currentMeshNode) slicer.mrmlScene.RemoveNode(currentLMNode)
def onrunTransformButtonClicked(self): inputDirPath = self.inputDirSelector.currentPath outputDirPath = self.outputDirSelector.currentPath transformFilePath = self.transformationSelector.currentPath if not (inputDirPath) or not (outputDirPath) or not (transformFilePath): qt.QMessageBox.critical(slicer.util.mainWindow(), 'Harden Transform', 'Tranformation file, input and output directories are required') return # model files with these extensions are loaded and transformed extensions = ('.vtk', '.vtp', '.stl', '.ply') # function to check if a dir2 is a subdirectory of dir1 # returns true if dir2 is a subdirectory of dir1 or dir1 == dir2 def is_subdir(dir1, dir2): path = os.path.realpath(dir1) directory = os.path.realpath(dir2) relative = os.path.relpath(dir1, dir2) return not (relative == os.pardir or relative.startswith(os.pardir + os.sep)) # check if input and output folders are identical or output is subdirectory of input, if so, do nothing to avoid overwriting if is_subdir(inputDirPath,outputDirPath): qt.QMessageBox.critical(slicer.util.mainWindow(),'Harden Transform', 'Please select an output directory that is different from the input directory and not a subdirectory of the input to avoid overwriting') else: # load transform file checkSuccessT, transformNode = slicer.util.loadTransform(str(transformFilePath),returnNode = True) if not checkSuccessT: print 'Could not load transform file' qt.QMessageBox.information(slicer.util.mainWindow(),'Harden Transform', 'Could not load transform file') exit() logic = slicer.vtkSlicerTransformLogic() tNodeID = transformNode.GetID() transformCounter = 0; for root, subfolders, files in os.walk(inputDirPath): for file in files: # only consider model filetypes slicer an read and write (vtk,vtp,stl,ply) ext = os.path.splitext(file)[-1].lower() if ext in extensions: filePath = os.path.join(root,file) checkSuccessM, modelNode = slicer.util.loadModel(filePath,returnNode = True) if not checkSuccessM: print 'Could not load model file ' + str(filepath) qt.QMessageBox.information(slicer.util.mainWindow(),'Harden Transform', 'Could not load model file' + str(filepath)) modelFileName, modelFileExt = os.path.splitext(ntpath.basename(filePath)) modelNode.SetAndObserveTransformNodeID(tNodeID) # harden transform logic.hardenTransform(modelNode) ## save transformed node # if file with this name already exists in the output folder, append '_new' app = '' while os.path.isfile(os.path.join(outputDirPath,os.path.splitext(ntpath.basename(file))[0]+app+modelFileExt)): app += '_new' newFileName = modelFileName+app+modelFileExt slicer.util.saveNode(modelNode, os.path.join(outputDirPath,newFileName)) slicer.mrmlScene.RemoveNode(modelNode) transformCounter += 1 slicer.mrmlScene.RemoveNode(transformNode) statusString = 'Transformed ' + str(transformCounter) + ' models' print statusString qt.QMessageBox.information(slicer.util.mainWindow(),'Harden Transform', statusString)
def run(self, inputVolume, outputVolume, atlasVolume, initialTrans, outputTrans, atlasFid): """ Run the actual algorithm """ #check appropriate volume is selected if not self.isValidInputOutputData(inputVolume, outputVolume): slicer.util.errorDisplay('Input volume is the same as output volume. Choose a different output volume.') return False logging.info('.....Printing Initial Transform....') logging.info(initialTrans) #Create intermediate linear transform node self.linearTrans = slicer.vtkMRMLTransformNode() slicer.mrmlScene.AddNode(self.linearTrans) #Set parameters and run affine registration Step 1 cliParamsAffine = { 'fixedVolume' : inputVolume.GetID(), 'movingVolume' : atlasVolume.GetID(), 'linearTransform' : self.linearTrans.GetID() } cliParamsAffine.update({'samplingPercentage' : 1, 'initialTransformMode' : 'off', 'transformType' : 'Affine'}) cliParamsAffine.update({'numberOfIterations' : 3000, 'minimumStepLength' : 0.00001, 'maximumStepLength' : 0.05}) cliAffineTransREG = slicer.cli.run(slicer.modules.brainsfit, None, cliParamsAffine, wait_for_completion=True) logging.info('....Printing Affine Transform....') logging.info(self.linearTrans) #Apply linear transform result from step 1 on Atlas Volume atlasVolume.SetAndObserveTransformNodeID(self.linearTrans.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(atlasVolume) # Set parameters and run BSpline registration Step 2 cliParams = { 'fixedVolume' : inputVolume.GetID(), 'movingVolume' : atlasVolume.GetID(), 'bsplineTransform' : outputTrans.GetID() } #'initialTransform' : self.linearTrans.GetID() cliParams.update({ 'samplingPercentage' : 1, 'initialTransfomMode' : 'off' }) cliParams.update({ 'transformType' : 'BSpline', 'splineGridSize': '3,3,3'}) cliParams.update({ 'numberOfIterations' : 3000, 'minimumStepLength' : 0.00001, 'maximumStepLength' : 0.05}) cliParams.update({'costMetric' : 'NC' }) cliBSplineREG = slicer.cli.run(slicer.modules.brainsfit, None, cliParams, wait_for_completion=True) logging.info('....Printing BSpline Transform....') logging.info(outputTrans) #Apply BSpline transform on A-Value Fiducials atlasFid.SetAndObserveTransformNodeID(outputTrans.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(atlasFid) #Calculate New A-Value numOfFids = atlasFid.GetNumberOfFiducials() fidXYZ_RW = [0,0,0] #Round Window placeholder fidXYZ_LW = [0,0,0] #Lateral Wall placeholder print numOfFids for index in range(numOfFids): if index == 0: atlasFid.GetNthFiducialPosition(index, fidXYZ_RW) print fidXYZ_RW else: atlasFid.GetNthFiducialPosition(index, fidXYZ_LW) print fidXYZ_LW newAValue = math.sqrt( ((fidXYZ_RW[0] - fidXYZ_LW[0])**2) + \ ((fidXYZ_RW[1] - fidXYZ_LW[1])**2) + \ ((fidXYZ_LW[2] - fidXYZ_RW[2])**2) ) #Calculating CDL Estimates AlexiadesCDLoc = 4.16 * newAValue - 4 #CDL estimate from Alexiades et al. (2015) KochCDLoc = 4.16 * newAValue - 5.05 #CDL estimate from Koch et al. (2017) KochCDLlw = 3.86 * newAValue + 4.99 #CDL estimtae from Koch et al. (2017) #Display Patient ID and Estimated A Valuee outputDisp = "Patient ID:\n" + inputVolume.GetName() + \ "\n\nEstimated A Value:\n" + format(newAValue, '0.1f') + 'mm\n\n' + \ "Estimated CDL Values\n" + "CDL(oc)-1: " + format(AlexiadesCDLoc, '0.1f') + 'mm\n' + \ "CDL(oc)-2: " + format(KochCDLoc, '0.1f') + "mm\n" + \ "CDL(lw)-1: " + format(KochCDLlw, '0.1f') + "mm\n" slicer.util.infoDisplay(outputDisp) logging.info('Processing completed') #TODO - Deal with output Volume!! return True
def run(self, landmarkDirectory, meshDirectory, gridLandmarks, outputDirectory): #read all landmarks #landmarks = self.getLandmarks(landmarkDirectory) #print("landmark file shape: ", landmarks.shape) #[landmarkNumber,dim,subjectNumber] = landmarks.shape #get grid points #newGridPoints = self.getGridPoints(landmarks, gridLandmarks) #reflect gridPoints onto each surface #meshList = self.getAllSubjectMeshes(meshDirectory) #for i in range(subjectNumber): #mesh = self.readMesh(meshList(i)) #stickToSurface(newGridPoints(:,:,i), mesh) # save semi-landmarks #self.saveSemiLandmarks(newGridPoints, outputDirectory) #Read data S = slicer.util.getNode("gor_skull") L = slicer.util.getNode("Gorilla_template_LM1") surfacePolydata = S.GetPolyData() # Set up point locator for later pointLocator = vtk.vtkPointLocator() pointLocator.SetDataSet(surfacePolydata) pointLocator.BuildLocator() #set up locater for intersection with normal vector rays obbTree = vtk.vtkOBBTree() obbTree.SetDataSet(surfacePolydata) obbTree.BuildLocator() #Set up fiducial grid print("Set up grid") fiducialPoints = vtk.vtkPoints() point = [0, 0, 0] for pt in range(L.GetNumberOfControlPoints()): L.GetMarkupPoint(pt, 0, point) fiducialPoints.InsertNextPoint(point) fiducialPolydata = vtk.vtkPolyData() fiducialPolydata.SetPoints(fiducialPoints) delaunayFilter = vtk.vtkDelaunay2D() delaunayFilter.SetInputData(fiducialPolydata) delaunayFilter.Update() fiducialMesh = delaunayFilter.GetOutput() fiducialMeshModel = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLModelNode", "fiducialMesh") fiducialMeshModel.SetAndObservePolyData(fiducialMesh) #Set up triangle grid for projection gridPoints = vtk.vtkPoints() sampleRate = 3 for row in range(sampleRate): for col in range(sampleRate): if (row + col) < sampleRate: gridPoints.InsertNextPoint(row, col, 0) gridPolydata = vtk.vtkPolyData() gridPolydata.SetPoints(gridPoints) #Set up source and target for triangle transform sourcePoints = vtk.vtkPoints() targetPoints = vtk.vtkPoints() sourcePoints.InsertNextPoint(gridPoints.GetPoint(0)) sourcePoints.InsertNextPoint(gridPoints.GetPoint(sampleRate - 1)) sourcePoints.InsertNextPoint( gridPoints.GetPoint(gridPoints.GetNumberOfPoints() - 1)) #set up transform transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(sourcePoints) transform.SetBasisToR() transformNode = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLTransformNode", "TPS") transformNode.SetAndObserveTransformToParent(transform) #Iterate through each triangle in fiducial mesh and project sampled triangle idList = vtk.vtkIdList() triangleArray = fiducialMesh.GetPolys() triangleArray.InitTraversal() #define new landmark sets semilandmarkPoints = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLMarkupsFiducialNode", "semiLM") gridPoints = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLMarkupsFiducialNode", "grid") print("Iterate through cells") #iterate through each triangle cell in the landmark mesh for triangle in range(4): #(triangleArray.GetNumberOfCells()): print("Semilandmark n: ", semilandmarkPoints.GetNumberOfFiducials()) print("Triangle: ", triangle) triangleArray.GetNextCell(idList) print(idList) targetPoints.Reset() for verticeIndex in range(3): verticeID = idList.GetId(verticeIndex) verticeLocation = fiducialMesh.GetPoint(verticeID) targetPoints.InsertNextPoint(verticeLocation) print("Adding target point: ", verticeLocation) transform.SetTargetLandmarks(targetPoints) model = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLModelNode", "mesh_resampled") model.SetAndObservePolyData(gridPolydata) model.SetAndObserveTransformNodeID(transformNode.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(model) transformedPolydata = model.GetPolyData() # #get surface normal from each landmark point rayDirection = [0, 0, 0] normalArray = surfacePolydata.GetPointData().GetArray("Normals") # #calculate average normal from cell landmarks for verticeIndex in range(3): verticeLocation = targetPoints.GetPoint(verticeIndex) closestPointId = pointLocator.FindClosestPoint(verticeLocation) tempNormal = normalArray.GetTuple(closestPointId) print("Normal: ", tempNormal) rayDirection[0] += tempNormal[0] rayDirection[1] += tempNormal[1] rayDirection[2] += tempNormal[2] for dim in range(len(rayDirection)): rayDirection[dim] = rayDirection[dim] / 3 #get a sample distance for quality control m1 = model.GetPolyData().GetPoint(0) m2 = model.GetPolyData().GetPoint(1) #sampleDistance = 3*abs( (m1[0] - m2[0])+(m1[1] - m2[1])+ (m1[2] - m2[2])) sampleDistance = fiducialMesh.GetLength() / 10 print("Sample distance: ", sampleDistance) # iterate through each point in the resampled triangle for index in range(model.GetPolyData().GetNumberOfPoints()): print("Point: ", index) modelPoint = model.GetPolyData().GetPoint(index) gridPoints.AddFiducialFromArray(modelPoint) rayEndPoint = [0, 0, 0] for dim in range(len(rayEndPoint)): rayEndPoint[ dim] = modelPoint[dim] + rayDirection[dim] * 1000 intersectionIds = vtk.vtkIdList() intersectionPoints = vtk.vtkPoints() obbTree.IntersectWithLine(modelPoint, rayEndPoint, intersectionPoints, intersectionIds) #print("Intersections: ", intersectionIds.GetNumberOfIds()) #if there are intersections, update the point to most external one. if intersectionPoints.GetNumberOfPoints() > 0: exteriorPoint = intersectionPoints.GetPoint( intersectionPoints.GetNumberOfPoints() - 1) projectionDistance = abs( (exteriorPoint[0] - modelPoint[0]) + (exteriorPoint[1] - modelPoint[1]) + (exteriorPoint[2] - modelPoint[2])) semilandmarkPoints.AddFiducialFromArray(exteriorPoint) #if there are no intersections, reverse the normal vector else: for dim in range(len(rayEndPoint)): rayEndPoint[ dim] = modelPoint[dim] + rayDirection[dim] * -1000 obbTree.IntersectWithLine(modelPoint, rayEndPoint, intersectionPoints, intersectionIds) if intersectionPoints.GetNumberOfPoints() > 0: exteriorPoint = intersectionPoints.GetPoint(0) projectionDistance = abs( (exteriorPoint[0] - modelPoint[0]) + (exteriorPoint[1] - modelPoint[1]) + (exteriorPoint[2] - modelPoint[2])) if projectionDistance < sampleDistance: semilandmarkPoints.AddFiducialFromArray( exteriorPoint) else: print("projectionDistance distance: ", projectionDistance) closestPointId = pointLocator.FindClosestPoint( modelPoint) rayOrigin = surfacePolydata.GetPoint( closestPointId) semilandmarkPoints.AddFiducialFromArray(rayOrigin) #if none in reverse direction, use closest mesh point else: closestPointId = pointLocator.FindClosestPoint( modelPoint) rayOrigin = surfacePolydata.GetPoint(closestPointId) semilandmarkPoints.AddFiducialFromArray(rayOrigin) #remove temporary model node #slicer.mrmlScene.RemoveNode(model) slicer.mrmlScene.RemoveNode(transformNode) return True
def applyPatch(self, meshNode, LMNode, gridLandmarks, sampleRate, polydataNormalArray, maximumProjectionDistance=.25): surfacePolydata = meshNode.GetPolyData() gridPoints = vtk.vtkPoints() gridPoints.InsertNextPoint(0, 0, 0) gridPoints.InsertNextPoint(0, sampleRate - 1, 0) gridPoints.InsertNextPoint(sampleRate - 1, 0, 0) for row in range(1, sampleRate - 1): for col in range(1, sampleRate - 1): if (row + col) < (sampleRate - 1): gridPoints.InsertNextPoint(row, col, 0) gridPolydata = vtk.vtkPolyData() gridPolydata.SetPoints(gridPoints) sourcePoints = vtk.vtkPoints() targetPoints = vtk.vtkPoints() sourcePoints.InsertNextPoint(gridPoints.GetPoint(0)) sourcePoints.InsertNextPoint(gridPoints.GetPoint(1)) sourcePoints.InsertNextPoint(gridPoints.GetPoint(2)) point = [0, 0, 0] for gridVertex in gridLandmarks: LMNode.GetMarkupPoint(0, int(gridVertex - 1), point) targetPoints.InsertNextPoint(point) #transform grid to triangle transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(sourcePoints) transform.SetTargetLandmarks(targetPoints) transform.SetBasisToR() transformNode = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLTransformNode", "TPS") transformNode.SetAndObserveTransformToParent(transform) model = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLModelNode", "mesh_resampled") model.SetAndObservePolyData(gridPolydata) model.SetAndObserveTransformNodeID(transformNode.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(model) resampledPolydata = model.GetPolyData() pointLocator = vtk.vtkPointLocator() pointLocator.SetDataSet(surfacePolydata) pointLocator.BuildLocator() #get surface normal from each landmark point rayDirection = [0, 0, 0] for gridVertex in gridLandmarks: LMNode.GetMarkupPoint(0, int(gridVertex - 1), point) closestPointId = pointLocator.FindClosestPoint(point) tempNormal = polydataNormalArray.GetTuple(closestPointId) rayDirection[0] += tempNormal[0] rayDirection[1] += tempNormal[1] rayDirection[2] += tempNormal[2] #normalize vtk.vtkMath().Normalize(rayDirection) # # get normal at each grid point # gridNormals = np.zeros((3,3)) # gridIndex=0 # for gridVertex in gridLandmarks: # print(gridVertex) # LMNode.GetMarkupPoint(0,int(gridVertex-1),point) # closestPointId = pointLocator.FindClosestPoint(point) # tempNormal = polydataNormalArray.GetTuple(closestPointId) # print(tempNormal) # gridNormals[gridIndex] = tempNormal # gridIndex+=1 # print(gridNormals) #set up locater for intersection with normal vector rays obbTree = vtk.vtkOBBTree() obbTree.SetDataSet(surfacePolydata) obbTree.BuildLocator() #define new landmark sets semilandmarkNodeName = "semiLM_" + str(gridLandmarks[0]) + "_" + str( gridLandmarks[1]) + "_" + str(gridLandmarks[2]) semilandmarkPoints = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLMarkupsFiducialNode", semilandmarkNodeName) #get a sample distance for quality control m1 = model.GetPolyData().GetPoint(0) m2 = model.GetPolyData().GetPoint(1) m3 = model.GetPolyData().GetPoint(2) d1 = math.sqrt(vtk.vtkMath().Distance2BetweenPoints(m1, m2)) d2 = math.sqrt(vtk.vtkMath().Distance2BetweenPoints(m2, m3)) d3 = math.sqrt(vtk.vtkMath().Distance2BetweenPoints(m1, m3)) sampleDistance = (d1 + d2 + d3) # set initial three grid points for index in range(0, 3): origLMPoint = resampledPolydata.GetPoint(index) #landmarkLabel = LMNode.GetNthFiducialLabel(gridLandmarks[index]-1) landmarkLabel = str(gridLandmarks[index]) semilandmarkPoints.AddFiducialFromArray(origLMPoint, landmarkLabel) # calculate maximum projection distance projectionTolerance = maximumProjectionDistance / .25 #boundingBox = vtk.vtkBoundingBox() #boundingBox.AddBounds(surfacePolydata.GetBounds()) #diagonalDistance = boundingBox.GetDiagonalLength() #rayLength = math.sqrt(diagonalDistance) * projectionTolerance rayLength = sampleDistance * projectionTolerance # get normal projection intersections for remaining semi-landmarks for index in range(3, resampledPolydata.GetNumberOfPoints()): modelPoint = resampledPolydata.GetPoint(index) # ## get estimated normal vector # rayDirection = [0,0,0] # distance1=math.sqrt(vtk.vtkMath().Distance2BetweenPoints(modelPoint,resampledPolydata.GetPoint(0))) # distance2=math.sqrt(vtk.vtkMath().Distance2BetweenPoints(modelPoint,resampledPolydata.GetPoint(1))) # distance3=math.sqrt(vtk.vtkMath().Distance2BetweenPoints(modelPoint,resampledPolydata.GetPoint(2))) # distanceTotal = distance1 + distance2 + distance3 # weights=[(distance3+distance2)/distanceTotal, (distance1+distance3)/distanceTotal,(distance1+distance2)/distanceTotal] # for gridIndex in range(0,3): # rayDirection+=(weights[gridIndex]*gridNormals[gridIndex]) # vtk.vtkMath().Normalize(rayDirection) # ## rayEndPoint = [0, 0, 0] for dim in range(len(rayEndPoint)): rayEndPoint[ dim] = modelPoint[dim] + rayDirection[dim] * rayLength intersectionIds = vtk.vtkIdList() intersectionPoints = vtk.vtkPoints() obbTree.IntersectWithLine(modelPoint, rayEndPoint, intersectionPoints, intersectionIds) #if there are intersections, update the point to most external one. if intersectionPoints.GetNumberOfPoints() > 0: exteriorPoint = intersectionPoints.GetPoint( intersectionPoints.GetNumberOfPoints() - 1) semilandmarkPoints.AddFiducialFromArray(exteriorPoint) #if there are no intersections, reverse the normal vector else: for dim in range(len(rayEndPoint)): rayEndPoint[ dim] = modelPoint[dim] + rayDirection[dim] * -rayLength obbTree.IntersectWithLine(modelPoint, rayEndPoint, intersectionPoints, intersectionIds) if intersectionPoints.GetNumberOfPoints() > 0: exteriorPoint = intersectionPoints.GetPoint(0) semilandmarkPoints.AddFiducialFromArray(exteriorPoint) else: print("No intersection, using closest point") closestPointId = pointLocator.FindClosestPoint(modelPoint) rayOrigin = surfacePolydata.GetPoint(closestPointId) semilandmarkPoints.AddFiducialFromArray(rayOrigin) # update lock status and color semilandmarkPoints.SetLocked(True) semilandmarkPoints.GetDisplayNode().SetColor(random.random(), random.random(), random.random()) semilandmarkPoints.GetDisplayNode().SetSelectedColor( random.random(), random.random(), random.random()) semilandmarkPoints.GetDisplayNode().PointLabelsVisibilityOff() #clean up slicer.mrmlScene.RemoveNode(transformNode) slicer.mrmlScene.RemoveNode(model) print("Total points:", semilandmarkPoints.GetNumberOfFiducials()) return semilandmarkPoints
def run(self, inputT1Volume, inputFLAIRVolume, inputT2Volume, inputPDVolume, inputFAVolume, inputADCVolume, returnSpace, isBET, isMNI, lesionLoad, isLongitudinal, numberFollowUp, balanceHI, outputFolder, cutFraction, samplingPerc, grid, initiationMethod, numberOfThreads): """ Run the actual algorithm """ # # Defines reference image modality based on pre-defined order if data is not in MNI space # if not isMNI: if inputT1Volume is not None: referenceVolume = inputT1Volume logging.info('T1 volume found. Will be used as reference space.') elif inputT2Volume is not None: referenceVolume = inputT2Volume logging.info('T2 volume found. Will be used as reference space.') elif inputFLAIRVolume is not None: referenceVolume = inputFLAIRVolume logging.info('FLAIR volume found. Will be used as reference space.') elif inputPDVolume is not None: referenceVolume = inputPDVolume logging.info('PD volume found. Will be used as reference space.') else: logging.info('ERROR: At least one structural image should be provided. Aborting.') return False # # Defines count variable for step progression log # currentStep = 1 logging.info('Processing started') slicer.util.showStatusMessage("Processing started") # # Data space normalization to T1 space # volumesLogic = slicer.modules.volumes.logic() if not isMNI: if inputT2Volume is not None and inputT2Volume is not referenceVolume: try: slicer.util.showStatusMessage("Pre-processing: Conforming T2 volume to reference space...") regT2toRefTransform = slicer.vtkMRMLLinearTransformNode() slicer.mrmlScene.AddNode(regT2toRefTransform) clonedT2Volume = volumesLogic.CloneVolume(slicer.mrmlScene, inputT2Volume, "Cloned T2") self.conformInputSpace(referenceVolume, inputT2Volume, inputT2Volume, regT2toRefTransform, numberOfThreads) except: logging.info("Exception caught when trying to conform T2 image to reference space.") if inputFLAIRVolume is not None and inputFLAIRVolume is not referenceVolume: try: slicer.util.showStatusMessage("Pre-processing: Conforming T2-FLAIR volume to reference space...") regFLAIRtoRefTransform = slicer.vtkMRMLLinearTransformNode() slicer.mrmlScene.AddNode(regFLAIRtoRefTransform) clonedFLAIRVolume = volumesLogic.CloneVolume(slicer.mrmlScene, inputFLAIRVolume, "Cloned FLAIR") self.conformInputSpace(referenceVolume, inputFLAIRVolume, inputFLAIRVolume, regFLAIRtoRefTransform, numberOfThreads) except: logging.info("Exception caught when trying to create node for T2-FLAIR image in reference space.") if inputPDVolume is not None and inputPDVolume is not referenceVolume: try: slicer.util.showStatusMessage("Pre-processing: Conforming PD volume to reference space...") regPDtoRefTransform = slicer.vtkMRMLLinearTransformNode() slicer.mrmlScene.AddNode(regPDtoRefTransform) clonedPDVolume = volumesLogic.CloneVolume(slicer.mrmlScene, inputPDVolume, "Cloned PD") self.conformInputSpace(referenceVolume, inputPDVolume, inputPDVolume, regPDtoRefTransform, numberOfThreads) except: logging.info("Exception caught when trying to create node for PD image in reference space.") if inputFAVolume is not None: try: slicer.util.showStatusMessage("Pre-processing: Conforming DTI-FA map to reference space...") regFAtoRefTransform = slicer.vtkMRMLLinearTransformNode() slicer.mrmlScene.AddNode(regFAtoRefTransform) clonedFAVolume = volumesLogic.CloneVolume(slicer.mrmlScene, inputFAVolume, "Cloned FA") self.conformInputSpace(referenceVolume, inputFAVolume, inputFAVolume, regFAtoRefTransform, numberOfThreads) except: logging.info("Exception caught when trying to create node for FA image in reference space.") if inputADCVolume is not None: try: slicer.util.showStatusMessage("Pre-processing: Conforming DTI-ADC map to reference space...") regADCtoRefTransform = slicer.vtkMRMLLinearTransformNode() slicer.mrmlScene.AddNode(regADCtoRefTransform) clonedADCVolume = volumesLogic.CloneVolume(slicer.mrmlScene, inputADCVolume, "Cloned ADC") self.conformInputSpace(referenceVolume, inputADCVolume, inputADCVolume, regADCtoRefTransform, numberOfThreads) except: logging.info("Exception caught when trying to create node for ADC image in reference space.") slicer.util.showStatusMessage("Step "+str(currentStep)+": Reading brain templates...") logging.info("Step "+str(currentStep)+": Reading brain templates...") currentStep+=1 modulePath = os.path.dirname(slicer.modules.mslesionsimulator.path) if platform.system() is "Windows": databasePath = modulePath + "\\Resources\\MSlesion_database" else: databasePath = modulePath + "/Resources/MSlesion_database" if isBET: if platform.system() is "Windows": (readSuccess, MNINode)=slicer.util.loadVolume(databasePath+"\\MNI152_T1_1mm_brain.nii.gz",{},True) else: (readSuccess,MNINode)=slicer.util.loadVolume(databasePath + "/MNI152_T1_1mm_brain.nii.gz",{},True) else: if platform.system() is "Windows": (readSuccess, MNINode)=slicer.util.loadVolume(databasePath + "\\MNI152_T1_1mm.nii.gz",{},True) else: (readSuccess, MNINode)=slicer.util.loadVolume(databasePath + "/MNI152_T1_1mm.nii.gz",{},True) if not isMNI: # # Registration between Input Image and MNI Image Space # slicer.util.showStatusMessage("Step " + str(currentStep) + ": MNI152 template to native space...") logging.info("Step " + str(currentStep) + ": MNI152 template to native space...") currentStep += 1 MNI_ref = slicer.vtkMRMLScalarVolumeNode() slicer.mrmlScene.AddNode(MNI_ref) regMNItoRefTransform = slicer.vtkMRMLBSplineTransformNode() slicer.mrmlScene.AddNode(regMNItoRefTransform) self.doNonLinearRegistration(referenceVolume, MNINode, MNI_ref, regMNItoRefTransform, samplingPerc, grid, initiationMethod, numberOfThreads) # # Find lesion mask using Probability Image, lesion labels and desired Lesion Load # slicer.util.showStatusMessage("Step "+str(currentStep)+": Simulating MS lesion map...") logging.info("Step "+str(currentStep)+": Simulating MS lesion map...") currentStep+=1 lesionMap = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(lesionMap) if platform.system() is "Windows": self.doGenerateMask(MNINode, lesionLoad, lesionMap, databasePath+"\\labels-database") else: self.doGenerateMask(MNINode, lesionLoad, lesionMap, databasePath + "/labels-database") # Transforming lesion map to native space if not isMNI: # Get transform logic for hardening transforms transformLogic = slicer.vtkSlicerTransformLogic() self.applyRegistrationTransform(lesionMap,referenceVolume,lesionMap,regMNItoRefTransform,False, True) transformLogic.hardenTransform(lesionMap) # Filtering lesion map to minimize or exclude regions outside of WM if inputT1Volume is not None: # Lesion Map: T1 lesionMapT1 = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(lesionMapT1) lesionMapT1.SetName("T1_lesion_label") self.doFilterMask(inputT1Volume, lesionMap, lesionMapT1, cutFraction) if inputFLAIRVolume is not None: # Lesion Map: T2-FLAIR lesionMapFLAIR = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(lesionMapFLAIR) lesionMapFLAIR.SetName("T2FLAIR_lesion_label") self.doFilterMask(inputFLAIRVolume, lesionMap, lesionMapFLAIR, cutFraction) if inputT2Volume is not None: # Lesion Map: T2 lesionMapT2 = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(lesionMapT2) lesionMapT2.SetName("T2_lesion_label") self.doFilterMask(inputT2Volume, lesionMap, lesionMapT2, cutFraction) if inputPDVolume is not None: # Lesion Map: PD lesionMapPD = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(lesionMapPD) lesionMapPD.SetName("PD_lesion_label") self.doFilterMask(inputPDVolume, lesionMap, lesionMapPD, cutFraction) if inputFAVolume is not None: # Lesion Map: DTI-FA lesionMapFA = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(lesionMapFA) lesionMapFA.SetName("FA_lesion_label") self.doFilterMask(inputFAVolume, lesionMap, lesionMapFA, cutFraction) if inputADCVolume is not None: # Lesion Map: DTI-FA lesionMapADC = slicer.vtkMRMLLabelMapVolumeNode() slicer.mrmlScene.AddNode(lesionMapADC) lesionMapADC.SetName("ADC_lesion_label") self.doFilterMask(inputADCVolume, lesionMap, lesionMapADC, cutFraction) # # Generating lesions in each input image # # List of parameters: Sigma Sigma= {} Sigma["T1"]=0.75 Sigma["T2"]=0.75 Sigma["PD"]=0.75 Sigma["T2FLAIR"]=0.75 Sigma["DTI-FA"]=1.5 Sigma["DTI-ADC"]=1.3 variability = 0.5 if not isLongitudinal: if inputT1Volume is not None: try: slicer.util.showStatusMessage("Step "+str(currentStep)+": Applying lesion deformation on T1 volume...") logging.info("Step "+str(currentStep)+": Applying lesion deformation on T1 volume...") self.doSimulateLesions(inputT1Volume, "T1", lesionMapT1, inputT1Volume, Sigma["T1"], variability) except: logging.info("Exception caught when trying to apply lesion deformation in T1 volume.") if inputFLAIRVolume is not None: try: slicer.util.showStatusMessage("Step "+str(currentStep)+": Applying lesion deformation on T2-FLAIR volume...") logging.info("Step "+str(currentStep)+": Applying lesion deformation on T2-FLAIR volume...") self.doSimulateLesions(inputFLAIRVolume, "T2-FLAIR", lesionMapFLAIR, inputFLAIRVolume, Sigma["T2FLAIR"], variability) except: logging.info("Exception caught when trying to apply lesion deformation in T2-FLAIR volume.") if inputT2Volume is not None: try: slicer.util.showStatusMessage("Step "+str(currentStep)+": Applying lesion deformation on T2 volume...") logging.info("Step "+str(currentStep)+": Applying lesion deformation on T2 volume...") self.doSimulateLesions(inputT2Volume, "T2", lesionMapT2, inputT2Volume, Sigma["T2"], variability) except: logging.info("Exception caught when trying to apply lesion deformation in T2 volume.") if inputPDVolume is not None: try: slicer.util.showStatusMessage("Step "+str(currentStep)+": Applying lesion deformation on PD volume...") logging.info("Step "+str(currentStep)+": Applying lesion deformation on PD volume...") self.doSimulateLesions(inputPDVolume, "PD", lesionMapPD, inputPDVolume, Sigma["PD"], variability) except: logging.info("Exception caught when trying to apply lesion deformation in PD volume.") if inputFAVolume is not None: try: slicer.util.showStatusMessage("Step "+str(currentStep)+": Applying lesion deformation on DTI-FA map...") logging.info("Step "+str(currentStep)+": Applying lesion deformation on DTI-FA volume...") self.doSimulateLesions(inputFAVolume, "DTI-FA", lesionMapFA, inputFAVolume, Sigma["DTI-FA"], variability) except: logging.info("Exception caught when trying to apply lesion deformation in FA volume.") if inputADCVolume is not None: try: slicer.util.showStatusMessage("Step "+str(currentStep)+": Applying lesion deformation on DTI-ADC map...") logging.info("Step "+str(currentStep)+": Applying lesion deformation on DTI-ADC volume...") self.doSimulateLesions(inputADCVolume, "DTI-ADC", lesionMapADC, inputADCVolume, Sigma["DTI-ADC"], variability) except: logging.info("Exception caught when trying to apply lesion deformation in ADC volume.") else: # # Simulate Longitudinal Exams # if inputT1Volume is not None: try: slicer.util.showStatusMessage("Extra: Generating longitudinal lesion deformation on T1 volume...") logging.info("Extra: Generating longitudinal lesion deformation on T1 volume......") self.doLongitudinalExams(inputT1Volume, "T1", lesionMapT1, outputFolder, numberFollowUp, balanceHI, Sigma["T1"], variability) except: logging.info("Exception caught when trying to generate longitudinal lesion deformation in T1 volume.") if inputFLAIRVolume is not None: try: slicer.util.showStatusMessage("Extra: Generating longitudinal lesion deformation on T2-FLAIR volume...") logging.info("Extra: Generating longitudinal lesion deformation on T2-FLAIR volume......") self.doLongitudinalExams(inputFLAIRVolume, "T2-FLAIR", lesionMapFLAIR, outputFolder, numberFollowUp, balanceHI, Sigma["T2FLAIR"], variability) except: logging.info("Exception caught when trying to generate longitudinal lesion deformation in T2-FLAIR volume.") if inputT2Volume is not None: try: slicer.util.showStatusMessage("Extra: Generating longitudinal lesion deformation on T2 volume...") logging.info("Extra: Generating longitudinal lesion deformation on T2 volume...") self.doLongitudinalExams(inputT2Volume, "T2", lesionMapT2, outputFolder, numberFollowUp, balanceHI, Sigma["T2"], variability) except: logging.info("Exception caught when trying to generate longitudinal lesion deformation in T2 volume.") if inputPDVolume is not None: try: slicer.util.showStatusMessage("Extra: Generating longitudinal lesion deformation on PD volume...") logging.info("Extra: Generating longitudinal lesion deformation on PD volume...") self.doLongitudinalExams(inputPDVolume, "PD", lesionMapPD, outputFolder, numberFollowUp, balanceHI, Sigma["PD"], variability) except: logging.info("Exception caught when trying to generate longitudinal lesion deformation in PD volume.") if inputFAVolume is not None: try: slicer.util.showStatusMessage("Extra: Generating longitudinal lesion deformation on DTI-FA volume...") logging.info("Extra: Generating longitudinal lesion deformation on DTI-FA volume...") self.doLongitudinalExams(inputFAVolume, "DTI-FA", lesionMapFA, outputFolder, numberFollowUp, balanceHI, Sigma["DTI-FA"], variability) except: logging.info("Exception caught when trying to generate longitudinal lesion deformation in FA volume.") if inputADCVolume is not None: try: slicer.util.showStatusMessage("Extra: Generating longitudinal lesion deformation on DTI-ADC volume...") logging.info("Extra: Generating longitudinal lesion deformation on DTI-ADC volume...") self.doLongitudinalExams(inputADCVolume, "DTI-ADC", lesionMapADC, outputFolder, numberFollowUp, balanceHI, Sigma["DTI-ADC"], variability) except: logging.info("Exception caught when trying to generate longitudinal lesion deformation in ADC volume.") currentStep+=1 # # Return inputs to its original space # if returnSpace and not isMNI: if inputT2Volume is not None and inputT2Volume is not referenceVolume: try: slicer.util.showStatusMessage("post-processing: Returning T2 image space...") logging.info("post-processing: Returning T2 image space...") # T2 inverse transform self.applyRegistrationTransform(inputT2Volume,clonedT2Volume,inputT2Volume,regT2toRefTransform,True,False) except: logging.info("Exception caught when trying to return T2 image space.") if inputFLAIRVolume is not None and inputFLAIRVolume is not referenceVolume: try: slicer.util.showStatusMessage("post-processing: Returning T2-FLAIR image space...") logging.info("post-processing: Returning T2-FLAIR image space...") # T2-FLAIR inverse transform self.applyRegistrationTransform(inputFLAIRVolume,clonedFLAIRVolume,inputFLAIRVolume,regFLAIRtoRefTransform,True,False) except: logging.info("Exception caught when trying to return T2-FLAIR image space.") if inputPDVolume is not None and inputPDVolume is not referenceVolume: try: slicer.util.showStatusMessage("post-processing: Returning PD image space...") logging.info("post-processing: Returning PD image space...") # PD inverse transform self.applyRegistrationTransform(inputPDVolume, clonedPDVolume, inputPDVolume, regPDtoRefTransform, True, False) except: logging.info("Exception caught when trying to return PD image space.") if inputFAVolume is not None: try: slicer.util.showStatusMessage("post-processing: Returning DTI-FA map space...") logging.info("post-processing: Returning DTI-FA image space...") # DTI-FA inverse transform self.applyRegistrationTransform(inputFAVolume, clonedFAVolume, inputFAVolume, regFAtoRefTransform, True, False) except: logging.info("Exception caught when trying to return FA image space.") if inputADCVolume is not None: try: slicer.util.showStatusMessage("post-processing: Returning DTI-ADC map space...") logging.info("post-processing: Returning DTI-ADC image space...") # DTI-ADC inverse transform self.applyRegistrationTransform(inputADCVolume, clonedADCVolume, inputADCVolume, regADCtoRefTransform, True, False) except: logging.info("Exception caught when trying to return ADC image space.") # Removing unnecessary nodes slicer.mrmlScene.RemoveNode(lesionMap) slicer.mrmlScene.RemoveNode(MNINode) if not isMNI: slicer.mrmlScene.RemoveNode(MNI_ref) slicer.mrmlScene.RemoveNode(regMNItoRefTransform) if inputFLAIRVolume is not None and inputFLAIRVolume is not referenceVolume: try: slicer.mrmlScene.RemoveNode(regFLAIRtoRefTransform) slicer.mrmlScene.RemoveNode(clonedFLAIRVolume) except: logging.info('Exception caught when trying to delete FLAIR in T1 space node.') if inputT2Volume is not None and inputT2Volume is not referenceVolume: try: slicer.mrmlScene.RemoveNode(regT2toRefTransform) slicer.mrmlScene.RemoveNode(clonedT2Volume) except: logging.info('Exception caught when trying to delete T2 in T1 space node.') if inputPDVolume is not None and inputPDVolume is not referenceVolume: try: slicer.mrmlScene.RemoveNode(regPDtoRefTransform) slicer.mrmlScene.RemoveNode(clonedPDVolume) except: logging.info('Exception caught when trying to delete PD in T1 space node.') if inputFAVolume is not None: try: slicer.mrmlScene.RemoveNode(regFAtoRefTransform) slicer.mrmlScene.RemoveNode(clonedFAVolume) except: logging.info('Exception caught when trying to delete FA in T1 space node.') if inputADCVolume is not None: try: slicer.mrmlScene.RemoveNode(regADCtoRefTransform) slicer.mrmlScene.RemoveNode(clonedADCVolume) except: logging.info('Exception caught when trying to delete ADC in T1 space node.') slicer.util.showStatusMessage("Processing completed") logging.info('Processing completed') return True
def run(self, meshNode, LMNode, gridLandmarks, sampleRate): surfacePolydata = meshNode.GetPolyData() gridPoints = vtk.vtkPoints() gridPoints.InsertNextPoint(0, 0, 0) gridPoints.InsertNextPoint(0, sampleRate - 1, 0) gridPoints.InsertNextPoint(sampleRate - 1, 0, 0) for row in range(1, sampleRate - 1): for col in range(1, sampleRate - 1): if (row + col) < (sampleRate - 1): gridPoints.InsertNextPoint(row, col, 0) gridPolydata = vtk.vtkPolyData() gridPolydata.SetPoints(gridPoints) sourcePoints = vtk.vtkPoints() targetPoints = vtk.vtkPoints() #sourcePoints.InsertNextPoint(gridPoints.GetPoint(0)) #sourcePoints.InsertNextPoint(gridPoints.GetPoint(sampleRate-1)) #sourcePoints.InsertNextPoint(gridPoints.GetPoint(gridPoints.GetNumberOfPoints()-1)) sourcePoints.InsertNextPoint(gridPoints.GetPoint(0)) sourcePoints.InsertNextPoint(gridPoints.GetPoint(1)) sourcePoints.InsertNextPoint(gridPoints.GetPoint(2)) point = [0, 0, 0] for gridVertex in gridLandmarks: LMNode.GetMarkupPoint(int(gridVertex - 1), 0, point) targetPoints.InsertNextPoint(point) #transform grid to triangle transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(sourcePoints) transform.SetTargetLandmarks(targetPoints) transform.SetBasisToR() transformNode = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLTransformNode", "TPS") transformNode.SetAndObserveTransformToParent(transform) model = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLModelNode", "mesh_resampled") model.SetAndObservePolyData(gridPolydata) model.SetAndObserveTransformNodeID(transformNode.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform(model) resampledPolydata = model.GetPolyData() pointLocator = vtk.vtkPointLocator() pointLocator.SetDataSet(surfacePolydata) pointLocator.BuildLocator() #get surface normal from each landmark point rayDirection = [0, 0, 0] normalArray = surfacePolydata.GetPointData().GetArray("Normals") if (not normalArray): normalFilter = vtk.vtkPolyDataNormals() normalFilter.ComputePointNormalsOn() normalFilter.SetInputData(surfacePolydata) normalFilter.Update() normalArray = normalFilter.GetOutput().GetPointData().GetArray( "Normals") if (not normalArray): print("Error: no normal array") for gridVertex in gridLandmarks: LMNode.GetMarkupPoint(int(gridVertex - 1), 0, point) closestPointId = pointLocator.FindClosestPoint(point) tempNormal = normalArray.GetTuple(closestPointId) rayDirection[0] += tempNormal[0] rayDirection[1] += tempNormal[1] rayDirection[2] += tempNormal[2] #calculate average for dim in range(len(rayDirection)): rayDirection[dim] = rayDirection[dim] / 4 #set up locater for intersection with normal vector rays obbTree = vtk.vtkOBBTree() obbTree.SetDataSet(surfacePolydata) obbTree.BuildLocator() #define new landmark sets semilandmarkNodeName = "semiLM_" + str(gridLandmarks[0]) + "_" + str( gridLandmarks[1]) + "_" + str(gridLandmarks[2]) semilandmarkPoints = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLMarkupsFiducialNode", semilandmarkNodeName) #get a sample distance for quality control m1 = model.GetPolyData().GetPoint(0) m2 = model.GetPolyData().GetPoint(1) m3 = model.GetPolyData().GetPoint(1) d1 = math.sqrt(vtk.vtkMath().Distance2BetweenPoints(m1, m2)) d2 = math.sqrt(vtk.vtkMath().Distance2BetweenPoints(m2, m3)) d3 = math.sqrt(vtk.vtkMath().Distance2BetweenPoints(m1, m3)) sampleDistance = (d1 + d2 + d3) # set initial three grid points for index in range(0, 3): origLMPoint = resampledPolydata.GetPoint(index) #landmarkLabel = LMNode.GetNthFiducialLabel(gridLandmarks[index]-1) landmarkLabel = str(gridLandmarks[index]) semilandmarkPoints.AddFiducialFromArray(origLMPoint, landmarkLabel) # calculate maximum projection distance projectionTolerance = 2 boundingBox = vtk.vtkBoundingBox() boundingBox.AddBounds(surfacePolydata.GetBounds()) diagonalDistance = boundingBox.GetDiagonalLength() #rayLength = math.sqrt(diagonalDistance) * projectionTolerance rayLength = sampleDistance # get normal projection intersections for remaining semi-landmarks print("Target number of points: ", resampledPolydata.GetNumberOfPoints()) for index in range(3, resampledPolydata.GetNumberOfPoints()): modelPoint = resampledPolydata.GetPoint(index) rayEndPoint = [0, 0, 0] for dim in range(len(rayEndPoint)): rayEndPoint[ dim] = modelPoint[dim] + rayDirection[dim] * rayLength intersectionIds = vtk.vtkIdList() intersectionPoints = vtk.vtkPoints() obbTree.IntersectWithLine(modelPoint, rayEndPoint, intersectionPoints, intersectionIds) #if there are intersections, update the point to most external one. if intersectionPoints.GetNumberOfPoints() > 0: exteriorPoint = intersectionPoints.GetPoint( intersectionPoints.GetNumberOfPoints() - 1) #projectionDistance=math.sqrt(vtk.vtkMath().Distance2BetweenPoints(exteriorPoint, modelPoint)) semilandmarkPoints.AddFiducialFromArray(exteriorPoint) #if there are no intersections, reverse the normal vector else: for dim in range(len(rayEndPoint)): rayEndPoint[ dim] = modelPoint[dim] + rayDirection[dim] * -rayLength obbTree.IntersectWithLine(modelPoint, rayEndPoint, intersectionPoints, intersectionIds) if intersectionPoints.GetNumberOfPoints() > 0: exteriorPoint = intersectionPoints.GetPoint(0) semilandmarkPoints.AddFiducialFromArray(exteriorPoint) #projectionDistance=math.sqrt(vtk.vtkMath().Distance2BetweenPoints(exteriorPoint, modelPoint)) #if projectionDistance < sampleDistance: # semilandmarkPoints.AddFiducialFromArray(exteriorPoint) #else: # closestPointId = pointLocator.FindClosestPoint(modelPoint) # rayOrigin = surfacePolydata.GetPoint(closestPointId) # semilandmarkPoints.AddFiducialFromArray(rayOrigin) #if none in reverse direction, use closest mesh point else: closestPointId = pointLocator.FindClosestPoint(modelPoint) rayOrigin = surfacePolydata.GetPoint(closestPointId) semilandmarkPoints.AddFiducialFromArray(rayOrigin) # update lock status and color semilandmarkPoints.SetLocked(True) semilandmarkPoints.GetDisplayNode().SetColor(random.random(), random.random(), random.random()) semilandmarkPoints.GetDisplayNode().SetSelectedColor( random.random(), random.random(), random.random()) semilandmarkPoints.GetDisplayNode().PointLabelsVisibilityOff() #clean up slicer.mrmlScene.RemoveNode(transformNode) slicer.mrmlScene.RemoveNode(model) print("Total points:", semilandmarkPoints.GetNumberOfFiducials()) return semilandmarkPoints
def confirmMove(self): logic = slicer.vtkSlicerTransformLogic() logic.hardenTransform(self.activeNode) self.confirmAction() self.endMove()
def run(self, baseMeshNode, baseLMNode, semiLMNode, meshDirectory, lmDirectory, ouputDirectory, outputExtension, scaleProjection): SLLogic = CreateSemiLMPatches.CreateSemiLMPatchesLogic() targetPoints = vtk.vtkPoints() point = [0, 0, 0] # estimate a sample size usingn semi-landmark spacing sampleArray = np.zeros(shape=(25, 3)) for i in range(25): semiLMNode.GetMarkupPoint(0, i, point) sampleArray[i, :] = point sampleDistances = self.distanceMatrix(sampleArray) minimumMeshSpacing = sampleDistances[sampleDistances.nonzero()].min( axis=0) rayLength = minimumMeshSpacing * (scaleProjection) for i in range(baseLMNode.GetNumberOfFiducials()): baseLMNode.GetMarkupPoint(0, i, point) targetPoints.InsertNextPoint(point) for meshFileName in os.listdir(meshDirectory): if (not meshFileName.startswith(".")): print(meshFileName) lmFileList = os.listdir(lmDirectory) meshFilePath = os.path.join(meshDirectory, meshFileName) regex = re.compile(r'\d+') subjectID = [int(x) for x in regex.findall(meshFileName)][0] for lmFileName in lmFileList: if str(subjectID) in lmFileName: # if mesh and lm file with same subject id exist, load into scene currentMeshNode = slicer.util.loadModel(meshFilePath) lmFilePath = os.path.join(lmDirectory, lmFileName) success, currentLMNode = slicer.util.loadMarkupsFiducialList( lmFilePath) # set up transform between base lms and current lms sourcePoints = vtk.vtkPoints() for i in range(currentLMNode.GetNumberOfMarkups()): currentLMNode.GetMarkupPoint(0, i, point) sourcePoints.InsertNextPoint(point) transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(sourcePoints) transform.SetTargetLandmarks(targetPoints) transform.SetBasisToR() # for 3D transform transformNode = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLTransformNode", "TPS") transformNode.SetAndObserveTransformToParent(transform) # apply transform to the current surface mesh currentMeshNode.SetAndObserveTransformNodeID( transformNode.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform( currentMeshNode) # project semi-landmarks resampledLandmarkNode = slicer.mrmlScene.AddNewNodeByClass( 'vtkMRMLMarkupsFiducialNode', meshFileName + '_SL_warped') success = SLLogic.projectPoints( baseMeshNode, currentMeshNode, semiLMNode, resampledLandmarkNode, rayLength) transformNode.Inverse() resampledLandmarkNode.SetAndObserveTransformNodeID( transformNode.GetID()) slicer.vtkSlicerTransformLogic().hardenTransform( resampledLandmarkNode) # transfer point data for index in range( semiLMNode.GetNumberOfControlPoints()): fiducialLabel = semiLMNode.GetNthControlPointLabel( index) fiducialDescription = semiLMNode.GetNthControlPointDescription( index) resampledLandmarkNode.SetNthControlPointLabel( index, fiducialLabel) resampledLandmarkNode.SetNthControlPointDescription( index, fiducialDescription) # save output file outputFileName = meshFileName + '_SL_warped' + outputExtension outputFilePath = os.path.join(ouputDirectory, outputFileName) slicer.util.saveNode(resampledLandmarkNode, outputFilePath) # clean up slicer.mrmlScene.RemoveNode(resampledLandmarkNode) slicer.mrmlScene.RemoveNode(currentLMNode) slicer.mrmlScene.RemoveNode(currentMeshNode) slicer.mrmlScene.RemoveNode(transformNode)