2 import vtk, qt, ctk, slicer
5 from SegmentEditorEffects
import *
8 """This effect makes a segment hollow by replacing it with a shell at the segment boundary""" 11 scriptedEffect.name =
'Hollow' 12 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
15 import qSlicerSegmentationsEditorEffectsPythonQt
as effects
16 clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(
None)
17 clonedEffect.setPythonSource(__file__.replace(
'\\',
'/'))
21 iconPath = os.path.join(os.path.dirname(__file__),
'Resources/Icons/Hollow.png')
22 if os.path.exists(iconPath):
23 return qt.QIcon(iconPath)
27 return """Make the selected segment hollow by replacing the segment with a uniform-thickness shell defined by the segment boundary.""" 31 operationLayout = qt.QVBoxLayout()
41 self.scriptedEffect.addLabeledOptionsWidget(
"Use current segment as:", operationLayout)
52 self.
shellThicknessLabel.setToolTip(
"Closest achievable thickness. Constrained by the segmentation's binary labelmap representation spacing.")
54 shellThicknessFrame = qt.QHBoxLayout()
56 self.
shellThicknessMMLabel = self.scriptedEffect.addLabeledOptionsWidget(
"Shell thickness:", shellThicknessFrame)
60 self.
applyButton.objectName = self.__class__.__name__ +
'Apply' 61 self.
applyButton.setToolTip(
"Makes the segment hollow by replacing it with a thick shell at the segment boundary.")
62 self.scriptedEffect.addOptionsWidget(self.
applyButton)
72 return slicer.util.mainWindow().cursor
75 self.scriptedEffect.setParameterDefault(
"ShellMode", INSIDE_SURFACE)
76 self.scriptedEffect.setParameterDefault(
"ShellThicknessMm", 3.0)
79 selectedSegmentLabelmapSpacing = [1.0, 1.0, 1.0]
80 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
81 if selectedSegmentLabelmap:
82 selectedSegmentLabelmapSpacing = selectedSegmentLabelmap.GetSpacing()
84 shellThicknessMM = abs(self.scriptedEffect.doubleParameter(
"ShellThicknessMm"))
85 shellThicknessPixel = [int(math.floor(shellThicknessMM / selectedSegmentLabelmapSpacing[componentIndex]))
for componentIndex
in range(3)]
86 return shellThicknessPixel
89 shellThicknessMM = self.scriptedEffect.doubleParameter(
"ShellThicknessMm")
91 self.setWidgetMinMaxStepFromImageSpacing(self.
shellThicknessMMSpinBox, self.scriptedEffect.selectedSegmentLabelmap())
107 selectedSegmentLabelmapSpacing = [1.0, 1.0, 1.0]
108 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
109 if selectedSegmentLabelmap:
110 selectedSegmentLabelmapSpacing = selectedSegmentLabelmap.GetSpacing()
112 if shellThicknessPixel[0] < 1
or shellThicknessPixel[1] < 1
or shellThicknessPixel[2] < 1:
117 self.
shellThicknessLabel.text =
"Actual: {0} x {1} x {2} mm ({3}x{4}x{5} pixel)".format(*thicknessMM, *shellThicknessPixel)
122 self.setWidgetMinMaxStepFromImageSpacing(self.
shellThicknessMMSpinBox, self.scriptedEffect.selectedSegmentLabelmap())
130 self.scriptedEffect.setParameter(
"ShellMode", INSIDE_SURFACE)
134 self.scriptedEffect.setParameter(
"ShellMode", MEDIAL_SURFACE)
138 self.scriptedEffect.setParameter(
"ShellMode", OUTSIDE_SURFACE)
141 selectedSegmentLabelmapSpacing = [1.0, 1.0, 1.0]
142 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
143 if selectedSegmentLabelmap:
144 selectedSegmentLabelmapSpacing = selectedSegmentLabelmap.GetSpacing()
147 shellThicknessMM = [abs((shellThicknessPixel[i])*selectedSegmentLabelmapSpacing[i])
for i
in range(3)]
149 if shellThicknessMM[i] > 0:
150 shellThicknessMM[i] = round(shellThicknessMM[i], max(int(-math.floor(math.log10(shellThicknessMM[i]))),1))
151 return shellThicknessMM
155 if not self.scriptedEffect.confirmCurrentSegmentVisible():
158 self.scriptedEffect.saveStateForUndo()
161 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
162 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
167 thresh = vtk.vtkImageThreshold()
168 thresh.SetInputData(selectedSegmentLabelmap)
169 thresh.ThresholdByLower(0)
170 thresh.SetInValue(backgroundValue)
171 thresh.SetOutValue(labelValue)
172 thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
174 shellMode = self.scriptedEffect.parameter(
"ShellMode")
175 shellThicknessMM = abs(self.scriptedEffect.doubleParameter(
"ShellThicknessMm"))
177 margin = vtkITK.vtkITKImageMargin()
178 margin.SetInputConnection(thresh.GetOutputPort())
179 margin.CalculateMarginInMMOn()
181 spacing = selectedSegmentLabelmap.GetSpacing()
182 voxelDiameter = min(selectedSegmentLabelmap.GetSpacing())
183 if shellMode == MEDIAL_SURFACE:
184 margin.SetOuterMarginMM( 0.5 * shellThicknessMM)
185 margin.SetInnerMarginMM(-0.5 * shellThicknessMM + 0.5*voxelDiameter)
186 elif shellMode == INSIDE_SURFACE:
187 margin.SetOuterMarginMM(shellThicknessMM + 0.1*voxelDiameter)
188 margin.SetInnerMarginMM(0.0 + 0.1*voxelDiameter)
189 elif shellMode == OUTSIDE_SURFACE:
190 margin.SetOuterMarginMM(0.0)
191 margin.SetInnerMarginMM(-shellThicknessMM + voxelDiameter)
193 modifierLabelmap.DeepCopy(margin.GetOutput())
196 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
199 modifierLabelmap.ShallowCopy(margin.GetOutput())
202 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
204 qt.QApplication.restoreOverrideCursor()
206 INSIDE_SURFACE =
'INSIDE_SURFACE' 207 MEDIAL_SURFACE =
'MEDIAL_SURFACE' 208 OUTSIDE_SURFACE =
'OUTSIDE_SURFACE' def __init__(self, scriptedEffect)
def insideSurfaceModeToggled(self, toggled)
outsideSurfaceOptionRadioButton
def setupOptionsFrame(self)
def setMRMLDefaults(self)
def createCursor(self, widget)
def getShellThicknessMM(self)
def getShellThicknessPixel(self)
def outsideSurfaceModeToggled(self, toggled)
def medialSurfaceModeToggled(self, toggled)
insideSurfaceOptionRadioButton
medialSurfaceOptionRadioButton
def updateMRMLFromGUI(self)
def updateGUIFromMRML(self)