10 from SegmentEditorEffects
import *
14 """This effect makes a segment hollow by replacing it with a shell at the segment boundary""" 17 scriptedEffect.name =
'Hollow' 18 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
21 import qSlicerSegmentationsEditorEffectsPythonQt
as effects
22 clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(
None)
23 clonedEffect.setPythonSource(__file__.replace(
'\\',
'/'))
27 iconPath = os.path.join(os.path.dirname(__file__),
'Resources/Icons/Hollow.png')
28 if os.path.exists(iconPath):
29 return qt.QIcon(iconPath)
33 return """Make the selected segment hollow by replacing the segment with a uniform-thickness shell defined by the segment boundary.""" 37 operationLayout = qt.QVBoxLayout()
47 self.scriptedEffect.addLabeledOptionsWidget(
"Use current segment as:", operationLayout)
58 self.
shellThicknessLabel.setToolTip(
"Closest achievable thickness. Constrained by the segmentation's binary labelmap representation spacing.")
60 shellThicknessFrame = qt.QHBoxLayout()
62 self.
shellThicknessMMLabel = self.scriptedEffect.addLabeledOptionsWidget(
"Shell thickness:", shellThicknessFrame)
67 This operation may take a while.")
72 self.
applyButton.objectName = self.__class__.__name__ +
'Apply' 73 self.
applyButton.setToolTip(
"Makes the segment hollow by replacing it with a thick shell at the segment boundary.")
74 self.scriptedEffect.addOptionsWidget(self.
applyButton)
85 return slicer.util.mainWindow().cursor
88 self.scriptedEffect.setParameterDefault(
"ApplyToAllVisibleSegments", 0)
89 self.scriptedEffect.setParameterDefault(
"ShellMode", INSIDE_SURFACE)
90 self.scriptedEffect.setParameterDefault(
"ShellThicknessMm", 3.0)
93 selectedSegmentLabelmapSpacing = [1.0, 1.0, 1.0]
94 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
95 if selectedSegmentLabelmap:
96 selectedSegmentLabelmapSpacing = selectedSegmentLabelmap.GetSpacing()
98 shellThicknessMM = abs(self.scriptedEffect.doubleParameter(
"ShellThicknessMm"))
99 shellThicknessPixel = [int(math.floor(shellThicknessMM / selectedSegmentLabelmapSpacing[componentIndex]))
for componentIndex
in range(3)]
100 return shellThicknessPixel
103 shellThicknessMM = self.scriptedEffect.doubleParameter(
"ShellThicknessMm")
105 self.setWidgetMinMaxStepFromImageSpacing(self.
shellThicknessMMSpinBox, self.scriptedEffect.selectedSegmentLabelmap())
121 selectedSegmentLabelmapSpacing = [1.0, 1.0, 1.0]
122 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
123 if selectedSegmentLabelmap:
124 selectedSegmentLabelmapSpacing = selectedSegmentLabelmap.GetSpacing()
126 if shellThicknessPixel[0] < 1
or shellThicknessPixel[1] < 1
or shellThicknessPixel[2] < 1:
131 self.
shellThicknessLabel.text =
"Actual: {} x {} x {} mm ({}x{}x{} pixel)".format(*thicknessMM, *shellThicknessPixel)
136 self.setWidgetMinMaxStepFromImageSpacing(self.
shellThicknessMMSpinBox, self.scriptedEffect.selectedSegmentLabelmap())
138 applyToAllVisibleSegments = qt.Qt.Unchecked
if self.scriptedEffect.integerParameter(
"ApplyToAllVisibleSegments") == 0
else qt.Qt.Checked
147 self.scriptedEffect.setParameter(
"ApplyToAllVisibleSegments", applyToAllVisibleSegments)
151 self.scriptedEffect.setParameter(
"ShellMode", INSIDE_SURFACE)
155 self.scriptedEffect.setParameter(
"ShellMode", MEDIAL_SURFACE)
159 self.scriptedEffect.setParameter(
"ShellMode", OUTSIDE_SURFACE)
162 selectedSegmentLabelmapSpacing = [1.0, 1.0, 1.0]
163 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
164 if selectedSegmentLabelmap:
165 selectedSegmentLabelmapSpacing = selectedSegmentLabelmap.GetSpacing()
168 shellThicknessMM = [abs((shellThicknessPixel[i]) * selectedSegmentLabelmapSpacing[i])
for i
in range(3)]
170 if shellThicknessMM[i] > 0:
171 shellThicknessMM[i] = round(shellThicknessMM[i], max(int(-math.floor(math.log10(shellThicknessMM[i]))), 1))
172 return shellThicknessMM
175 slicer.util.showStatusMessage(msg, timeoutMsec)
176 slicer.app.processEvents()
180 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
181 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
185 thresh = vtk.vtkImageThreshold()
186 thresh.SetInputData(selectedSegmentLabelmap)
187 thresh.ThresholdByLower(0)
188 thresh.SetInValue(backgroundValue)
189 thresh.SetOutValue(labelValue)
190 thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
192 shellMode = self.scriptedEffect.parameter(
"ShellMode")
193 shellThicknessMM = abs(self.scriptedEffect.doubleParameter(
"ShellThicknessMm"))
195 margin = vtkITK.vtkITKImageMargin()
196 margin.SetInputConnection(thresh.GetOutputPort())
197 margin.CalculateMarginInMMOn()
199 spacing = selectedSegmentLabelmap.GetSpacing()
200 voxelDiameter = min(selectedSegmentLabelmap.GetSpacing())
201 if shellMode == MEDIAL_SURFACE:
202 margin.SetOuterMarginMM(0.5 * shellThicknessMM)
203 margin.SetInnerMarginMM(-0.5 * shellThicknessMM + 0.5 * voxelDiameter)
204 elif shellMode == INSIDE_SURFACE:
205 margin.SetOuterMarginMM(shellThicknessMM + 0.1 * voxelDiameter)
206 margin.SetInnerMarginMM(0.0 + 0.1 * voxelDiameter)
207 elif shellMode == OUTSIDE_SURFACE:
208 margin.SetOuterMarginMM(0.0)
209 margin.SetInnerMarginMM(-shellThicknessMM + voxelDiameter)
211 modifierLabelmap.DeepCopy(margin.GetOutput())
214 modifierLabelmap.ShallowCopy(margin.GetOutput())
217 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
221 if not self.scriptedEffect.confirmCurrentSegmentVisible():
226 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
227 self.scriptedEffect.saveStateForUndo()
229 applyToAllVisibleSegments = int(self.scriptedEffect.parameter(
"ApplyToAllVisibleSegments")) != 0 \
230 if self.scriptedEffect.parameter(
"ApplyToAllVisibleSegments")
else False 232 if applyToAllVisibleSegments:
234 inputSegmentIDs = vtk.vtkStringArray()
235 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
236 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(inputSegmentIDs)
237 segmentEditorWidget = slicer.modules.segmenteditor.widgetRepresentation().self().editor
238 segmentEditorNode = segmentEditorWidget.mrmlSegmentEditorNode()
240 selectedStartSegmentID = segmentEditorNode.GetSelectedSegmentID()
241 if inputSegmentIDs.GetNumberOfValues() == 0:
242 logging.info(
"Hollow operation skipped: there are no visible segments.")
245 for index
in range(inputSegmentIDs.GetNumberOfValues()):
246 segmentID = inputSegmentIDs.GetValue(index)
247 self.
showStatusMessage(f
'Processing {segmentationNode.GetSegmentation().GetSegment(segmentID).GetName()}...')
248 segmentEditorNode.SetSelectedSegmentID(segmentID)
251 segmentEditorNode.SetSelectedSegmentID(selectedStartSegmentID)
256 qt.QApplication.restoreOverrideCursor()
259 INSIDE_SURFACE =
'INSIDE_SURFACE' 260 MEDIAL_SURFACE =
'MEDIAL_SURFACE' 261 OUTSIDE_SURFACE =
'OUTSIDE_SURFACE' def __init__(self, scriptedEffect)
def insideSurfaceModeToggled(self, toggled)
def showStatusMessage(self, msg, timeoutMsec=500)
outsideSurfaceOptionRadioButton
def setupOptionsFrame(self)
def setMRMLDefaults(self)
def createCursor(self, widget)
def getShellThicknessMM(self)
def processHollowing(self)
def getShellThicknessPixel(self)
applyToAllVisibleSegmentsCheckBox
def outsideSurfaceModeToggled(self, toggled)
def medialSurfaceModeToggled(self, toggled)
applyToAllVisibleSegmentsLabel
insideSurfaceOptionRadioButton
medialSurfaceOptionRadioButton
def updateMRMLFromGUI(self)
def updateGUIFromMRML(self)