def onApply(self): # Make sure the user wants to do the operation, even if the segment is not visible if not self.scriptedEffect.confirmCurrentSegmentVisible(): return self.scriptedEffect.saveStateForUndo() # Get modifier labelmap and parameters modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(selectedSegmentLabelmap) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType()) shellMode = self.scriptedEffect.parameter("ShellMode") shellThicknessMM = abs( self.scriptedEffect.doubleParameter("ShellThicknessMm")) import vtkITK margin = vtkITK.vtkITKImageMargin() margin.SetInputConnection(thresh.GetOutputPort()) margin.CalculateMarginInMMOn() spacing = selectedSegmentLabelmap.GetSpacing() voxelDiameter = min(selectedSegmentLabelmap.GetSpacing()) if shellMode == MEDIAL_SURFACE: margin.SetOuterMarginMM(0.5 * shellThicknessMM) margin.SetInnerMarginMM(-0.5 * shellThicknessMM + 0.5 * voxelDiameter) elif shellMode == INSIDE_SURFACE: margin.SetOuterMarginMM(shellThicknessMM + 0.1 * voxelDiameter) margin.SetInnerMarginMM( 0.0 + 0.1 * voxelDiameter) # Don't include the original border (0.0) elif shellMode == OUTSIDE_SURFACE: margin.SetOuterMarginMM(0.0) margin.SetInnerMarginMM(-shellThicknessMM + voxelDiameter) modifierLabelmap.DeepCopy(margin.GetOutput()) # This can be a long operation - indicate it to the user qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor) margin.Update() modifierLabelmap.ShallowCopy(margin.GetOutput()) # Apply changes self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) qt.QApplication.restoreOverrideCursor()
def onApply(self): # Make sure the user wants to do the operation, even if the segment is not visible if not self.scriptedEffect.confirmCurrentSegmentVisible(): return self.scriptedEffect.saveStateForUndo() # Get modifier labelmap and parameters modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() marginSizeMM = self.scriptedEffect.doubleParameter("MarginSizeMm") # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(selectedSegmentLabelmap) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType()) if (marginSizeMM < 0): # The distance filter used in the margin filter starts at zero at the border voxels, # so if we need to shrink the margin, it is more accurate to invert the labelmap and # use positive distance when calculating the margin thresh.SetInValue(labelValue) thresh.SetOutValue(backgroundValue) import vtkITK margin = vtkITK.vtkITKImageMargin() margin.SetInputConnection(thresh.GetOutputPort()) margin.CalculateMarginInMMOn() margin.SetOuterMarginMM(abs(marginSizeMM)) margin.Update() if marginSizeMM >= 0: modifierLabelmap.ShallowCopy(margin.GetOutput()) else: # If we are shrinking then the result needs to be inverted. thresh = vtk.vtkImageThreshold() thresh.SetInputData(margin.GetOutput()) thresh.ThresholdByLower(0) thresh.SetInValue(labelValue) thresh.SetOutValue(backgroundValue) thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType()) thresh.Update() modifierLabelmap.ShallowCopy(thresh.GetOutput()) # Apply changes self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) qt.QApplication.restoreOverrideCursor()
def processHollowing(self): # Get modifier labelmap and parameters modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(selectedSegmentLabelmap) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType()) shellMode = self.scriptedEffect.parameter("ShellMode") shellThicknessMM = abs( self.scriptedEffect.doubleParameter("ShellThicknessMm")) import vtkITK margin = vtkITK.vtkITKImageMargin() margin.SetInputConnection(thresh.GetOutputPort()) margin.CalculateMarginInMMOn() spacing = selectedSegmentLabelmap.GetSpacing() voxelDiameter = min(selectedSegmentLabelmap.GetSpacing()) if shellMode == MEDIAL_SURFACE: margin.SetOuterMarginMM(0.5 * shellThicknessMM) margin.SetInnerMarginMM(-0.5 * shellThicknessMM + 0.5 * voxelDiameter) elif shellMode == INSIDE_SURFACE: margin.SetOuterMarginMM(shellThicknessMM + 0.1 * voxelDiameter) margin.SetInnerMarginMM( 0.0 + 0.1 * voxelDiameter) # Don't include the original border (0.0) elif shellMode == OUTSIDE_SURFACE: margin.SetOuterMarginMM(0.0) margin.SetInnerMarginMM(-shellThicknessMM + voxelDiameter) modifierLabelmap.DeepCopy(margin.GetOutput()) margin.Update() modifierLabelmap.ShallowCopy(margin.GetOutput()) # Apply changes self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
def onApply(self): # Make sure the user wants to do the operation, even if the segment is not visible if not self.scriptedEffect.confirmCurrentSegmentVisible(): return self.scriptedEffect.saveStateForUndo() # Get modifier labelmap and parameters modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap() selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap() # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value labelValue = 1 backgroundValue = 0 thresh = vtk.vtkImageThreshold() thresh.SetInputData(selectedSegmentLabelmap) thresh.ThresholdByLower(0) thresh.SetInValue(backgroundValue) thresh.SetOutValue(labelValue) thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType()) marginSizeMM = self.scriptedEffect.doubleParameter("MarginSizeMm") if (marginSizeMM < 0): # The border voxel starts at zero, if we need to account for this single voxel thickness in the margin size. # Currently this is done by reducing the magnitude of the margin size by 0.9 * the smallest spacing. voxelDiameter = min(selectedSegmentLabelmap.GetSpacing()) marginSizeMM += 0.9 * voxelDiameter import vtkITK margin = vtkITK.vtkITKImageMargin() margin.SetInputConnection(thresh.GetOutputPort()) margin.CalculateMarginInMMOn() margin.SetOuterMarginMM(marginSizeMM) margin.Update() modifierLabelmap.ShallowCopy(margin.GetOutput()) # Apply changes self.scriptedEffect.modifySelectedSegmentByLabelmap( modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet) qt.QApplication.restoreOverrideCursor()
def _extractCavity(self, shrunkenPd): spacing = self._inputSpacing / self.remeshOversampling outsideObjectLabelmap = WrapSolidifyLogic._polydataToLabelmap( shrunkenPd, spacing) # 0=outside, 1=inside inputLabelmap = WrapSolidifyLogic._polydataToLabelmap( self._inputPd, referenceImage=outsideObjectLabelmap) if self.splitCavities: # It is less accurate but more robust to dilate labelmap than grow polydata. # Since accuracy is not important here, we dilate labelmap. splitCavitiesRadius = self.splitCavitiesDiameter / 2.0 # Dilate import vtkITK margin = vtkITK.vtkITKImageMargin() margin.SetInputData(inputLabelmap) margin.CalculateMarginInMMOn() margin.SetOuterMarginMM(splitCavitiesRadius) margin.Update() # extendedInputLabelmap: 255 near original inputLabelmap voxels, 0 elsewhere extendedInputLabelmap = margin.GetOutput() self._saveIntermediateResult( "SplitCavitiesGrown", WrapSolidifyLogic._labelmapToPolydata(extendedInputLabelmap, 255)) else: extendedInputLabelmap = inputLabelmap outsideObjectLabelmapInverter = vtk.vtkImageThreshold() outsideObjectLabelmapInverter.SetInputData(outsideObjectLabelmap) outsideObjectLabelmapInverter.ThresholdByLower(0) outsideObjectLabelmapInverter.SetInValue(1) # backgroundValue outsideObjectLabelmapInverter.SetOutValue(0) # labelValue outsideObjectLabelmapInverter.SetOutputScalarType( outsideObjectLabelmap.GetScalarType()) outsideObjectLabelmapInverter.Update() addImage = vtk.vtkImageMathematics() addImage.SetInput1Data(outsideObjectLabelmapInverter.GetOutput()) addImage.SetInput2Data(extendedInputLabelmap) addImage.SetOperationToMax() addImage.Update() internalHolesLabelmap = addImage.GetOutput() # internal holes are 0, elsewhere >=1 self._saveIntermediateResult( "SplitCavitiesAll", WrapSolidifyLogic._labelmapToPolydata(internalHolesLabelmap, 0)) # Find largest internal hole largestHoleExtract = vtk.vtkImageConnectivityFilter() largestHoleExtract.SetScalarRange(-0.5, 0.5) largestHoleExtract.SetInputData(internalHolesLabelmap) largestHoleExtract.SetExtractionModeToLargestRegion() largestHoleExtract.Update() largestHolesLabelmap = largestHoleExtract.GetOutput() # Convert back to polydata initialRegionPd = vtk.vtkPolyData() initialRegionPd.DeepCopy( WrapSolidifyLogic._labelmapToPolydata(largestHolesLabelmap, 0)) self._saveIntermediateResult("SplitCavitiesLargest", initialRegionPd) if self.splitCavities: return self._shrinkWrap(initialRegionPd) else: return initialRegionPd
def _getInitialRegionPd(self): """Get initial shape that will be snapped to closest point of the input segment""" spacing = self._inputSpacing / self.remeshOversampling if self.carveHolesInOuterSurface: # Grow input polydata to close holes between outer surface and internal cavities. # It is less accurate but more robust to dilate labelmap than grow polydata. # Since accuracy is not important here, we dilate labelmap. # Convert to labelmap carveHolesInOuterSurfaceRadius = self.carveHolesInOuterSurfaceDiameter / 2.0 # add self.carveHolesInOuterSurfaceDiameter extra to bounds to ensure that the grown input still has an margin around inputLabelmap = WrapSolidifyLogic._polydataToLabelmap( self._inputPd, spacing, extraMarginToBounds=carveHolesInOuterSurfaceRadius) self._saveIntermediateResult( "InitialRegionResampled", WrapSolidifyLogic._labelmapToPolydata(inputLabelmap, 1)) # Dilate import vtkITK margin = vtkITK.vtkITKImageMargin() margin.SetInputData(inputLabelmap) margin.CalculateMarginInMMOn() margin.SetOuterMarginMM(carveHolesInOuterSurfaceRadius) margin.Update() # extendedInputLabelmap: 255 near original inputLabelmap voxels, 0 elsewhere extendedInputLabelmap = margin.GetOutput() self._saveIntermediateResult( "InitialRegionGrown", WrapSolidifyLogic._labelmapToPolydata(extendedInputLabelmap, 255)) # Region growing from a corner of the image seedPoints = vtk.vtkPoints() seedPoints.InsertNextPoint( inputLabelmap.GetOrigin()[0] + spacing / 2, inputLabelmap.GetOrigin()[1] + spacing / 2, inputLabelmap.GetOrigin()[2] + spacing / 2) seedScalars = vtk.vtkUnsignedCharArray() seedScalars.InsertNextValue( 255) # this will be the label value to the grown region seedData = vtk.vtkPolyData() seedData.SetPoints(seedPoints) seedData.GetPointData().SetScalars(seedScalars) regionGrowing = vtk.vtkImageConnectivityFilter() regionGrowing.SetSeedData(seedData) regionGrowing.SetScalarRange(-10, 10) regionGrowing.SetInputData(extendedInputLabelmap) regionGrowing.Update() # outsideObjectImage: 255 outside the object, 0 inside outsideObjectLabelmap = regionGrowing.GetOutput() # Cavity extraction and outer surface extraction starts from the same outer surface shrinkwrap if self.region == REGION_OUTER_SURFACE or self.region == REGION_LARGEST_CAVITY: if self.carveHolesInOuterSurface: # Convert back to polydata initialRegionPd = vtk.vtkPolyData() initialRegionPd.DeepCopy( WrapSolidifyLogic._labelmapToPolydata( outsideObjectLabelmap, 255)) else: # create sphere that encloses entire segment content bounds = np.zeros(6) self._inputPd.GetBounds(bounds) diameters = np.array([ bounds[1] - bounds[0], bounds[3] - bounds[2], bounds[5] - bounds[4] ]) maxRadius = max(diameters) / 2.0 sphereSource = vtk.vtkSphereSource() # to make sure the volume is fully included in the sphere, radius must be sqrt(2) times larger sphereSource.SetRadius(maxRadius * 1.5) # Set resolution to be about one magnitude lower than the final resolution # (by creating an initial surface element for about every 100th final element). sphereSurfaceArea = 4 * math.pi * maxRadius * maxRadius voxelSurfaceArea = spacing * spacing numberOfSurfaceElements = sphereSurfaceArea / voxelSurfaceArea numberOfIinitialSphereSurfaceElements = numberOfSurfaceElements / 100 sphereResolution = math.sqrt( numberOfIinitialSphereSurfaceElements) # Set resolution to minimum 10 sphereResolution = max(int(sphereResolution), 10) sphereSource.SetPhiResolution(sphereResolution) sphereSource.SetThetaResolution(sphereResolution) sphereSource.SetCenter((bounds[0] + bounds[1]) / 2.0, (bounds[2] + bounds[3]) / 2.0, (bounds[4] + bounds[5]) / 2.0) sphereSource.Update() initialRegionPd = sphereSource.GetOutput() elif self.region == REGION_SEGMENT: # create initial region from segment (that will be grown) if not self.regionSegmentId: raise ValueError("Region segment is not set") if self.regionSegmentId == self.segmentId: raise ValueError( "Region segment cannot be the same segment as the current segment" ) initialRegionPd = vtk.vtkPolyData() self.segmentationNode.GetClosedSurfaceRepresentation( self.regionSegmentId, initialRegionPd) if not initialRegionPd or initialRegionPd.GetNumberOfPoints() == 0: raise ValueError("Region segment is empty") # initialRegionPd = self._remeshPolydata(initialRegionPd, self._inputSpacing*5.0) # simplify the mesh else: raise ValueError("Invalid region: " + self.region) cleanPolyData = vtk.vtkCleanPolyData() cleanPolyData.SetInputData(initialRegionPd) cleanPolyData.Update() initialRegionPd = cleanPolyData.GetOutput() self._saveIntermediateResult("InitialRegion", initialRegionPd) return initialRegionPd