def gridTransformFromCorners(self, volumeNode, sourceCorners, targetCorners): """Create a grid transform that maps between the current and the desired corners. """ # sanity check columns, rows, slices = volumeNode.GetImageData().GetDimensions() cornerShape = (slices, 2, 2, 3) if not (sourceCorners.shape == cornerShape and targetCorners.shape == cornerShape): raise Exception( "Corner shapes do not match volume dimensions %s, %s, %s" % (sourceCorners.shape, targetCorners.shape, cornerShape)) # create the grid transform node gridTransform = slicer.vtkMRMLGridTransformNode() gridTransform.SetName( slicer.mrmlScene.GenerateUniqueName(volumeNode.GetName() + ' acquisition transform')) slicer.mrmlScene.AddNode(gridTransform) # place grid transform in the same subject hierarchy folder as the volume node shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode( slicer.mrmlScene) volumeParentItemId = shNode.GetItemParent( shNode.GetItemByDataNode(volumeNode)) shNode.SetItemParent(shNode.GetItemByDataNode(gridTransform), volumeParentItemId) # create a grid transform with one vector at the corner of each slice # the transform is in the same space and orientation as the volume node gridImage = vtk.vtkImageData() gridImage.SetOrigin(*volumeNode.GetOrigin()) gridImage.SetDimensions(2, 2, slices) sourceSpacing = volumeNode.GetSpacing() gridImage.SetSpacing(sourceSpacing[0] * columns, sourceSpacing[1] * rows, sourceSpacing[2]) gridImage.AllocateScalars(vtk.VTK_DOUBLE, 3) transform = slicer.vtkOrientedGridTransform() directionMatrix = vtk.vtkMatrix4x4() volumeNode.GetIJKToRASDirectionMatrix(directionMatrix) transform.SetGridDirectionMatrix(directionMatrix) transform.SetDisplacementGridData(gridImage) gridTransform.SetAndObserveTransformToParent(transform) volumeNode.SetAndObserveTransformNodeID(gridTransform.GetID()) # populate the grid so that each corner of each slice # is mapped from the source corner to the target corner displacements = slicer.util.arrayFromGridTransform(gridTransform) for sliceIndex in range(slices): for row in range(2): for column in range(2): displacements[sliceIndex][row][column] = targetCorners[ sliceIndex][row][column] - sourceCorners[ sliceIndex][row][column]
def onExportGrid(self): """Converts the current thin plate transform to a grid""" self.hotUpdateButton = None state = self.registrationState() # since the transform is ras-to-ras, we find the extreme points # in ras space of the fixed (target) volume and fix the unoriented # box around it. Sample the grid transform at the resolution of # the fixed volume, which may be a bit overkill but it should aways # work without too much loss. rasBounds = [ 0, ] * 6 state.fixed.GetRASBounds(rasBounds) from math import floor, ceil origin = list(map(int, map(floor, rasBounds[::2]))) maxes = list(map(int, map(ceil, rasBounds[1::2]))) boundSize = [m - o for m, o in zip(maxes, origin)] spacing = state.fixed.GetSpacing() samples = [ceil(int(b / s)) for b, s in zip(boundSize, spacing)] extent = [ 0, ] * 6 extent[::2] = [ 0, ] * 3 extent[1::2] = samples extent = list(map(int, extent)) toGrid = vtk.vtkTransformToGrid() toGrid.SetGridOrigin(origin) toGrid.SetGridSpacing(state.fixed.GetSpacing()) toGrid.SetGridExtent(extent) toGrid.SetInput( state.transform.GetTransformFromParent()) # same in VTKv 5 & 6 toGrid.Update() gridTransform = slicer.vtkOrientedGridTransform() if vtk.VTK_MAJOR_VERSION < 6: gridTransform.SetDisplacementGrid( toGrid.GetOutput()) # different in VTKv 5 & 6 else: gridTransform.SetDisplacementGridData(toGrid.GetOutput()) gridNode = slicer.vtkMRMLGridTransformNode() gridNode.SetAndObserveTransformFromParent(gridTransform) gridNode.SetName(state.transform.GetName() + "-grid") slicer.mrmlScene.AddNode(gridNode)
def gridTransformFromCorners(self,volumeNode,sourceCorners,targetCorners): """Create a grid transform that maps between the current and the desired corners. """ # sanity check columns, rows, slices = volumeNode.GetImageData().GetDimensions() cornerShape = (slices, 2, 2, 3) if not (sourceCorners.shape == cornerShape and targetCorners.shape == cornerShape): raise Exception("Corner shapes do not match volume dimensions %s, %s, %s" % (sourceCorners.shape, targetCorners.shape, cornerShape)) # create the grid transform node gridTransform = slicer.vtkMRMLGridTransformNode() gridTransform.SetName(slicer.mrmlScene.GenerateUniqueName(volumeNode.GetName()+' acquisition transform')) slicer.mrmlScene.AddNode(gridTransform) # place grid transform in the same subject hierarchy folder as the volume node shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene) volumeParentItemId = shNode.GetItemParent(shNode.GetItemByDataNode(volumeNode)) shNode.SetItemParent(shNode.GetItemByDataNode(gridTransform), volumeParentItemId) # create a grid transform with one vector at the corner of each slice # the transform is in the same space and orientation as the volume node gridImage = vtk.vtkImageData() gridImage.SetOrigin(*volumeNode.GetOrigin()) gridImage.SetDimensions(2, 2, slices) sourceSpacing = volumeNode.GetSpacing() gridImage.SetSpacing(sourceSpacing[0] * columns, sourceSpacing[1] * rows, sourceSpacing[2]) gridImage.AllocateScalars(vtk.VTK_DOUBLE, 3) transform = slicer.vtkOrientedGridTransform() directionMatrix = vtk.vtkMatrix4x4() volumeNode.GetIJKToRASDirectionMatrix(directionMatrix) transform.SetGridDirectionMatrix(directionMatrix) transform.SetDisplacementGridData(gridImage) gridTransform.SetAndObserveTransformToParent(transform) volumeNode.SetAndObserveTransformNodeID(gridTransform.GetID()) # populate the grid so that each corner of each slice # is mapped from the source corner to the target corner displacements = slicer.util.arrayFromGridTransform(gridTransform) for sliceIndex in range(slices): for row in range(2): for column in range(2): displacements[sliceIndex][row][column] = targetCorners[sliceIndex][row][column] - sourceCorners[sliceIndex][row][column]
def onExportGrid(self): """Converts the current thin plate transform to a grid""" state = self.registationState() # since the transform is ras-to-ras, we find the extreme points # in ras space of the fixed (target) volume and fix the unoriented # box around it. Sample the grid transform at the resolution of # the fixed volume, which may be a bit overkill but it should aways # work without too much loss. rasBounds = [0,]*6 state.fixed.GetRASBounds(rasBounds) from math import floor, ceil origin = map(int,map(floor,rasBounds[::2])) maxes = map(int,map(ceil,rasBounds[1::2])) boundSize = [m - o for m,o in zip(maxes,origin) ] spacing = state.fixed.GetSpacing() samples = [ceil(b / s) for b,s in zip(boundSize,spacing)] extent = [0,]*6 extent[::2] = [0,]*3 extent[1::2] = samples extent = map(int,extent) toGrid = vtk.vtkTransformToGrid() toGrid.SetGridOrigin(origin) toGrid.SetGridSpacing(state.fixed.GetSpacing()) toGrid.SetGridExtent(extent) toGrid.SetInput(state.transform.GetTransformFromParent()) # same in VTKv 5 & 6 toGrid.Update() gridTransform = vtk.vtkGridTransform() if vtk.VTK_MAJOR_VERSION < 6: gridTransform.SetDisplacementGrid(toGrid.GetOutput()) # different in VTKv 5 & 6 else: gridTransform.SetDisplacementGridData(toGrid.GetOutput()) gridNode = slicer.vtkMRMLGridTransformNode() gridNode.SetAndObserveTransformFromParent(gridTransform) gridNode.SetName(state.transform.GetName()+"-grid") slicer.mrmlScene.AddNode(gridNode)
def createAcquisitionTransform(self, volumeNode, metadata): # Creates transform that applies scan conversion transform probeRadius = metadata['CurvatureRadiusProbe'] trackRadius = metadata['CurvatureRadiusTrack'] if trackRadius != 0.0: raise ValueError(f"Curvature Radius (Track) is {trackRadius}. Currently, only volume with zero radius can be imported.") # Create a sampling grid for the transform import numpy as np spacing = np.array(volumeNode.GetSpacing()) averageSpacing = (spacing[0] + spacing[1] + spacing[2]) / 3.0 voxelsPerTransformControlPoint = 20 # the transform is changing smoothly, so we don't need to add too many control points gridSpacingMm = averageSpacing * voxelsPerTransformControlPoint gridSpacingVoxel = np.floor(gridSpacingMm / spacing).astype(int) gridAxesIJK = [] imageData = volumeNode.GetImageData() extent = imageData.GetExtent() for axis in range(3): gridAxesIJK.append(list(range(extent[axis * 2], extent[axis * 2 + 1] + gridSpacingVoxel[axis], gridSpacingVoxel[axis]))) samplingPoints_shape = [len(gridAxesIJK[0]), len(gridAxesIJK[1]), len(gridAxesIJK[2]), 3] # create a grid transform with one vector at the corner of each slice # the transform is in the same space and orientation as the volume node import vtk gridImage = vtk.vtkImageData() gridImage.SetOrigin(*volumeNode.GetOrigin()) gridImage.SetDimensions(samplingPoints_shape[:3]) gridImage.SetSpacing(gridSpacingVoxel[0] * spacing[0], gridSpacingVoxel[1] * spacing[1], gridSpacingVoxel[2] * spacing[2]) gridImage.AllocateScalars(vtk.VTK_DOUBLE, 3) transform = slicer.vtkOrientedGridTransform() directionMatrix = vtk.vtkMatrix4x4() volumeNode.GetIJKToRASDirectionMatrix(directionMatrix) transform.SetGridDirectionMatrix(directionMatrix) transform.SetDisplacementGridData(gridImage) # create the grid transform node gridTransform = slicer.vtkMRMLGridTransformNode() gridTransform.SetName(slicer.mrmlScene.GenerateUniqueName(volumeNode.GetName() + ' acquisition transform')) slicer.mrmlScene.AddNode(gridTransform) gridTransform.SetAndObserveTransformToParent(transform) # populate the grid so that each corner of each slice # is mapped from the source corner to the target corner nshape = tuple(reversed(gridImage.GetDimensions())) nshape = nshape + (3,) displacements = vtk.util.numpy_support.vtk_to_numpy(gridImage.GetPointData().GetScalars()).reshape(nshape) # Get displacements from math import sin, cos ijkToRas = vtk.vtkMatrix4x4() volumeNode.GetIJKToRASMatrix(ijkToRas) spacing = volumeNode.GetSpacing() center_IJK = [(extent[0] + extent[1]) / 2.0, extent[2], (extent[4] + extent[5]) / 2.0] sourcePoints_RAS = numpy.zeros(shape=samplingPoints_shape) targetPoints_RAS = numpy.zeros(shape=samplingPoints_shape) for k in range(samplingPoints_shape[2]): for j in range(samplingPoints_shape[1]): for i in range(samplingPoints_shape[0]): samplingPoint_IJK = [gridAxesIJK[0][i], gridAxesIJK[1][j], gridAxesIJK[2][k], 1] sourcePoint_RAS = np.array(ijkToRas.MultiplyPoint(samplingPoint_IJK)[:3]) radius = probeRadius - (samplingPoint_IJK[1] - center_IJK[1]) * spacing[1] angleRad = (samplingPoint_IJK[0] - center_IJK[0]) * spacing[0] / probeRadius targetPoint_RAS = np.array([ -radius * sin(angleRad), radius * cos(angleRad) - probeRadius, spacing[2] * (samplingPoint_IJK[2] - center_IJK[2])]) displacements[k][j][i] = targetPoint_RAS - sourcePoint_RAS return gridTransform