def get_thin_plate_spline_deform(input_target_mesh, input_source_mesh): ## sparsen boundary points to speed up computation # clean_ratio = 0.1 # clean_data_polydata = vtk.vtkCleanPolyData() # clean_data_polydata.SetInputData(input_target_mesh) # clean_data_polydata.SetTolerance(clean_ratio) # clean_data_polydata.Update() # target_mesh = clean_data_polydata.GetOutput() # source_clean_data_polydata = vtk.vtkCleanPolyData() # source_clean_data_polydata.SetTolerance(clean_ratio) # source_clean_data_polydata.SetInputData(input_source_mesh) # source_clean_data_polydata.Update() # source_mesh = source_clean_data_polydata.GetOutput() target_mesh = input_target_mesh source_mesh = input_source_mesh # compute_distance_between_poly(target_mesh, source_mesh) source_pts = vtk.vtkPoints() target_pts = vtk.vtkPoints() for i in range(target_mesh.GetNumberOfPoints()): source_pts.InsertNextPoint(source_mesh.GetPoint(i)) target_pts.InsertNextPoint(target_mesh.GetPoint(i)) tps = vtk.vtkThinPlateSplineTransform() tps.SetSourceLandmarks(source_pts) tps.SetTargetLandmarks(target_pts) tps.SetBasisToR() tps.Modified() return tps
def __init__(self, sliceWidget): # keep a flag since events such as sliceNode modified # may come during superclass construction, which will # invoke our processEvents method self.initialized = False PointerEffectTool.__init__(self, sliceWidget) # interaction state variables self.activeSlice = None self.lastInsertSLiceNodeMTime = None self.actionState = None # initialization self.xyPoints = vtk.vtkPoints() self.rasPoints = vtk.vtkPoints() self.polyData = self.createPolyData() self.mapper = vtk.vtkPolyDataMapper2D() self.actor = vtk.vtkActor2D() self.mapper.SetInputData(self.polyData) self.actor.SetMapper(self.mapper) property_ = self.actor.GetProperty() property_.SetColor(1, 1, 0) property_.SetLineWidth(1) self.renderer.AddActor2D(self.actor) self.actors.append(self.actor) self.transform = vtk.vtkThinPlateSplineTransform() self.transform.SetBasisToR() self.transform.Inverse() self.auxNodes = [] self.initialized = True
def spline(source_mesh, source_landmarks, target_landmarks): """ Applies a thin plate spline transform to the source mesh “Principal warps: thin-plate splines and the decomposition of deformations” F. L. Bookstein :param source_mesh: vtkPolyData to be transformed :param source_landmarks: array of shape [n,3] :param target_landmarks: array of shape [n,3] :return: the transformed source mesh """ src = utils.np_to_vtkPoints(source_landmarks) tar = utils.np_to_vtkPoints(target_landmarks) spline = vtk.vtkThinPlateSplineTransform() spline.SetBasisToR() spline.SetTargetLandmarks(tar) spline.SetSourceLandmarks(src) spline.Update() transform = vtk.vtkTransformPolyDataFilter() transform.SetTransform(spline) transform.SetInputData(source_mesh) transform.Update() return transform.GetOutput()
def thinPlateSplineTransform(self, breastBoundPolyData,plane, modelNode): # Creates thin plate spline transform between target points # which are the breast boundary points (or code can be uncommented # to include more points) and the source points which are on # the plane of best fit # Target points using breast boundary points targetPoints = vtk.vtkPoints() for i in range(breastBoundPolyData.GetNumberOfPoints()): targetPoints.InsertNextPoint(breastBoundPolyData.GetPoint(i)) # Create source point set for thin plate spline by getting points on plane sourcePoints = vtk.vtkPoints() sourcePointsPolyData = vtk.vtkPolyData() NumberOfPoints = targetPoints.GetNumberOfPoints() for i in range(NumberOfPoints): p = targetPoints.GetPoint(i) pProj = [0, 0, 0] plane.ProjectPoint(p, pProj) sourcePoints.InsertNextPoint(pProj) sourcePointsPolyData.SetPoints(sourcePoints) # creates a thinPlate spline transform between the target and source points splineTransform = vtk.vtkThinPlateSplineTransform() splineTransform.SetSourceLandmarks(sourcePoints) splineTransform.SetTargetLandmarks(targetPoints) splineTransform.SetBasisToR() # Since our points are 3D return splineTransform
def compute_thin_plate_spline_transform(source_points, target_points): """Produce an output vtkThinPlateSplineTransform. Input is a vtkPoints object of source (moving) landmarks and a vtkPoints object of target (fixed) landmarks. """ vtktrans = vtk.vtkThinPlateSplineTransform() vtktrans.SetSourceLandmarks(source_points) vtktrans.SetTargetLandmarks(target_points) vtktrans.SetBasisToR() return vtktrans
def DeformSurface(self): # interpolate and sample the displacement norms over the surface rbf = vtkvmtkcontrib.vtkvmtkRBFInterpolation2() rbf.SetSource(self.SourceSpheres) rbf.SetRBFTypeToBiharmonic() rbf.ComputeCoefficients() sampler = vtkvmtkcontrib.vtkvmtkPolyDataSampleFunction() sampler.SetInput(self.Surface) sampler.SetImplicitFunction(rbf) sampler.SetSampleArrayName("DisplacementNorms") sampler.Update() sampArray = sampler.GetOutput().GetPointData().GetArray("DisplacementNorms") ##Clamp the negative values to 0 and the positive values to one in a weight array calculator = vtk.vtkArrayCalculator() calculator.SetInput(sampler.GetOutput()) calculator.AddScalarArrayName("DisplacementNorms") calculator.SetFunction("if( DisplacementNorms > 0 , iHat, jHat)") calculator.SetResultArrayName("Weights") calculator.SetResultArrayType(vtk.VTK_FLOAT) calculator.Update() # Create the transform thinPlateSplineTransform = vtk.vtkThinPlateSplineTransform() thinPlateSplineTransform.SetBasisToR() thinPlateSplineTransform.SetSourceLandmarks(self.SourcePoints) thinPlateSplineTransform.SetTargetLandmarks(self.TargetPoints) transform = vtk.vtkTransform() transform.Identity() transform2 = vtk.vtkTransform() transform2.Identity() # Apply weighted transform transformFilter = vtk.vtkWeightedTransformFilter() transformFilter.SetInput(calculator.GetOutput()) transformFilter.SetNumberOfTransforms(3) transformFilter.SetWeightArray("Weights") transformFilter.SetTransform(thinPlateSplineTransform, 0) transformFilter.SetTransform(transform, 1) transformFilter.SetTransform(transform2, 2) transformFilter.Update() normalsFilter = vtk.vtkPolyDataNormals() normalsFilter.SetInput(transformFilter.GetOutput()) normalsFilter.Update() # FIXME: the normal filter apparently introduced some holes in some meshes (wtf?). This filter cleans the mesh cleanFilter = vtk.vtkCleanPolyData() cleanFilter.SetInput(normalsFilter.GetOutput()) cleanFilter.Update() self.DeformedSurface = cleanFilter.GetOutput()
def warp(srcLandmark,dstLandmark,subj): tps = vtkThinPlateSplineTransform() tps.SetSourceLandmarks(srcLandmark.GetPoints()) tps.SetTargetLandmarks(dstLandmark.GetPoints()) tps.SetBasisToR() t1 = vtkGeneralTransform() t1.SetInput(tps) tf = vtkTransformPolyDataFilter() tf.SetInput(subj) tf.SetTransform(t1) tf.Update() warped = tf.GetOutput() return warped
def warp(srcLandmark, dstLandmark, subj): tps = vtkThinPlateSplineTransform() tps.SetSourceLandmarks(srcLandmark.GetPoints()) tps.SetTargetLandmarks(dstLandmark.GetPoints()) tps.SetBasisToR() t1 = vtkGeneralTransform() t1.SetInput(tps) tf = vtkTransformPolyDataFilter() tf.SetInput(subj) tf.SetTransform(t1) tf.Update() warped = tf.GetOutput() return warped
def computePreviewWarp(self, sourceFiducial, targetFiducial): # points sourcePoints = vtk.vtkPoints() targetPoints = vtk.vtkPoints() # add drawing sourceFiducial.GetControlPointPositionsWorld(sourcePoints) targetFiducial.GetControlPointPositionsWorld(targetPoints) # thin plate transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(sourcePoints) transform.SetTargetLandmarks(targetPoints) transform.SetBasisToR() transform.Inverse() transformNode = slicer.mrmlScene.AddNewNodeByClass( 'vtkMRMLTransformNode') transformNode.SetAndObserveTransformFromParent(transform) return transformNode
def applyTPSTransform(self, sourcePoints, targetPoints, modelNode, nodeName): transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(sourcePoints) transform.SetTargetLandmarks(targetPoints) transform.SetBasisToR() # for 3D transform transformFilter = vtk.vtkTransformPolyDataFilter() transformFilter.SetInputData(modelNode.GetPolyData()) transformFilter.SetTransform(transform) transformFilter.Update() warpedPolyData = transformFilter.GetOutput() warpedModelNode = slicer.mrmlScene.AddNewNodeByClass( 'vtkMRMLModelNode', nodeName) warpedModelNode.CreateDefaultDisplayNodes() warpedModelNode.SetAndObservePolyData(warpedPolyData) #self.RAS2LPSTransform(warpedModelNode) return warpedModelNode
def define_transform(source_landmarks, target_landmarks, volume_size=[1024, 1024, 1023]): """ Defines a non-linear warp between a set of source and target landmarks Parameters ========== source_landmarks - np.ndarray (N x 3) target_landmarks - np.ndarray (N x 3) volume_size - list of x, y, z max dimensions Returns ======= transform - vtkThinPlateSplineTransform """ transform = vtk.vtkThinPlateSplineTransform() source_points = vtk.vtkPoints() target_points = vtk.vtkPoints() for x in [0,volume_size[0]]: for y in [0,volume_size[1]]: for z in [0,volume_size[2]]: source_points.InsertNextPoint([z,x,y]) target_points.InsertNextPoint([z,x,y]) for i in range(source_landmarks.shape[0]): if source_landmarks[i,0] > -1 and target_landmarks[i,0] > -1: source_points.InsertNextPoint(source_landmarks[i,:]) for i in range(target_landmarks.shape[0]): if source_landmarks[i,0] > -1 and target_landmarks[i,0] > -1: target_points.InsertNextPoint(target_landmarks[i,:]) transform.SetBasisToR() # for 3D transform transform.SetSourceLandmarks(source_points) transform.SetTargetLandmarks(target_points) transform.Update() return transform
def performThinPlateRegistration(self, state, landmarks): """Perform the thin plate transform using the vtkThinPlateSplineTransform class""" volumeNodes = (state.fixed, state.moving) fiducialNodes = (state.fixedFiducials,state.movingFiducials) points = state.logic.vtkPointsForVolumes( volumeNodes, fiducialNodes ) # since this is a resample transform, source is the fixed (resampling target) space # and moving is the target space if not self.thinPlateTransform: self.thinPlateTransform = vtk.vtkThinPlateSplineTransform() self.thinPlateTransform.SetBasisToR() # for 3D transform self.thinPlateTransform.SetSourceLandmarks(points[state.moving]) self.thinPlateTransform.SetTargetLandmarks(points[state.fixed]) self.thinPlateTransform.Update() if points[state.moving].GetNumberOfPoints() != points[state.fixed].GetNumberOfPoints(): raise hell state.transform.SetAndObserveTransformToParent(self.thinPlateTransform)
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 SurfaceXRayRegistration(self, szeLMReader, wrlLMReader, szeReader): # Rotate surface because it is not aligned with landmarks self.AlignSurfaceLM(szeReader) # 1st register the topo to the xray using tps and external landmarks thin_plate_trans = vtk.vtkThinPlateSplineTransform() thin_plate_trans.SetSourceLandmarks(szeLMReader.points) thin_plate_trans.SetTargetLandmarks(wrlLMReader.capteurs_points) thin_plate_trans.SetBasisToR2LogR() thin_plate_trans.Update() # Apply transformation to landmarks self.ApplyTransform(szeLMReader.actor, thin_plate_trans) print( "-----Performance Metrics of Surface Topography to X-Ray using Thin Plate Spline Registration-----" ) self.getMetrics(szeLMReader, wrlLMReader, thin_plate_trans) # Apply transformation to Surface self.ApplyTransform(szeReader.actorCopy, thin_plate_trans) self.ApplyTransform(szeReader.actor, thin_plate_trans)
def performThinPlateRegistration(self, state, landmarks): """Perform the thin plate transform using the vtkThinPlateSplineTransform class""" volumeNodes = (state.fixed, state.moving) fiducialNodes = (state.fixedFiducials, state.movingFiducials) points = state.logic.vtkPointsForVolumes(volumeNodes, fiducialNodes) # since this is a resample transform, source is the fixed (resampling target) space # and moving is the target space if not self.thinPlateTransform: self.thinPlateTransform = vtk.vtkThinPlateSplineTransform() self.thinPlateTransform.SetBasisToR() # for 3D transform self.thinPlateTransform.SetSourceLandmarks(points[state.moving]) self.thinPlateTransform.SetTargetLandmarks(points[state.fixed]) self.thinPlateTransform.Update() if points[state.moving].GetNumberOfPoints() != points[ state.fixed].GetNumberOfPoints(): raise hell state.transform.SetAndObserveTransformToParent(self.thinPlateTransform)
def previewWarp(sourceNode, targetNode, outNode): # points sourcePoints = vtk.vtkPoints() sourceNode.GetControlPointPositionsWorld(sourcePoints) targetPoints = vtk.vtkPoints() targetNode.GetControlPointPositionsWorld(targetPoints) # thin plate transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(sourcePoints) transform.SetTargetLandmarks(targetPoints) transform.SetBasisToR() transform.Inverse() transformNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLTransformNode') transformNode.SetAndObserveTransformFromParent(transform) # display transformNode.CreateDefaultDisplayNodes() transformNode.GetDisplayNode().SetVisibility(1) transformNode.GetDisplayNode().SetVisibility3D(0) transformNode.GetDisplayNode().SetAndObserveGlyphPointsNode(sourceNode) transformNode.GetDisplayNode().SetVisibility2D(1) delay() # update display return transformNode
def __init__(self, sliceWidget): # keep a flag since events such as sliceNode modified # may come during superclass construction, which will # invoke our processEvents method self.initialized = False AbstractCircleEffect.__init__(self, sliceWidget) # interaction state variables self.actionState = None # initialization self.xyPoints = vtk.vtkPoints() self.rasPoints = vtk.vtkPoints() self.transform = vtk.vtkThinPlateSplineTransform() self.transform.SetBasisToR() self.transform.Inverse() self.auxNodes = [] self.initialized = True
def previewWarp(self, source, target): if isinstance(source, slicer.vtkMRMLMarkupsFiducialNode) and isinstance( target, slicer.vtkMRMLMarkupsFiducialNode): sourcePoints = vtk.vtkPoints() targetPoints = vtk.vtkPoints() for i in range(target.GetNumberOfControlPoints()): if target.GetNthControlPointSelected(i): sourcePoints.InsertNextPoint( source.GetNthControlPointPosition(i)) targetPoints.InsertNextPoint( target.GetNthControlPointPosition(i)) else: sourcePoints = source targetPoints = target sourceDisplayFiducial = slicer.mrmlScene.AddNewNodeByClass( 'vtkMRMLMarkupsFiducialNode') sourceDisplayFiducial.GetDisplayNode().SetVisibility(0) sourceDisplayFiducial.SetControlPointPositionsWorld(sourcePoints) # thin plate transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(sourcePoints) transform.SetTargetLandmarks(targetPoints) transform.SetBasisToR() transform.Inverse() transformNode = slicer.mrmlScene.AddNewNodeByClass( 'vtkMRMLTransformNode') transformNode.SetAndObserveTransformFromParent(transform) # display transformNode.CreateDefaultDisplayNodes() transformNode.GetDisplayNode().SetVisibility(1) transformNode.GetDisplayNode().SetVisibility3D(0) transformNode.GetDisplayNode().SetAndObserveGlyphPointsNode( sourceDisplayFiducial) transformNode.GetDisplayNode().SetVisibility2D(1) return transformNode, sourceDisplayFiducial
def warp(self, sourcePts=(), targetPts=(), transform=None, sigma=1, mirroring=False, bc='w', alpha=1): """ Warp an image using thin-plate splines. Parameters ---------- sourcePts : list, optional source points. targetPts : list, optional target points. transform : TYPE, optional a vtkTransform object can be supplied. The default is None. sigma : float, optional stiffness of the interpolation. The default is 1. mirroring : TYPE, optional fill the margins with a reflection of the original image. The default is False. bc : TYPE, optional fill the margins with a solid color. The default is 'w'. alpha : TYPE, optional opacity of the filled margins. The default is 1. """ if transform is None: # source and target must be filled transform = vtk.vtkThinPlateSplineTransform() transform.SetBasisToR2LogR() if isinstance(sourcePts, vedo.Points): sourcePts = sourcePts.points() if isinstance(targetPts, vedo.Points): targetPts = targetPts.points() ns = len(sourcePts) nt = len(targetPts) if ns != nt: colors.printc("Error in picture.warp(): #source != #target points", ns, nt, c='r') raise RuntimeError() ptsou = vtk.vtkPoints() ptsou.SetNumberOfPoints(ns) pttar = vtk.vtkPoints() pttar.SetNumberOfPoints(nt) for i in range(ns): p = sourcePts[i] ptsou.SetPoint(i, [p[0],p[1],0]) p = targetPts[i] pttar.SetPoint(i, [p[0],p[1],0]) transform.SetSigma(sigma) transform.SetSourceLandmarks(pttar) transform.SetTargetLandmarks(ptsou) else: # ignore source and target pass reslice = vtk.vtkImageReslice() reslice.SetInputData(self._data) reslice.SetOutputDimensionality(2) reslice.SetResliceTransform(transform) reslice.SetInterpolationModeToCubic() reslice.SetMirror(mirroring) c = np.array(colors.getColor(bc))*255 reslice.SetBackgroundColor([c[0],c[1],c[2], alpha*255]) reslice.Update() self.transform = transform return self._update(reslice.GetOutput())
def __init__(self): """ Called when the logic class is instantiated. Can be used for initializing member variables. """ ScriptedLoadableModuleLogic.__init__(self) self.inputCurveNode = None self.inputCurveNodeObservations = [] self.inputSurfacePointsNode = None self.inputSurfacePointsNodeObservations = [] self.numberOfCurveLandmarkPoints = 80 self.printThreeDViewNode = None self.printThreeDWidget = None self.printViewWidth = 1024 self.printViewHeight = 1024 self.printXResolutionDpi = 300 self.printYResolutionDpi = 300 self.printScale = 2.0 #TODO: Workaround for scaling problem, see https://github.com/SlicerFab/SlicerFab/issues/13 self.printTransparentBackground = False # Create triangulated flat disk that will be warped self.surfaceUnitDisk = vtk.vtkDiskSource() self.surfaceUnitDisk.SetOuterRadius(1.0) self.surfaceUnitDisk.SetInnerRadius(0.0) self.surfaceUnitDisk.SetCircumferentialResolution( self.numberOfCurveLandmarkPoints) self.surfaceUnitDisk.SetRadialResolution(60) self.surfaceTriangulator = vtk.vtkDelaunay2D() self.surfaceTriangulator.SetTolerance( 0.01 ) # get rid of the small triangles near the center of the unit disk self.surfaceTriangulator.SetInputConnection( self.surfaceUnitDisk.GetOutputPort()) # Prepare transform object # points on the unit disk (circumference and surface) self.surfaceTransformSourcePoints = vtk.vtkPoints() self.surfaceTransformSourceCurvePoints = vtk.vtkPoints() self.surfaceTransformSourceCurvePoints.SetNumberOfPoints( self.numberOfCurveLandmarkPoints) import math angleIncrement = 2.0 * math.pi / float( self.numberOfCurveLandmarkPoints) for pointIndex in range(self.numberOfCurveLandmarkPoints): angle = float(pointIndex) * angleIncrement self.surfaceTransformSourceCurvePoints.SetPoint( pointIndex, math.cos(angle), math.sin(angle), 0) # points on the warped surface (curve points and surface points) self.surfaceTransformTargetPoints = vtk.vtkPoints() self.surfaceTransform = vtk.vtkThinPlateSplineTransform() self.surfaceTransform.SetSourceLandmarks( self.surfaceTransformSourcePoints) self.surfaceTransform.SetTargetLandmarks( self.surfaceTransformTargetPoints) # Transform polydata self.surfaceTransformFilter = vtk.vtkTransformPolyDataFilter() self.surfaceTransformFilter.SetTransform(self.surfaceTransform) self.surfaceTransformFilter.SetInputConnection( self.surfaceTriangulator.GetOutputPort()) self.cleanPolyDataFilter = vtk.vtkCleanPolyData() self.cleanPolyDataFilter.SetInputConnection( self.surfaceTransformFilter.GetOutputPort()) # self.surfacePolyDataNormalsThin = vtk.vtkPolyDataNormals() self.surfacePolyDataNormalsThin.SetInputConnection( self.cleanPolyDataFilter.GetOutputPort()) # There are a few triangles in the triangulated unit disk with inconsistent # orientation. Enabling consistency check fixes them. self.surfacePolyDataNormalsThin.ConsistencyOn( ) # TODO: check if needed, probably not self.surfacePolyDataNormalsThin.SplittingOff( ) # this prevents stray normals at the edge TODO: check # Add thickness to warped surface (if needed) # self.surfacePolyDataNormals = vtk.vtkPolyDataNormals() # self.surfacePolyDataNormals.SetInputConnection(self.cleanPolyDataFilter.GetOutputPort()) # self.surfacePolyDataNormals.SplittingOff() # this prevents stray normals at the edge TODO: check self.surfaceOffset = vtk.vtkWarpVector() self.surfaceOffset.SetInputConnection( self.surfacePolyDataNormalsThin.GetOutputPort()) self.surfaceOffset.SetInputArrayToProcess( 0, 0, 0, vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS, vtk.vtkDataSetAttributes.NORMALS) self.surfaceExtrude = vtk.vtkLinearExtrusionFilter() self.surfaceExtrude.SetInputConnection( self.surfaceOffset.GetOutputPort()) self.surfaceExtrude.SetExtrusionTypeToNormalExtrusion() self.surfacePolyDataNormalsThick = vtk.vtkPolyDataNormals() self.surfacePolyDataNormalsThick.SetInputConnection( self.surfaceExtrude.GetOutputPort()) self.surfacePolyDataNormalsThick.AutoOrientNormalsOn()
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 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 volumetric_spline_augmentation(): out_name = 'C:/Users/lakri/Documents/BachelorProjekt/Spares MeshCNN/MeshCNN_sparse/datasets/DataAug/0079aug.vtk' file_name = 'C:/Users/lakri/Documents/BachelorProjekt/Spares MeshCNN/MeshCNN_sparse/datasets/LAA_segmentation/0079.vtk' out_name_grid = 'C:/Users/lakri/Documents/BachelorProjekt/Spares MeshCNN/MeshCNN_sparse/datasets/DataAug/0079_grid.vtk' out_name_grid_warped = 'C:/Users/lakri/Documents/BachelorProjekt/Spares MeshCNN/MeshCNN_sparse/datasets/DataAug/0079_grid_warped.vtk' pd_in = vtk.vtkPolyDataReader() pd_in.SetFileName(file_name) pd_in.Update() pd = pd_in.GetOutput() grid_3d = create_3d_grid(pd) print('writing grid') writer = vtk.vtkPolyDataWriter() writer.SetFileName(out_name_grid) writer.SetInputData(grid_3d) writer.Write() print('done writing') # Get bounding box bounds = pd.GetBounds() x_min = bounds[0] x_max = bounds[1] y_min = bounds[2] y_max = bounds[3] z_min = bounds[4] z_max = bounds[5] # get a measure of scale as the diagonal length scale = math.sqrt((x_max - x_min) * (x_max - x_min) + (y_max - y_min) * (y_max - y_min) + (z_max - z_min) * (z_max - z_min)) # Reference points # Here just corners of the bounding box # TODO: make more points n = 8 i = 0 p1 = vtk.vtkPoints() p1.SetNumberOfPoints(n * n * n) xlin = np.linspace(x_min, x_max, n) ylin = np.linspace(y_min, y_max, n) zlin = np.linspace(z_min, z_max, n) for x in xlin: for y in ylin: for z in zlin: p1.SetPoint(i, x, y, z) i += 1 ''' p1.SetPoint(0, x_min, y_min, z_min) p1.SetPoint(1, x_max, y_min, z_min) p1.SetPoint(2, x_min, y_max, z_min) p1.SetPoint(3, x_max, y_max, z_min) p1.SetPoint(4, x_min, y_min, z_max) p1.SetPoint(5, x_max, y_min, z_max) p1.SetPoint(6, x_min, y_max, z_max) p1.SetPoint(7, x_max, y_max, z_max) ''' # Deformed points p2 = vtk.vtkPoints() # Start by copying all info from p1 p2.DeepCopy(p1) # Displace the points in a random direction displacement_length = scale * 0.03 #change parameter around a bit for i in range(p2.GetNumberOfPoints()): p = list(p2.GetPoint(i)) for j in range(3): p[j] = p[j] + (2.0 * random() - 1) * displacement_length p2.SetPoint(i, p) transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(p1) transform.SetTargetLandmarks(p2) transform.SetBasisToR2LogR() transform.Update() transform_filter = vtk.vtkTransformPolyDataFilter() transform_filter.SetTransform(transform) transform_filter.SetInputData(pd) transform_filter.Update() vs = np.array(transform_filter.GetOutput().GetPoints().GetData()) face_labels = np.array( transform_filter.GetOutput().GetCellData().GetScalars()) poly = dsa.WrapDataObject(transform_filter.GetOutput()).Polygons faces = np.reshape(poly, (-1, 4))[:, 1:4] #vcolor = [] print('writing aug mesh') with open(out_name, 'w+') as f: f.write( "# vtk DataFile Version 4.2 \nvtk output \nASCII \n \nDATASET POLYDATA \nPOINTS %d float \n" % len(vs)) for vi, v in enumerate(vs): #vcol = ' %f %f %f' % (vcolor[vi, 0], vcolor[vi, 1], vcolor[vi, 2]) if vcolor is not None else '' f.write("%f %f %f\n" % (v[0], v[1], v[2])) f.write("POLYGONS %d %d \n" % (len(faces), 4 * len(faces))) for face_id in range(len(faces) - 1): f.write("3 %d %d %d\n" % (faces[face_id][0], faces[face_id][1], faces[face_id][2])) f.write("3 %d %d %d" % (faces[-1][0], faces[-1][1], faces[-1][2])) f.write( "\n \nCELL_DATA %d \nSCALARS scalars double \nLOOKUP_TABLE default" % len(faces)) for j, face in enumerate(faces): if j % 9 == 0: f.write("\n") f.write("%d " % face_labels[j]) print('done writing') transform_filter_grid = vtk.vtkTransformPolyDataFilter() transform_filter_grid.SetTransform(transform) transform_filter_grid.SetInputData(grid_3d) transform_filter_grid.Update() #grid_points = np.array( transform_filter.GetOutput().GetPoints().GetData() ) #grid_lines = np.array( transform_filter.GetOutput().GetLines().GetData() ) #poly = dsa.WrapDataObject(transform_filter.GetOutput()).Polygons #faces = np.reshape(poly,(-1,4))[:,1:4] #print('writing warped grid') #with open(out_name, 'w+') as f: #f.write("# vtk DataFile Version 4.2 \nvtk output \nASCII \n \nDATASET POLYDATA \nPOINTS %d float \n" % len(grid_points)) #for vi, v in enumerate(grid_points): #vcol = ' %f %f %f' % (vcolor[vi, 0], vcolor[vi, 1], vcolor[vi, 2]) if vcolor is not None else '' #f.write("%f %f %f\n" % (v[0], v[1], v[2])) #f.write("LINES %d %d \n" % (len(grid_lines),2*len(grid_lines)-1)) #for lines_id in range(len(grid_lines) - 1): #f.write("3 %d %d %d\n" % (grid_lines[face_id][0] , faces[face_id][1], faces[face_id][2])) #f.write("3 %d %d %d" % (faces[-1][0], faces[-1][1], faces[-1][2])) #f.write("\n \nCELL_DATA %d \nSCALARS scalars double \nLOOKUP_TABLE default" % len(faces)) #for j,face in enumerate(faces): #if j%9 == 0: #f.write("\n") #f.write("%d " % face_labels[j]) #print('done writing') filt = vtk.vtkConnectivityFilter() filt.SetInputData(grid_3d) # get the data from the MC alg. #filt.ColorRegionsOn() filt.Update() ren = vtk.vtkRenderer() renWin = vtk.vtkRenderWindow() renWin.AddRenderer(ren) WIDTH = 640 HEIGHT = 480 renWin.SetSize(WIDTH, HEIGHT) #create a renderwindowinteractor iren = vtk.vtkRenderWindowInteractor() iren.SetRenderWindow(renWin) # mapper dataMapper1 = vtk.vtkPolyDataMapper() dataMapper2 = vtk.vtkPolyDataMapper() #dataMapper1.AddInputConnection(pd_in.GetOutputPort()) #dataMapper2.AddInputConnection(filt.GetOutputPort()) dataMapper1.AddInputConnection(transform_filter.GetOutputPort()) dataMapper2.AddInputConnection(transform_filter_grid.GetOutputPort()) #vtkPolyDataMapper = dataMapper.SetInputConnection(0,transform_filter.GetOutputPort()) #dataMapper.SetInputConnection(0,transform_filter.GetOutputPort()) #dataMapper.SetInputConnection(1,transform_filter_grid.GetOutputPort()) # actor dataActor1 = vtk.vtkActor() dataActor2 = vtk.vtkActor() dataActor1.SetMapper(dataMapper1) dataActor2.SetMapper(dataMapper2) # assign actor to the renderer ren.AddActor(dataActor1) ren.AddActor(dataActor2) # enable user interface interactor iren.Initialize() renWin.Render() iren.Start()
def inv_curv(b_pointsU, b_pointsD, sPoints, diffeoX, diffeoY, rotPtX, rotPtY, interSkelX, interSkelY): invPtsU = b_pointsU.copy() invPtsD = b_pointsD.copy() skelPt = sPoints.copy() # looping through all the iterations of the curvature flow (we are doing the inverse of the flow) for j in range(0, len(diffeoX[0])): movingX = [] movingY = [] source_pts = vtk.vtkPoints() target_pts = vtk.vtkPoints() for i in range(0, len(rotPtX)): pt = [] if j == 0: pt = [rotPtX[i], rotPtY[i], 0] else: pt = [diffeoX[i][-1 * j], diffeoY[i][-1 * j], 0] # source pts for TPS starts off as best fit ellipse # next iteration it will be the last diffeomorphism # keep going until it is the second diffeomorphism source_pts.InsertNextPoint(pt) pt = [diffeoX[i][-1 - j], diffeoY[i][-1 - j], 0] movingX.append(diffeoX[i][-1 - j]) movingY.append(diffeoY[i][-1 - j]) # target points is the diffeomorphism before the diffeomorphism of the source points # starts as last and goes to first target_pts.InsertNextPoint(pt) source_pts.Modified() target_pts.Modified() ### Interpolate deformation with thin-plate-spline tps = vtk.vtkThinPlateSplineTransform() tps.SetSourceLandmarks(source_pts) tps.SetTargetLandmarks(target_pts) tps.SetBasisToR() tps.Modified() sXs = [] sYs = [] # finding interpolated skel points for i in range(0, len(interSkelX)): intSkelPt = tps.TransformPoint([interSkelX[i], interSkelY[i], 0]) interSkelX[i] = intSkelPt[0] interSkelY[i] = intSkelPt[1] for i in range(0, len(sPoints)): sp = [skelPt[i][0], skelPt[i][1], 0] up = [invPtsU[i][0], invPtsU[i][1], 0] if i != 0 and i != len(sPoints) - 1: down = [invPtsD[i - 1][0], invPtsD[i - 1][1], 0] # finding new skeletal points sk_pt = tps.TransformPoint(sp) skelPt[i][0] = sk_pt[0] skelPt[i][1] = sk_pt[1] sXs.append(sk_pt[0]) sYs.append(sk_pt[1]) # finding new boundary points for the up spokes newUp = tps.TransformPoint(up) invPtsU[i][0] = newUp[0] invPtsU[i][1] = newUp[1] # new boundary points of bottom spokes if i != 0 and i != len(sPoints) - 1: newDown = tps.TransformPoint(down) invPtsD[i - 1][0] = newDown[0] invPtsD[i - 1][1] = newDown[1] # plotting the final spokes if j == len(diffeoX[0]) - 1: xUp = [sk_pt[0], newUp[0]] yUp = [sk_pt[1], newUp[1]] # plt.plot(xUp, yUp,'k') if i != 0 and i != len(sPoints) - 1: xDown = [sk_pt[0], newDown[0]] yDown = [sk_pt[1], newDown[1]] # plt.plot(xDown, yDown,'k') movingX.append(movingX[0]) movingY.append(movingY[0]) # plt.plot(interSkelX, interSkelY,'r') # plt.plot(movingX, movingY, 'b') # plt.show() return sXs, sYs, invPtsU, invPtsD
def createMorph(selectedImages,selectedPMs): """ Translates and rotate the bitmaps based on the shapedefining landmarks (selectedPMs) of the associated image to the target image (first image). Morphs the result so that the bitmap overlay on the first image is valid for the first image. """ #save the temporary results here later: os_temp_path = tempfile.gettempdir() #get the measurements for the first image (our targets) mainImage = OriginalImage.objects.all().get(id=selectedImages[0]) potentialids = [] #now get the associated measurements measures = Measurement.objects.all().filter(id__in=selectedPMs[0]).filter(mogelijkemeting__shapedefining=True) measures = [j for j in measures] measures.sort(key=lambda x: x.mogelijkemeting.name) coordsx = [] coordsy = [] for k, measurement in enumerate(measures): coordsx.append(float(measurement.x)) coordsy.append(float(measurement.y)) potentialids.append(measurement.mogelijkemeting.id) r1 = vtk.vtkJPEGReader() r1.SetFileName(settings.DATADIR + mainImage.id + ".jpg") r1.Update() # flip y coord (VTK has opposite convention), create 3-d coords (z=0) ydim = r1.GetOutput().GetDimensions()[1] coords = [(x, ydim - y, 0) for (x,y) in zip(coordsx, coordsy)] # convert everything to vtkPoints lmt = vtk.vtkPoints() lmt.SetNumberOfPoints(len(coords)) for i, coord in enumerate(coords): lmt.SetPoint(i,coord) #The target is clear, let's get to work, get the source images... images = [] #we don't need the first image or its measures anymore, because they don't need to be transformed or morphed selectedImages.pop(0) selectedPMs.pop(0) for id in selectedImages: images.append(OriginalImage.objects.all().get(id=id)) transformations = [] morphtransformations = [] #Create a new database object for the target image to associate the bitmaps with img = OriginalImage(project=mainImage.project, name='MorphedImage') img.save() imp = Image.open(settings.DATADIR + mainImage.id + '.jpg') imp.save(settings.DATADIR + img.id + '.jpg', 'JPEG') orig_bitmaps = Bitmap.objects.all().filter(image=mainImage) for bm in orig_bitmaps: #store bitmaps of mainImage as sub of img bitmap = Bitmap(project=img.project, name='warpedbitmap', image=img, mogelijkemeting=bm.mogelijkemeting, imagewidth=bm.imagewidth, imageheight=bm.imageheight, minx=bm.minx, miny=bm.miny, maxx=bm.maxx, maxy=bm.maxy) bitmap.save() bitmap_image = Image.open(settings.DATADIR + bm.id + '.gif') bitmap_image = bitmap_image.convert("RGBA") bitmap_image.save(settings.DATADIR + bitmap.id + '.gif', transparency=0) #now get the other images and perform our transformations for i in range(len(images)): measures = Measurement.objects.all().filter(id__in=selectedPMs[i]).filter(mogelijkemeting__shapedefining=True)#get measurements measures = [j for j in measures] measures.sort(key=lambda x: x.mogelijkemeting.name) coordsx = [] coordsy = [] for k, measurement in enumerate(measures): coordsx.append(float(measurement.x)) coordsy.append(float(measurement.y)) if potentialids[k] != measurement.mogelijkemeting.id: #the potentialmeasurements do not match up to the ones in the target image return img, 0 r = vtk.vtkJPEGReader() r.SetFileName(settings.DATADIR + images[i].id + ".jpg") r.Update() ydim = r.GetOutput().GetDimensions()[1] coordso = [(x, ydim - y, 0) for (x,y) in zip(coordsx, coordsy)] lms = vtk.vtkPoints() lms.SetNumberOfPoints(len(coordso)) for k, coord in enumerate(coordso): lms.SetPoint(k,coord) transformation = vtk.vtkLandmarkTransform() transformation.SetTargetLandmarks(lmt) lmt.Modified() transformation.SetSourceLandmarks(lms) lms.Modified() #size matters, so set the mode to Rigid Body (also known as do not scale please) transformation.SetModeToRigidBody() transformation.Inverse() transformation.Update() out = vtk.vtkPoints()#this will be the source of our morph transform transformation.TransformPoints(lms,out) transformations.append(transformation) ir = vtk.vtkImageReslice() # we're not using linear, because we want to improve the quality of the bitmaps ir.SetInterpolationModeToNearestNeighbor() ir.SetResliceTransform(transformation) ir.SetInput(r.GetOutput()) ir.SetInformationInput(r1.GetOutput()) w = vtk.vtkJPEGWriter() w.SetFileName(os_temp_path+'/translated'+images[i].id+'.jpg') w.SetInput(ir.GetOutput()) w.Write() r2 = vtk.vtkJPEGReader() r2.SetFileName(os_temp_path+'/translated'+images[i].id+'.jpg') r2.Update() # the mighty morphing ThinPlateSplineTransform morphtransform = vtk.vtkThinPlateSplineTransform() morphtransform.SetBasisToR2LogR() morphtransform.SetSourceLandmarks(lms) lms.Modified() morphtransform.SetTargetLandmarks(lmt) lmt.Modified() morphtransform.Inverse() morphtransform.Update() morphtransformations.append(morphtransform) #ir.SetInput(r2.GetOutput()) #ir.SetInformationInput(r1.GetOutput()) bitmaps = Bitmap.objects.all().filter(image=images[i]) #now perform the total transformation on all bitmaps for bm in bitmaps: location = settings.DATADIR + bm.id + ".gif" im = Image.open(location) im = im.convert("RGBA") im.save(settings.DATADIR + bm.id + ".png", "PNG") r3 = vtk.vtkPNGReader() r3.SetFileName(settings.DATADIR + bm.id + '.png') r3.Update() ir2 = vtk.vtkImageReslice() ir2.SetInterpolationModeToNearestNeighbor() ir2.SetResliceTransform(morphtransform) ir2.SetInput(r3.GetOutput()) ir2.SetInformationInput(r2.GetOutput()) w3 = vtk.vtkPNGWriter() w3.SetFileName(os_temp_path+'/morphed'+bm.id+'.png') w3.SetInput(ir2.GetOutput()) w3.Write() bitmap = Bitmap(project=img.project, name='warpedbitmap', image=img, mogelijkemeting=bm.mogelijkemeting, imagewidth=bm.imagewidth, imageheight=bm.imageheight, minx=bm.minx, miny=bm.miny, maxx=bm.maxx, maxy=bm.maxy) bitmap.save() im = Image.open(os_temp_path+'/morphed'+bm.id+'.png') im = im.convert("RGBA") im.save(settings.DATADIR + bitmap.id + '.gif', transparency=0) return img, 1
def register(self, fixedData, movingData, index = -1, discard = False, delta = 0, fov = 9999999.0, down_fix = 1, down_mov = 1, occ = 9999999.0, op = False, useMask = False, isTime = False, MaxRate = 0.2, aug = False, distance_fix = 0.3, distance_mov = 0.1, w_wrong = 1.5, truth_mov = None): time1 = time.time() if index == -1: index = self.gui.getDataIndex({'Contour': 0, 'Centerline': 1}, 'Select the object') if index is None: return None, None, None if index == 0: fixed_points = fixedData.getPointSet('Contour').copy() moving_points = movingData.getPointSet('Contour').copy() else: fixed_points = fixedData.getPointSet('Centerline').copy() moving_points = movingData.getPointSet('Centerline').copy() if truth_mov is None: truth_mov = moving_points.copy() fixed_bif = db.getBifurcation(fixed_points) moving_bif = db.getBifurcation(moving_points) if useMask: mask_points = movingData.getPointSet('Mask') for point in mask_points: moving_points = npy.delete(moving_points, npy.where((npy.abs(moving_points[:, 2] - point[2]) < 0.0001) & (npy.round(moving_points[:, -1]) == point[3])), axis = 0) fixed_res = fixedData.getResolution().tolist() moving_res = movingData.getResolution().tolist() fixed_points = fixed_points[npy.where(fixed_points[:, 0] >= 0)] moving_points = moving_points[npy.where(moving_points[:, 0] >= 0)] # Use the bifurcation as the initial position if (fixed_bif < 0) or (moving_bif < 0): fixed_min = 0 # Augmentation of pointset fixed = fixed_points.copy() moving = moving_points.copy() if index == 1 and aug: fixed = util.augmentCenterline(fixed, 1, 10) moving = util.augmentCenterline(moving, 1, 10) fix_dis = util.getAxisSin(fixed, 3 / fixed_res[2]) * distance_fix mov_dis = util.getAxisSin(moving, 3 / moving_res[2]) * distance_mov fixed = util.resampleCenterline(fixed, fix_dis / fixed_res[2]) moving = util.resampleCenterline(moving, mov_dis / moving_res[2]) fixed = fixed[npy.cast[npy.int32](npy.abs(fixed[:, 2] - fixed_bif)) % down_fix == 0] moving = moving[npy.cast[npy.int32](npy.abs(moving[:, 2] - moving_bif)) % down_mov == 0] fixed[:, :3] *= fixed_res[:3] moving[:, :3] *= moving_res[:3] new_trans_points = truth_mov result_center_points = movingData.getPointSet('Centerline').copy() new_trans_points = new_trans_points[new_trans_points[:, 3] >= 0] result_center_points = result_center_points[result_center_points[:, 3] >= 0] new_trans_points[:, :3] *= moving_res[:3] result_center_points[:, :3] *= moving_res[:3] if (fixed_bif >= 0) and (moving_bif >= 0): fixed[:, 2] -= (fixed_bif * fixed_res[2] - moving_bif * moving_res[2] + delta) # Prepare for ICP MaxIterNum = 50 #MaxNum = 600 MaxNum = int(MaxRate * moving.shape[0] + 0.5) targetPoints = [vtk.vtkPoints(), vtk.vtkPoints(), vtk.vtkPoints()] targetVertices = [vtk.vtkCellArray(), vtk.vtkCellArray(), vtk.vtkCellArray()] target = [vtk.vtkPolyData(), vtk.vtkPolyData(), vtk.vtkPolyData()] Locator = [vtk.vtkCellLocator(), vtk.vtkCellLocator(), vtk.vtkCellLocator()] for i in range(3): for x in fixed[npy.round(fixed[:, 3]) == i]: id = targetPoints[i].InsertNextPoint(x[0], x[1], x[2]) targetVertices[i].InsertNextCell(1) targetVertices[i].InsertCellPoint(id) target[i].SetPoints(targetPoints[i]) target[i].SetVerts(targetVertices[i]) Locator[i].SetDataSet(target[i]) Locator[i].SetNumberOfCellsPerBucket(1) Locator[i].BuildLocator() step = 1 if moving.shape[0] > MaxNum: ind = moving[:, 2].argsort() moving = moving[ind, :] step = moving.shape[0] / MaxNum nb_points = moving.shape[0] / step points1 = vtk.vtkPoints() points1.SetNumberOfPoints(nb_points) label = npy.zeros([MaxNum * 2], dtype = npy.int8) j = 0 for i in range(nb_points): points1.SetPoint(i, moving[j][0], moving[j][1], moving[j][2]) label[i] = moving[j][3] j += step closestp = vtk.vtkPoints() closestp.SetNumberOfPoints(nb_points) points2 = vtk.vtkPoints() points2.SetNumberOfPoints(nb_points) id1 = id2 = vtk.mutable(0) dist = vtk.mutable(0.0) outPoint = [0.0, 0.0, 0.0] p1 = [0.0, 0.0, 0.0] p2 = [0.0, 0.0, 0.0] iternum = 0 a = points1 b = points2 if (op and index == 0) or (not op and index == 1): w_mat = [[1, w_wrong, w_wrong], [w_wrong, 1, 99999999], [w_wrong, 99999999, 1]] else: w_mat = [[1, 1, 1], [1, 1, 1], [1, 1, 1]] accumulate = vtk.vtkTransform() accumulate.PostMultiply() LandmarkTransform = vtk.vtkLandmarkTransform() LandmarkTransform.SetModeToRigidBody() while True: for i in range(nb_points): min_dist = 99999999 min_outPoint = [0.0, 0.0, 0.0] for j in range(3): Locator[j].FindClosestPoint(a.GetPoint(i), outPoint, id1, id2, dist) dis = npy.sqrt(npy.sum((npy.array(outPoint) - a.GetPoint(i)) ** 2)) if dis * w_mat[label[i]][j] < min_dist: min_dist = dis * w_mat[label[i]][j] min_outPoint = copy.deepcopy(outPoint) closestp.SetPoint(i, min_outPoint) LandmarkTransform.SetSourceLandmarks(a) LandmarkTransform.SetTargetLandmarks(closestp) LandmarkTransform.Update() accumulate.Concatenate(LandmarkTransform.GetMatrix()) iternum += 1 for i in range(nb_points): a.GetPoint(i, p1) LandmarkTransform.InternalTransformPoint(p1, p2) b.SetPoint(i, p2) b, a = a, b if iternum >= MaxIterNum: break matrix = accumulate.GetMatrix() T = ml.mat([matrix.GetElement(0, 3), matrix.GetElement(1, 3), matrix.GetElement(2, 3)]).T R = ml.mat([[matrix.GetElement(0, 0), matrix.GetElement(0, 1), matrix.GetElement(0, 2)], [matrix.GetElement(1, 0), matrix.GetElement(1, 1), matrix.GetElement(1, 2)], [matrix.GetElement(2, 0), matrix.GetElement(2, 1), matrix.GetElement(2, 2)]]).I result_center_points[:, :3] = util.applyTransformForPoints(result_center_points[:, :3], npy.array([1.0, 1, 1]), npy.array([1.0, 1, 1]), R, T, ml.zeros([3, 1], dtype = npy.float32)) new_trans_points[:, :3] = util.applyTransformForPoints(new_trans_points[:, :3], npy.array([1.0, 1, 1]), npy.array([1.0, 1, 1]), R, T, ml.zeros([3, 1], dtype = npy.float32)) LandmarkTransform = vtk.vtkThinPlateSplineTransform() LandmarkTransform.SetBasisToR() iternum = 0 # Non-rigid while True: for i in range(nb_points): min_dist = 99999999 min_outPoint = [0.0, 0.0, 0.0] for j in range(3): Locator[j].FindClosestPoint(a.GetPoint(i), outPoint, id1, id2, dist) dis = npy.sqrt(npy.sum((npy.array(outPoint) - a.GetPoint(i)) ** 2)) if dis * w_mat[label[i]][j] < min_dist: min_dist = dis * w_mat[label[i]][j] min_outPoint = copy.deepcopy(outPoint) closestp.SetPoint(i, min_outPoint) LandmarkTransform.SetSourceLandmarks(a) LandmarkTransform.SetTargetLandmarks(closestp) LandmarkTransform.Update() ''' for i in range(result_center_points.shape[0]): LandmarkTransform.InternalTransformPoint([result_center_points[i, 0], result_center_points[i, 1], result_center_points[i, 2]], p2) result_center_points[i, :3] = p2 ''' for i in range(new_trans_points.shape[0]): LandmarkTransform.InternalTransformPoint([new_trans_points[i, 0], new_trans_points[i, 1], new_trans_points[i, 2]], p2) new_trans_points[i, :3] = p2 iternum += 1 if iternum >= 1: break for i in range(nb_points): a.GetPoint(i, p1) LandmarkTransform.InternalTransformPoint(p1, p2) b.SetPoint(i, p2) b, a = a, b time2 = time.time() if (fixed_bif >= 0) and (moving_bif >= 0): new_trans_points[:, 2] += (fixed_bif * fixed_res[2] - moving_bif * moving_res[2] + delta) result_center_points[:, 2] += (fixed_bif * fixed_res[2] - moving_bif * moving_res[2] + delta) new_trans_points[:, :3] /= fixed_res[:3] result_center_points[:, :3] /= fixed_res[:3] resultImage = movingData.getData().copy() sa = SurfaceErrorAnalysis(None) dataset = db.BasicData(npy.array([[[0]]]), fixedData.getInfo(), {'Contour': new_trans_points, 'Centerline': result_center_points}) mean_dis, mean_whole, max_dis, max_whole = sa.analysis(dataset, point_data_fix = fixedData.getPointSet('Contour').copy(), useResult = True) del dataset print mean_dis print mean_whole if isTime: return resultImage, {'Contour': new_trans_points, 'Centerline': result_center_points}, [mean_dis, mean_whole], time2 - time1 return resultImage, {'Contour': new_trans_points, 'Centerline': result_center_points}, [mean_dis, mean_whole]
def performAffineAndThinPlateRegistration(self, state, landmarks): """Perform Affine registration first, use the transformed result as the input of the thin plate transform""" # if state.transformed: # if state.transformed.GetTransformNodeID() != state.transform.GetID(): # state.transformed.SetAndObserveTransformNodeID(state.transform.GetID()) volumeNodes = (state.fixed, state.moving) fiducialNodes = (state.fixedFiducials,state.movingFiducials) points = state.logic.vtkPointsForVolumes( volumeNodes, fiducialNodes ) #yingli debug #print 'self.linearMode',self.linearMode if not self.landmarkTransform: self.landmarkTransform = vtk.vtkLandmarkTransform() if self.linearMode == 'Rigid': self.landmarkTransform.SetModeToRigidBody() if self.linearMode == 'Similarity': self.landmarkTransform.SetModeToSimilarity() if self.linearMode == 'Affine': self.landmarkTransform.SetModeToAffine() if state.fixedFiducials.GetNumberOfFiducials() < 3: self.landmarkTransform.SetModeToRigidBody() self.landmarkTransform.SetSourceLandmarks(points[state.moving]) self.landmarkTransform.SetTargetLandmarks(points[state.fixed]) self.landmarkTransform.Update() #transform moving landmarks affine_transformed_moving_points = vtk.vtkPoints() self.landmarkTransform.TransformPoints(points[state.moving],affine_transformed_moving_points) #yingli debug #print self.landmarkTransform.GetMatrix() #print 'old moving', points[state.moving].GetPoint(0) #print 'new moving', affine_transformed_moving_points.GetPoint(0) # do thin plate, use affine transformed result as the input # since this is a resample transform, source is the fixed (resampling target) space # and moving is the target space if not self.thinPlateTransform: self.thinPlateTransform = vtk.vtkThinPlateSplineTransform() self.thinPlateTransform.SetBasisToR() # for 3D transform self.thinPlateTransform.SetSourceLandmarks(affine_transformed_moving_points) self.thinPlateTransform.SetTargetLandmarks(points[state.fixed]) self.thinPlateTransform.Update() if points[state.moving].GetNumberOfPoints() != points[state.fixed].GetNumberOfPoints(): raise hell #add nesting transfrom: order matters! transformSelector = slicer.qMRMLNodeComboBox() transformSelector.nodeTypes = ( ("vtkMRMLTransformNode"), "" ) transformSelector.selectNodeUponCreation = True transformSelector.addEnabled = True transformSelector.removeEnabled = True transformSelector.noneEnabled = True transformSelector.showHidden = False transformSelector.showChildNodeTypes = False transformSelector.setMRMLScene( slicer.mrmlScene ) transformSelector.setToolTip( "The transform for linear registration" ) transformSelector.enabled = False # concatenate transfroms: method 1: add nesting transfrom: order matters! # note: this method will keep each transform(not ideal) # landmarkTransformNode = slicer.util.getNode('Affine-Transform') # if not landmarkTransformNode: # landmarkTransformNode=transformSelector.addNode() # landmarkTransformNode.SetName('Affine-Transform') # state.transform.SetAndObserveTransformNodeID(landmarkTransformNode.GetID()) # landmarkTransformNode.ApplyTransform(self.landmarkTransform) # thinPlateTransformNode = slicer.util.getNode('ThinPlate-Transform') # if not thinPlateTransformNode: # thinPlateTransformNode=transformSelector.addNode() # thinPlateTransformNode.SetName('ThinPlate-Transform') # landmarkTransformNode.SetAndObserveTransformNodeID(thinPlateTransformNode.GetID()) # # thinPlateTransformNode.ApplyTransform(self.thinPlateTransform) #state.transform.SetAndObserveTransformToParent(self.landmarkTransform) # #state.transform.SetAndObserveTransformToParent(self.thinPlateTransform) # stateTransformNodeNode = slicer.util.getNode('Transform') # stateTransformNodeNode.SetAndObserveTransformToParent(self.thinPlateTransform) #test vtk concatenate #self.landmarkTransform.Concatenate(self.landmarkTransform) #state.transform.SetAndObserveTransformToParent(self.thinPlateTransform) #concatenate transfroms: method 2: use vtkGeneralTransform to concatenate transfroms. transform = vtk.vtkGeneralTransform() transform.Concatenate(self.thinPlateTransform) transform.Concatenate(self.landmarkTransform) state.transform.SetAndObserveTransformToParent(transform)
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 write_transforms_to_itk_format(transform_list, outdir, subject_ids=None): """Write VTK affine or spline transforms to ITK 4 text file formats. Input transforms are in VTK RAS space and are forward transforms. Output transforms are in LPS space and are the corresponsing inverse transforms, according to the conventions for these file formats and for resampling images. The affine transform is straightforward. The spline transform file format is just a list of displacements that have to be in the same order as they are stored in ITK C code. This now outputs an ITK transform that works correctly to transform the tracts (or any volume in the same space) in Slicer. In the nonrigid case, we also output a vtk native spline transform file using MNI format. """ idx = 0 tx_fnames = list() for tx in transform_list: # save out the vtk transform to a text file as it is # The MNI transform reader/writer are available in vtk so use those: if tx.GetClassName() != 'vtkBSplineTransform': writer = vtk.vtkMNITransformWriter() writer.AddTransform(tx) if subject_ids is not None: fname = 'vtk_txform_' + str(subject_ids[idx]) + '.xfm' else: fname = 'vtk_txform_{0:05d}.xfm'.format(idx) writer.SetFileName(os.path.join(outdir, fname)) writer.Write() # file name for itk transform written below if subject_ids is not None: fname = 'itk_txform_' + str(subject_ids[idx]) + '.tfm' else: fname = 'itk_txform_{0:05d}.tfm'.format(idx) fname = os.path.join(outdir, fname) tx_fnames.append(fname) # Save the itk transform as the inverse of this transform (resampling transform) and in LPS. # This will show the same transform in the slicer GUI as the vtk transform we internally computed # that is stored in the .xfm text file, above. # To apply our transform to resample a volume in LPS: # convert to RAS, use inverse of transform to resample, convert back to LPS if tx.GetClassName() == 'vtkThinPlateSplineTransform' or tx.GetClassName() == 'vtkBSplineTransform': #print 'Saving nonrigid transform displacements in ITK format' # Deep copy to avoid modifying input transform that will be applied to polydata if tx.GetClassName() == 'vtkThinPlateSplineTransform': tps = vtk.vtkThinPlateSplineTransform() else: tps = vtk.vtkBSplineTransform() tps.DeepCopy(tx) #extent = tps.GetCoefficients().GetExtent() #origin = tps.GetCoefficients().GetOrigin() #spacing = tps.GetCoefficients().GetSpacing() #dims = tps.GetCoefficients().GetDimensions() #print "E:", extent #print "O:", origin #print "S:", spacing #print "D:", dims # invert to get the transform suitable for resampling an image tps.Inverse() # convert the inverse spline transform from RAS to LPS ras_2_lps = vtk.vtkTransform() ras_2_lps.Scale(-1, -1, 1) lps_2_ras = vtk.vtkTransform() lps_2_ras.Scale(-1, -1, 1) spline_inverse_lps = vtk.vtkGeneralTransform() spline_inverse_lps.Concatenate(lps_2_ras) spline_inverse_lps.Concatenate(tps) spline_inverse_lps.Concatenate(ras_2_lps) # Now, loop through LPS space. Find the effect of the # inverse transform on each point. This is essentially what # vtk.vtkTransformToGrid() does, but this puts things into # LPS. # This low-res grid produced small differences (order of 1-2mm) when transforming # polydatas inside Slicer vs. in this code. #grid_size = [15, 15, 15] #grid_spacing = 10 # This higher-res grid has fewer small numerical differences # grid_size = [50, 50, 50] # grid_spacing = 5 # This higher-res grid has fewer small numerical differences, but files are larger #grid_size = [70, 70, 70] #grid_spacing = 3 # This higher-res grid is sufficient to limit numerical # differences to under .1mm in tests. However, files are # quite large (47M). As this is still much smaller than # the tractography files, and correctness is desired, we # will produce large transform files. A preferable # solution would be to store the forward transform we # compute at the grid points at which it is defined, but # there is no inverse flag available in the file # format. Therefore the inverse must be stored at high # resolution. grid_size = [105, 105, 105] grid_spacing = 2 extent_0 = [-(grid_size[0] - 1)/2, -(grid_size[1] - 1)/2, -(grid_size[2] - 1)/2] extent_1 = [ (grid_size[0] - 1)/2, (grid_size[1] - 1)/2, (grid_size[2] - 1)/2] origin = -grid_spacing * (numpy.array(extent_1) - numpy.array(extent_0))/2.0 grid_points_LPS = list() grid_points_RAS = list() # ordering of grid points must match itk-style array order for images for s in range(extent_0[0], extent_1[0]+1): for p in range(extent_0[1], extent_1[1]+1): for l in range(extent_0[2], extent_1[2]+1): grid_points_RAS.append([-l*grid_spacing, -p*grid_spacing, s*grid_spacing]) grid_points_LPS.append([l*grid_spacing, p*grid_spacing, s*grid_spacing]) displacements_LPS = list() print "LPS grid for storing transform:", grid_points_LPS[0], grid_points_LPS[-1], grid_spacing lps_points = vtk.vtkPoints() lps_points2 = vtk.vtkPoints() for gp_lps in grid_points_LPS: lps_points.InsertNextPoint(gp_lps[0], gp_lps[1], gp_lps[2]) spline_inverse_lps.TransformPoints(lps_points, lps_points2) pidx = 0 for gp_lps in grid_points_LPS: pt = lps_points2.GetPoint(pidx) diff_lps = [pt[0] - gp_lps[0], pt[1] - gp_lps[1], pt[2] - gp_lps[2]] pidx += 1 ## # this tested grid definition and origin were okay. ## diff_lps = [20,30,40] ## # this tested that the ordering of L,P,S is correct: ## diff_lps = [0, gp_lps[1], 0] ## diff_lps = [gp_lps[0], 0, 0] ## diff_lps = [0, 0, gp_lps[2]] ## # this tested that the ordering of grid points is correct ## # only the R>0, A>0, S<0 region shows a transform. ## if gp_lps[0] < 0 and gp_lps[1] < 0 and gp_lps[2] < 0: ## diff_lps = [gp_lps[0]/2.0, 0, 0] ## else: ## diff_lps = [0, 0, 0] displacements_LPS.append(diff_lps) # save the points and displacement vectors in ITK format. #print 'Saving in ITK transform format.' f = open(fname, 'w') f.write('#Insight Transform File V1.0\n') f.write('# Transform 0\n') # ITK version 3 that included an additive (!) affine transform #f.write('Transform: BSplineDeformableTransform_double_3_3\n') # ITK version 4 that does not include a second transform in the file f.write('Transform: BSplineTransform_double_3_3\n') f.write('Parameters: ') # "Here the data are: The bulk of the BSpline part are 3D # displacement vectors for each of the BSpline grid-nodes # in physical space, i.e. for each grid-node, there will # be three blocks of displacements defining dx,dy,dz for # all grid nodes." for block in [0, 1, 2]: for diff in displacements_LPS: f.write('{0} '.format(diff[block])) #FixedParameters: size size size origin origin origin origin spacing spacing spacing (then direction cosines: 1 0 0 0 1 0 0 0 1) f.write('\nFixedParameters:') #f.write(' {0} {0} {0}'.format(2*sz+1)) f.write(' {0}'.format(grid_size[0])) f.write(' {0}'.format(grid_size[1])) f.write(' {0}'.format(grid_size[2])) f.write(' {0}'.format(origin[0])) f.write(' {0}'.format(origin[1])) f.write(' {0}'.format(origin[2])) f.write(' {0} {0} {0}'.format(grid_spacing)) f.write(' 1 0 0 0 1 0 0 0 1\n') f.close() else: tx_inverse = vtk.vtkTransform() tx_inverse.DeepCopy(tx) tx_inverse.Inverse() ras_2_lps = vtk.vtkTransform() ras_2_lps.Scale(-1, -1, 1) lps_2_ras = vtk.vtkTransform() lps_2_ras.Scale(-1, -1, 1) tx2 = vtk.vtkTransform() tx2.Concatenate(lps_2_ras) tx2.Concatenate(tx_inverse) tx2.Concatenate(ras_2_lps) three_by_three = list() translation = list() for i in range(0,3): for j in range(0,3): three_by_three.append(tx2.GetMatrix().GetElement(i,j)) translation.append(tx2.GetMatrix().GetElement(0,3)) translation.append(tx2.GetMatrix().GetElement(1,3)) translation.append(tx2.GetMatrix().GetElement(2,3)) f = open(fname, 'w') f.write('#Insight Transform File V1.0\n') f.write('# Transform 0\n') f.write('Transform: AffineTransform_double_3_3\n') f.write('Parameters: ') for el in three_by_three: f.write('{0} '.format(el)) for el in translation: f.write('{0} '.format(el)) f.write('\nFixedParameters: 0 0 0\n') f.close() idx +=1 return(tx_fnames)
def cropWithCurve(self, modelNode, fidList, breastFlag, reverseNormal, setInsideOut, torsoFlag): # This method takes in a surface scan and list of fiducials as input and computes the breast volume # A curved posterior chest wall is constructed to create the closed breast modelsLogic = slicer.modules.models.logic() #Check which breast volume is being computed for if breastFlag == True: name = "ClosedLeftBreast" else: name = "ClosedRightBreast" # Define parameters InputModel = modelNode.GetPolyData() breastBoundPolyData = vtk.vtkPolyData() self.FiducialsToPolyData(fidList, breastBoundPolyData) # Create plane of best fit from input breast boundary fiducials plane = vtk.vtkPlane() self.LeastSquaresPlane(modelNode, breastBoundPolyData, plane) #creates loop from the breast boundary points breastBound = vtk.vtkImplicitSelectionLoop() breastBound.SetLoop(breastBoundPolyData.GetPoints()) #creates the torso model when the torso flag is set if torsoFlag == True: self.createTorsoModel(InputModel,breastBound) # creates model for the plane of best fit planeModel = vtk.vtkReverseSense() planeModel = self.createPlaneModel(InputModel, plane, breastFlag) splineTransform = vtk.vtkThinPlateSplineTransform() splineTransform = self.thinPlateSplineTransform(breastBoundPolyData,plane, modelNode) #apply spline transform to the plane visualization planeWithSplineTransform = vtk.vtkTransformPolyDataFilter() planeWithSplineTransform.SetInputConnection(planeModel.GetOutputPort()) planeWithSplineTransform.SetTransform(splineTransform) #create model of the transformed plane transformedPlaneModel = modelsLogic.AddModel(planeWithSplineTransform.GetOutputPort()) transformedPlaneModel.GetDisplayNode().SetVisibility(False) transformedPlaneModel.SetName("transformedPlane") # Creates cropped breast model breastModel = self.createBreastModel(breastBound, InputModel, breastFlag, reverseNormal, setInsideOut, plane, planeWithSplineTransform) # Creates cropped-posterior wall posteriorWallModel = vtk.vtkPolyData() posteriorWallModel = self.createClippedPlane(breastBound,planeWithSplineTransform) # Creates closed breast model appendClosedBreast = vtk.vtkAppendPolyData() appendClosedBreast.AddInputData(breastModel) #Breasts appendClosedBreast.AddInputData(posteriorWallModel) #Chest Wall appendClosedBreast.Update() # Applies cleaning filter to closed breast model cleanClosedBreast = vtk.vtkCleanPolyData() cleanClosedBreast.SetInputData(appendClosedBreast.GetOutput()) cleanClosedBreast.Update() # Added closed breast model to slicer models closedBreastModel = modelsLogic.AddModel(cleanClosedBreast.GetOutput()) closedBreastModel.GetDisplayNode().SetVisibility(True) closedBreastModel.SetName(name) closedBreastModel.GetDisplayNode().BackfaceCullingOff() slicer.mrmlScene.RemoveNode(transformedPlaneModel) return closedBreastModel
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)
p2.InsertNextPoint(-100,-100,-50) p1.InsertNextPoint(-100,-100,50) p2.InsertNextPoint(-100,-100,50) p1.InsertNextPoint(-100,100,-50) p2.InsertNextPoint(-100,100,-50) p1.InsertNextPoint(-100,100,50) p2.InsertNextPoint(-100,100,50) p1.InsertNextPoint(100,-100,-50) p2.InsertNextPoint(100,-100,-50) p1.InsertNextPoint(100,-100,50) p2.InsertNextPoint(100,-100,50) p1.InsertNextPoint(100,100,-50) p2.InsertNextPoint(100,100,-50) p1.InsertNextPoint(100,100,50) p2.InsertNextPoint(100,100,50) transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(p1) transform.SetTargetLandmarks(p2) transform.SetBasisToR() gridThinPlate = vtk.vtkTransformToGrid() gridThinPlate.SetInput(transform) gridThinPlate.SetGridExtent(0,64,0,64,0,50) gridThinPlate.SetGridSpacing(3.2,3.2,3.0) gridThinPlate.SetGridOrigin(-102.4,-102.4,-75) gridThinPlate.SetGridScalarTypeToUnsignedChar() gridThinPlate.Update() gridTransform = vtk.vtkGridTransform() gridTransform.SetDisplacementGridData(gridThinPlate.GetOutput()) gridTransform.SetDisplacementShift(gridThinPlate.GetDisplacementShift()) gridTransform.SetDisplacementScale(gridThinPlate.GetDisplacementScale()) reslice = vtk.vtkImageReslice()
def volumetric_spline_augmentation(file, save_name='', save_points=0): #file = '0212.vtk' file_name = 'C:/Users/lowes/OneDrive/Skrivebord/DTU/6_Semester/Bachelor/LAA_segmentation/' + file path = 'C:/Users/lowes/OneDrive/Skrivebord/DTU/6_Semester/Bachelor/Augmented_data/' out_name = path + file.split('.')[0] + 'aug' + save_name + '.vtk' pd_in = vtk.vtkPolyDataReader() pd_in.SetFileName(file_name) pd_in.Update() pd = pd_in.GetOutput() # Get bounding box bounds = pd.GetBounds() x_min = bounds[0] x_max = bounds[1] y_min = bounds[2] y_max = bounds[3] z_min = bounds[4] z_max = bounds[5] # get a measure of scale as the diagonal length scale = math.sqrt((x_max - x_min) * (x_max - x_min) + (y_max - y_min) * (y_max - y_min) + (z_max - z_min) * (z_max - z_min)) # Reference points # Here just corners of the bounding box # TODO: make more points n = 3 i = 0 p1 = vtk.vtkPoints() p1.SetNumberOfPoints(n * n * n) xlin = np.linspace(x_min, x_max, n) ylin = np.linspace(y_min, y_max, n) zlin = np.linspace(z_min, z_max, n) for x in xlin: for y in ylin: for z in zlin: p1.SetPoint(i, x, y, z) i += 1 ''' p1.SetPoint(0, x_min, y_min, z_min) p1.SetPoint(1, x_max, y_min, z_min) p1.SetPoint(2, x_min, y_max, z_min) p1.SetPoint(3, x_max, y_max, z_min) p1.SetPoint(4, x_min, y_min, z_max) p1.SetPoint(5, x_max, y_min, z_max) p1.SetPoint(6, x_min, y_max, z_max) p1.SetPoint(7, x_max, y_max, z_max) ''' # Deformed points p2 = vtk.vtkPoints() # Start by copying all info from p1 p2.DeepCopy(p1) # Displace the points in a random direction displacement_length = scale * 0.05 #change parameter around a bit for i in range(p2.GetNumberOfPoints()): p = list(p2.GetPoint(i)) for j in range(3): p[j] = p[j] + (2.0 * random() - 1) * displacement_length p2.SetPoint(i, p) transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(p1) transform.SetTargetLandmarks(p2) transform.SetBasisToR2LogR() transform.Update() transform_filter = vtk.vtkTransformPolyDataFilter() transform_filter.SetTransform(transform) transform_filter.SetInputData(pd) transform_filter.Update() vs = np.array(transform_filter.GetOutput().GetPoints().GetData()) face_labels = np.array( transform_filter.GetOutput().GetCellData().GetScalars()) poly = dsa.WrapDataObject(transform_filter.GetOutput()).Polygons faces = np.reshape(poly, (-1, 4))[:, 1:4] print('writing file') #vcolor = [] with open(out_name, 'w+') as f: f.write( "# vtk DataFile Version 4.2 \nvtk output \nASCII \n \nDATASET POLYDATA \nPOINTS %d float \n" % len(vs)) for vi, v in enumerate(vs): #vcol = ' %f %f %f' % (vcolor[vi, 0], vcolor[vi, 1], vcolor[vi, 2]) if vcolor is not None else '' f.write("%f %f %f\n" % (v[0], v[1], v[2])) f.write("POLYGONS %d %d \n" % (len(faces), 4 * len(faces))) for face_id in range(len(faces) - 1): f.write("3 %d %d %d\n" % (faces[face_id][0], faces[face_id][1], faces[face_id][2])) f.write("3 %d %d %d" % (faces[-1][0], faces[-1][1], faces[-1][2])) f.write( "\n \nCELL_DATA %d \nSCALARS scalars double \nLOOKUP_TABLE default" % len(faces)) for j, face in enumerate(faces): if j % 9 == 0: f.write("\n") f.write("%d " % face_labels[j]) print('done writing') if save_points: fig = plt.figure() ax = fig.add_subplot(projection='3d') points = np.asarray(p1.GetData()) ax.scatter(points[:, 0], points[:, 1], points[:, 2], linewidth=8) #plt.show() #fig = plt.figure() #ax = fig.add_subplot(projection='3d') points = np.asarray(p2.GetData()) ax.scatter(points[:, 0], points[:, 1], points[:, 2], linewidth=8, marker='x') plt.show() vs1 = np.array(p1.GetData()) out_p1 = path + file.split('.')[0] + '_p1.vtk' with open(out_p1, 'w+') as f: f.write( "# vtk DataFile Version 4.2 \nvtk output \nASCII \n \nDATASET POLYDATA \nPOINTS %d float \n" % len(vs1)) for vi, v in enumerate(vs1): f.write("%f %f %f\n" % (v[0], v[1], v[2])) vs = np.array(p1.GetData()) vs2 = np.array(p2.GetData()) out_p2 = path + file.split('.')[0] + '_p2.vtk' with open(out_p2, 'w+') as f: f.write( "# vtk DataFile Version 4.2 \nvtk output \nASCII \n \nDATASET POLYDATA \nPOINTS %d float \n" % len(vs2)) for vi, v in enumerate(vs2): f.write("%f %f %f\n" % (v[0], v[1], v[2])) '''
def fit_srep(obj_mesh, standard_ellipsoid): eps = np.finfo(float).eps num_pts = obj_mesh.GetNumberOfPoints() coords_mat = np.zeros((num_pts, 3)) for i in range(num_pts): pt = obj_mesh.GetPoint(i) coords_mat[i, :] = pt input_center = np.mean(coords_mat, axis=0)[np.newaxis, :] num_pts = standard_ellipsoid.GetNumberOfPoints() coords_mat = np.zeros((num_pts, 3)) for i in range(num_pts): pt = standard_ellipsoid.GetPoint(i) coords_mat[i, :] = pt ell_center = np.mean(coords_mat, axis=0)[np.newaxis, :] transVec = input_center[0] - ell_center[0] for i in range(num_pts): ell_pt = standard_ellipsoid.GetPoint(i) pt = list(ell_pt) pt = [pt[0] + transVec[0], pt[1] + transVec[1], pt[2] + transVec[2]] pt = tuple(pt) standard_ellipsoid.GetPoints().SetPoint(i, pt) num_pts = standard_ellipsoid.GetNumberOfPoints() num_crest_points = 24 coords_mat = np.zeros((num_pts, 3)) # import matplotlib.pyplot as plt # from mpl_toolkits.mplot3d import Axes3D # fig = plt.figure() # ax = fig.add_subplot(111, projection='3d') for i in range(num_pts): pt = standard_ellipsoid.GetPoint(i) coords_mat[i, :] = pt # if i % 5 == 0: # ax.scatter(pt[0], pt[1], pt[2]) input_center = np.mean(coords_mat, axis=0)[np.newaxis, :] centered_coords = coords_mat - input_center covariance = np.cov(centered_coords.T) / num_pts _, s, vh = np.linalg.svd(covariance) rx, ry, rz = 2 * np.sqrt(s * num_pts).T # plt.show() # mass_filter = vtk.vtkMassProperties() # mass_filter.SetInputData(standard_ellipsoid) # mass_filter.Update() # volume = mass_filter.GetVolume() # rx, ry, rz = np.sqrt(s) # ellipsoid_volume = 4 / 3.0 * np.pi * rx * ry * rz # volume_factor = pow(volume/ ellipsoid_volume, 1.0 / 3.0) volume_factor = 0.8 ### notations consistent with wenqi's presentation rx *= volume_factor ry *= volume_factor rz *= volume_factor mrx_o = (rx * rx - rz * rz) / rx mry_o = (ry * ry - rz * rz) / ry ELLIPSE_SCALE = 0.9 mrb = mry_o * ELLIPSE_SCALE mra = mrx_o * ELLIPSE_SCALE delta_theta = 2 * np.pi / num_crest_points num_steps = 3 skeletal_pts_x = np.zeros((num_crest_points, num_steps)) skeletal_pts_y = np.zeros((num_crest_points, num_steps)) skeletal_pts_z = np.zeros((num_crest_points, num_steps)) bdry_up_x = np.zeros((num_crest_points, num_steps)) bdry_up_y = np.zeros((num_crest_points, num_steps)) bdry_up_z = np.zeros((num_crest_points, num_steps)) bdry_down_x = np.zeros((num_crest_points, num_steps)) bdry_down_y = np.zeros((num_crest_points, num_steps)) bdry_down_z = np.zeros((num_crest_points, num_steps)) crest_bdry_pts = np.zeros((num_crest_points, 3)) crest_skeletal_pts = np.zeros((num_crest_points, 3)) for i in range(num_crest_points): theta = np.pi - delta_theta * i x = mra * np.cos(theta) y = mrb * np.sin(theta) mx_ = (mra * mra - mrb * mrb) * np.cos(theta) / mra my_ = .0 dx_ = x - mx_ dy_ = y - my_ step_size = 1.0 / float(num_steps - 1) for j in range(num_steps): sp_x = mx_ + step_size * j * dx_ sp_y = my_ + step_size * j * dy_ skeletal_pts_x[i, j] = sp_x skeletal_pts_y[i, j] = sp_y sin_spoke_angle = sp_y * mrx_o cos_spoke_angle = sp_x * mry_o # normalize to [-1, 1] l = np.sqrt(sin_spoke_angle**2 + cos_spoke_angle**2) if l > eps: sin_spoke_angle /= l cos_spoke_angle /= l cos_phi = l / (mrx_o * mry_o) sin_phi = np.sqrt(1 - cos_phi**2) bdry_x = rx * cos_phi * cos_spoke_angle bdry_y = ry * cos_phi * sin_spoke_angle bdry_z = rz * sin_phi bdry_up_x[i, j] = bdry_x bdry_up_y[i, j] = bdry_y bdry_up_z[i, j] = bdry_z bdry_down_x[i, j] = bdry_x bdry_down_y[i, j] = bdry_y bdry_down_z[i, j] = -bdry_z ## if at the boundary of the ellipse, add crest spokes if j == num_steps - 1: cx = rx * cos_spoke_angle - sp_x cy = ry * sin_spoke_angle - sp_y cz = 0 vec_c = np.asarray([cx, cy, cz]) norm_c = np.linalg.norm(vec_c) dir_c = np.asarray([bdry_x - sp_x, bdry_y - sp_y, 0.0]) dir_c = dir_c / np.linalg.norm(vec_c) crest_spoke = norm_c * dir_c crest_bdry_x = crest_spoke[0] + sp_x crest_bdry_y = crest_spoke[1] + sp_y crest_bdry_z = 0.0 crest_bdry_pts[i] = np.asarray( [crest_bdry_x, crest_bdry_y, crest_bdry_z]) crest_skeletal_pts[i] = np.asarray([sp_x, sp_y, 0.0]) ### Rotate skeletal/implied boundary points as boundary points of the ellipsoid rot_obj = np.flipud(vh.T) ## make this rotation matrix same with c++ computation with Eigen3 # rot_obj[0, :] *= -1 # rot_obj[-1, :] *= -1 concate_skeletal_pts = np.concatenate((skeletal_pts_x.flatten()[:, np.newaxis], \ skeletal_pts_y.flatten()[:, np.newaxis], \ skeletal_pts_z.flatten()[:, np.newaxis]), \ axis=1) concate_bdry_up_pts = np.concatenate((bdry_up_x.flatten()[:, np.newaxis], \ bdry_up_y.flatten()[:, np.newaxis], \ bdry_up_z.flatten()[:, np.newaxis]), axis=1) concate_bdry_down_pts = np.concatenate((bdry_down_x.flatten()[:, np.newaxis], \ bdry_down_y.flatten()[:, np.newaxis], \ bdry_down_z.flatten()[:, np.newaxis]), axis=1) second_moment_srep = np.matmul(concate_skeletal_pts.T, concate_skeletal_pts) s_srep, v_srep = np.linalg.eig(second_moment_srep) rot_srep = v_srep rotation = np.matmul(rot_obj, rot_srep) rotation = np.flipud(rotation) transformed_concate_skeletal_pts = np.matmul(concate_skeletal_pts, rotation) + input_center transformed_concate_bdry_up_pts = np.matmul(concate_bdry_up_pts, rotation) + input_center transformed_concate_bdry_down_pts = np.matmul(concate_bdry_down_pts, rotation) + input_center transformed_crest_bdry_pts = np.matmul(crest_bdry_pts, rotation) + input_center transformed_crest_skeletal_pts = np.matmul(crest_skeletal_pts, rotation) + input_center ### Convert spokes to visualizable elements up_spokes_poly = vtk.vtkPolyData() up_spokes_pts = vtk.vtkPoints() up_spokes_cells = vtk.vtkCellArray() down_spokes_poly = vtk.vtkPolyData() down_spokes_pts = vtk.vtkPoints() down_spokes_cells = vtk.vtkCellArray() crest_spokes_poly = vtk.vtkPolyData() crest_spokes_pts = vtk.vtkPoints() crest_spokes_cells = vtk.vtkCellArray() for i in range(concate_skeletal_pts.shape[0]): id_s = up_spokes_pts.InsertNextPoint( transformed_concate_skeletal_pts[i, :]) id_b = up_spokes_pts.InsertNextPoint( transformed_concate_bdry_up_pts[i, :]) id_sdwn = down_spokes_pts.InsertNextPoint( transformed_concate_skeletal_pts[i, :]) id_down = down_spokes_pts.InsertNextPoint( transformed_concate_bdry_down_pts[i, :]) up_spoke = vtk.vtkLine() up_spoke.GetPointIds().SetId(0, id_s) up_spoke.GetPointIds().SetId(1, id_b) up_spokes_cells.InsertNextCell(up_spoke) down_spoke = vtk.vtkLine() down_spoke.GetPointIds().SetId(0, id_sdwn) down_spoke.GetPointIds().SetId(1, id_down) down_spokes_cells.InsertNextCell(down_spoke) up_spokes_poly.SetPoints(up_spokes_pts) up_spokes_poly.SetLines(up_spokes_cells) down_spokes_poly.SetPoints(down_spokes_pts) down_spokes_poly.SetLines(down_spokes_cells) for i in range(num_crest_points): id_crest_s = crest_spokes_pts.InsertNextPoint( transformed_crest_skeletal_pts[i, :]) id_crest_b = crest_spokes_pts.InsertNextPoint( transformed_crest_bdry_pts[i, :]) crest_spoke = vtk.vtkLine() crest_spoke.GetPointIds().SetId(0, id_crest_s) crest_spoke.GetPointIds().SetId(1, id_crest_b) crest_spokes_cells.InsertNextCell(crest_spoke) crest_spokes_poly.SetPoints(crest_spokes_pts) crest_spokes_poly.SetLines(crest_spokes_cells) append_filter = vtk.vtkAppendPolyData() append_filter.AddInputData(up_spokes_poly) append_filter.AddInputData(down_spokes_poly) append_filter.AddInputData(crest_spokes_poly) append_filter.Update() srep_poly = append_filter.GetOutput() input_mesh = standard_ellipsoid num_spokes = srep_poly.GetNumberOfCells() num_pts = srep_poly.GetNumberOfPoints() radii_array = np.zeros(num_spokes) dir_array = np.zeros((num_spokes, 3)) base_array = np.zeros((num_spokes, 3)) ### read the parameters from s-rep for i in range(num_spokes): id_base_pt = i * 2 id_bdry_pt = id_base_pt + 1 base_pt = np.array(srep_poly.GetPoint(id_base_pt)) bdry_pt = np.array(srep_poly.GetPoint(id_bdry_pt)) radius = np.linalg.norm(bdry_pt - base_pt) direction = (bdry_pt - base_pt) / radius radii_array[i] = radius dir_array[i, :] = direction base_array[i, :] = base_pt def obj_func(radii, grad=None): """ Square of signed distance from tips of spokes to the input_mesh """ implicit_distance = vtk.vtkImplicitPolyDataDistance() implicit_distance.SetInput(input_mesh) total_loss = 0 for i, radius in enumerate(radii): direction = dir_array[i, :] base_pt = base_array[i, :] bdry_pt = base_pt + radius * direction dist = implicit_distance.FunctionValue(bdry_pt) total_loss += dist**2 return total_loss # from scipy import optimize as opt # minimum = opt.fmin(obj_func, radii_array) # minimizer = minimum[0] ### optimize the variables (i.e., radii) import nlopt opt = nlopt.opt(nlopt.LN_NEWUOA, len(radii_array)) opt.set_min_objective(obj_func) opt.set_maxeval(2000) minimizer = opt.optimize(radii_array) ## update radii of s-rep and return the updated num_diff_spokes = 0 arr_length = vtk.vtkDoubleArray() arr_length.SetNumberOfComponents(1) arr_length.SetName("spokeLength") arr_dirs = vtk.vtkDoubleArray() arr_dirs.SetNumberOfComponents(3) arr_dirs.SetName("spokeDirection") for i in range(num_spokes): id_base_pt = i * 2 id_bdry_pt = id_base_pt + 1 base_pt = base_array[i, :] radius = minimizer[i] direction = dir_array[i, :] new_bdry_pt = base_pt + radius * direction arr_length.InsertNextValue(radius) arr_dirs.InsertNextTuple(direction) srep_poly.GetPoints().SetPoint(id_bdry_pt, new_bdry_pt) if np.abs(np.linalg.norm(new_bdry_pt - base_pt) - radii_array[i]) > eps: num_diff_spokes += 1 # srep_poly.SetPoint srep_poly.GetPointData().AddArray(arr_length) srep_poly.GetPointData().AddArray(arr_dirs) srep_poly.Modified() ell_srep = srep_poly target_mesh = obj_mesh ell_mesh = standard_ellipsoid source_pts = vtk.vtkPoints() target_pts = vtk.vtkPoints() for i in range(num_pts): pt = [0] * 3 ell_mesh.GetPoint(i, pt) source_pts.InsertNextPoint(pt) target_mesh.GetPoint(i, pt) target_pts.InsertNextPoint(pt) source_pts.Modified() target_pts.Modified() ### Interpolate deformation with thin-plate-spline tps = vtk.vtkThinPlateSplineTransform() tps.SetSourceLandmarks(source_pts) tps.SetTargetLandmarks(target_pts) tps.SetBasisToR() tps.Modified() ### Apply the deformation onto the spokes deformed_srep = vtk.vtkPolyData() deformed_spokes_ends = vtk.vtkPoints() deformed_spoke_lines = vtk.vtkCellArray() # refined_srep is a polydata that collects spokes for i in range(ell_srep.GetNumberOfCells()): base_pt_id = i * 2 bdry_pt_id = i * 2 + 1 s_pt = ell_srep.GetPoint(base_pt_id) b_pt = ell_srep.GetPoint(bdry_pt_id) new_s_pt = tps.TransformPoint(s_pt) new_b_pt = tps.TransformPoint(b_pt) id0 = deformed_spokes_ends.InsertNextPoint(new_s_pt) id1 = deformed_spokes_ends.InsertNextPoint(new_b_pt) spoke_line = vtk.vtkLine() spoke_line.GetPointIds().SetId(0, id0) spoke_line.GetPointIds().SetId(1, id1) deformed_spoke_lines.InsertNextCell(spoke_line) deformed_srep.SetPoints(deformed_spokes_ends) deformed_srep.SetLines(deformed_spoke_lines) deformed_srep.Modified() return deformed_srep
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
p1.SetPoint(3, 255, 255, 0) p1.SetPoint(4, 96, 96, 0) p1.SetPoint(5, 96, 159, 0) p1.SetPoint(6, 159, 159, 0) p1.SetPoint(7, 159, 96, 0) p2 = vtk.vtkPoints() p2.SetNumberOfPoints(8) p2.SetPoint(0, 0, 0, 0) p2.SetPoint(1, 0, 255, 0) p2.SetPoint(2, 255, 0, 0) p2.SetPoint(3, 255, 255, 0) p2.SetPoint(4, 96, 159, 0) p2.SetPoint(5, 159, 159, 0) p2.SetPoint(6, 159, 96, 0) p2.SetPoint(7, 96, 96, 0) thinPlate = vtk.vtkThinPlateSplineTransform() thinPlate.SetSourceLandmarks(p2) thinPlate.SetTargetLandmarks(p1) thinPlate.SetBasisToR2LogR() # convert the thin plate spline into a grid transformToGrid = vtk.vtkTransformToGrid() transformToGrid.SetInput(thinPlate) transformToGrid.SetGridSpacing(16, 16, 1) transformToGrid.SetGridOrigin(-0.5, -0.5, 0) transformToGrid.SetGridExtent(0, 16, 0, 16, 0, 0) transformToGrid.Update() transform = vtk.vtkGridTransform() transform.SetDisplacementGridConnection(transformToGrid.GetOutputPort()) transform.SetInterpolationModeToCubic() # you must invert the transform before passing it to vtkImageReslice transform.Inverse()
def volumetric_spline_augmentation(pd): # Get bounding box bounds = pd.GetBounds() x_min = bounds[0] x_max = bounds[1] y_min = bounds[2] y_max = bounds[3] z_min = bounds[4] z_max = bounds[5] # get a measure of scale as the diagonal length scale = math.sqrt((x_max - x_min) * (x_max - x_min) + (y_max - y_min) * (y_max - y_min) + (z_max - z_min) * (z_max - z_min)) # Reference points # Here just corners of the bounding box n = 6 i = 0 p1 = vtk.vtkPoints() p1.SetNumberOfPoints(n * n * n) xlin = np.linspace(x_min, x_max, n) ylin = np.linspace(y_min, y_max, n) zlin = np.linspace(z_min, z_max, n) for x in xlin: for y in ylin: for z in zlin: p1.SetPoint(i, x, y, z) i += 1 # Deformed points p2 = vtk.vtkPoints() # Start by copying all info from p1 p2.DeepCopy(p1) # Displace the points in a random direction displacement_length = scale * 0.04 #change parameter around a bit for i in range(p2.GetNumberOfPoints()): p = list(p2.GetPoint(i)) for j in range(3): p[j] = p[j] + (2.0 * random() - 1) * displacement_length p2.SetPoint(i, p) transform = vtk.vtkThinPlateSplineTransform() transform.SetSourceLandmarks(p1) transform.SetTargetLandmarks(p2) transform.SetBasisToR() transform.Update() transform_filter = vtk.vtkTransformPolyDataFilter() transform_filter.SetTransform(transform) transform_filter.SetInputData(pd) transform_filter.Update() points = np.array(transform_filter.GetOutput().GetPoints().GetData()) face_labels = np.array( transform_filter.GetOutput().GetCellData().GetScalars()) poly = dsa.WrapDataObject(transform_filter.GetOutput()).Polygons faces = np.reshape(poly, (-1, 4))[:, 1:4] return points, faces