2 import vtk, qt, ctk, slicer
4 from SegmentEditorEffects
import *
7 """ LogicalEffect is an MorphologyEffect to erode a layer of pixels from a segment 11 scriptedEffect.name =
'Logical operators' 13 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
16 import qSlicerSegmentationsEditorEffectsPythonQt
as effects
17 clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(
None)
18 clonedEffect.setPythonSource(__file__.replace(
'\\',
'/'))
22 iconPath = os.path.join(os.path.dirname(__file__),
'Resources/Icons/Logical.png')
23 if os.path.exists(iconPath):
24 return qt.QIcon(iconPath)
28 return """<html>Apply logical operators or combine segments<br>. Available operations:<p> 29 <ul style="margin: 0"> 30 <li><b>Copy:</b> replace the selected segment by the modifier segment.</li> 31 <li><b>Add:</b> add modifier segment to current segment.</li> 32 <li><b>Subtract:</b> subtract region of modifier segment from the selected segment.</li> 33 <li><b>Intersect:</b> only keeps those regions in the select segment that are common with the modifier segment.</li> 34 <li><b>Invert:</b> inverts selected segment.</li> 35 <li><b>Clear:</b> clears selected segment.</li> 36 <li><b>Fill:</b> completely fills selected segment.</li> 38 <b>Selected segment:</b> segment selected in the segment list - above. <b>Modifier segment:</b> segment chosen in segment list in effect options - below. 51 self.
methodSelectorComboBox.setToolTip(
'Click <dfn>Show details</dfn> link above for description of operations.')
54 self.
bypassMaskingCheckBox.setToolTip(
"Ignore all masking options and only modify the selected segment.")
58 self.
applyButton.objectName = self.__class__.__name__ +
'Apply' 60 operationFrame = qt.QHBoxLayout()
64 self.
marginSizeMmLabel = self.scriptedEffect.addLabeledOptionsWidget(
"Operation:", operationFrame)
76 self.
modifierSegmentSelector.setToolTip(
'Contents of this segment will be used for modifying the selected segment. This segment itself will not be changed.')
86 return slicer.util.mainWindow().cursor
89 self.scriptedEffect.setParameterDefault(
"Operation", LOGICAL_COPY)
90 self.scriptedEffect.setParameterDefault(
"ModifierSegmentID",
"")
91 self.scriptedEffect.setParameterDefault(
"BypassMasking", 1)
94 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
95 if not segmentationNode:
97 if not self.scriptedEffect.parameterDefined(
"ModifierSegmentID"):
100 modifierSegmentIDs = self.scriptedEffect.parameter(
"ModifierSegmentID").split(
';')
101 if not modifierSegmentIDs:
103 return modifierSegmentIDs[0]
106 operation = self.scriptedEffect.parameter(
"Operation")
113 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
123 if operation == LOGICAL_COPY:
125 elif operation == LOGICAL_UNION:
127 elif operation == LOGICAL_SUBTRACT:
129 elif operation == LOGICAL_INTERSECT:
134 if modifierSegmentRequired
and not modifierSegmentID:
135 self.
applyButton.setToolTip(
"Please select a modifier segment in the list below.")
141 bypassMasking = qt.Qt.Unchecked
if self.scriptedEffect.integerParameter(
"BypassMasking") == 0
else qt.Qt.Checked
149 self.scriptedEffect.setParameter(
"Operation", operation)
152 self.scriptedEffect.setParameter(
"BypassMasking", bypassMasking)
155 self.scriptedEffect.setParameter(
"ModifierSegmentID", modifierSegmentIDs)
158 import vtkSegmentationCorePython
as vtkSegmentationCore
162 inverter = vtk.vtkImageThreshold()
163 inverter.SetInputData(modifierLabelmap)
164 inverter.SetInValue(fillValue)
165 inverter.SetOutValue(eraseValue)
166 inverter.ReplaceInOn()
167 inverter.ThresholdByLower(0)
168 inverter.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
171 invertedModifierLabelmap = slicer.vtkOrientedImageData()
172 invertedModifierLabelmap.ShallowCopy(inverter.GetOutput())
173 imageToWorldMatrix = vtk.vtkMatrix4x4()
174 modifierLabelmap.GetImageToWorldMatrix(imageToWorldMatrix)
175 invertedModifierLabelmap.SetGeometryFromImageToWorldMatrix(imageToWorldMatrix)
176 return invertedModifierLabelmap
180 if not self.scriptedEffect.confirmCurrentSegmentVisible():
183 import vtkSegmentationCorePython
as vtkSegmentationCore
185 self.scriptedEffect.saveStateForUndo()
187 import vtkSegmentationCorePython
as vtkSegmentationCore
191 operation = self.scriptedEffect.parameter(
"Operation")
192 bypassMasking = (self.scriptedEffect.integerParameter(
"BypassMasking") != 0)
194 selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
196 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
197 segmentation = segmentationNode.GetSegmentation()
203 if not modifierSegmentID:
204 logging.error(
"Operation {0} requires a selected modifier segment".format(operation))
206 modifierSegment = segmentation.GetSegment(modifierSegmentID)
207 modifierSegmentLabelmap = slicer.vtkOrientedImageData()
208 segmentationNode.GetBinaryLabelmapRepresentation(modifierSegmentID, modifierSegmentLabelmap)
211 commonGeometryString = segmentationNode.GetSegmentation().DetermineCommonLabelmapGeometry(
212 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS,
None)
213 if not commonGeometryString:
214 logging.info(
"Logical operation skipped: all segments are empty")
216 commonGeometryImage = slicer.vtkOrientedImageData()
217 vtkSegmentationCore.vtkSegmentationConverter.DeserializeImageGeometry(commonGeometryString, commonGeometryImage,
False)
221 if not vtkSegmentationCore.vtkOrientedImageDataResample.DoGeometriesMatch(commonGeometryImage, modifierSegmentLabelmap):
222 modifierSegmentLabelmap_CommonGeometry = slicer.vtkOrientedImageData()
223 vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
224 modifierSegmentLabelmap, commonGeometryImage, modifierSegmentLabelmap_CommonGeometry,
228 modifierSegmentLabelmap = modifierSegmentLabelmap_CommonGeometry
230 if operation == LOGICAL_COPY:
231 self.scriptedEffect.modifySelectedSegmentByLabelmap(
232 modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, bypassMasking)
233 elif operation == LOGICAL_UNION:
234 self.scriptedEffect.modifySelectedSegmentByLabelmap(
235 modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd, bypassMasking)
236 elif operation == LOGICAL_SUBTRACT:
237 self.scriptedEffect.modifySelectedSegmentByLabelmap(
238 modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove, bypassMasking)
239 elif operation == LOGICAL_INTERSECT:
240 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
241 intersectionLabelmap = slicer.vtkOrientedImageData()
242 vtkSegmentationCore.vtkOrientedImageDataResample.MergeImage(
243 selectedSegmentLabelmap, modifierSegmentLabelmap, intersectionLabelmap,
244 vtkSegmentationCore.vtkOrientedImageDataResample.OPERATION_MINIMUM, selectedSegmentLabelmap.GetExtent())
245 selectedSegmentLabelmapExtent = selectedSegmentLabelmap.GetExtent()
246 modifierSegmentLabelmapExtent = modifierSegmentLabelmap.GetExtent()
247 commonExtent = [max(selectedSegmentLabelmapExtent[0], modifierSegmentLabelmapExtent[0]),
248 min(selectedSegmentLabelmapExtent[1], modifierSegmentLabelmapExtent[1]),
249 max(selectedSegmentLabelmapExtent[2], modifierSegmentLabelmapExtent[2]),
250 min(selectedSegmentLabelmapExtent[3], modifierSegmentLabelmapExtent[3]),
251 max(selectedSegmentLabelmapExtent[4], modifierSegmentLabelmapExtent[4]),
252 min(selectedSegmentLabelmapExtent[5], modifierSegmentLabelmapExtent[5])]
253 self.scriptedEffect.modifySelectedSegmentByLabelmap(
254 intersectionLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, commonExtent, bypassMasking)
256 elif operation == LOGICAL_INVERT:
257 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
259 self.scriptedEffect.modifySelectedSegmentByLabelmap(
260 invertedSelectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, bypassMasking)
262 elif operation == LOGICAL_CLEAR
or operation == LOGICAL_FILL:
263 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
264 vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(selectedSegmentLabelmap, 1
if operation == LOGICAL_FILL
else 0, selectedSegmentLabelmap.GetExtent())
265 self.scriptedEffect.modifySelectedSegmentByLabelmap(
266 selectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, bypassMasking)
269 logging.error(
"Unknown operation: {0}".format(operation))
271 LOGICAL_COPY =
'COPY' 272 LOGICAL_UNION =
'UNION' 273 LOGICAL_INTERSECT =
'INTERSECT' 274 LOGICAL_SUBTRACT =
'SUBTRACT' 275 LOGICAL_INVERT =
'INVERT' 276 LOGICAL_CLEAR =
'CLEAR' 277 LOGICAL_FILL =
'FILL'
modifierSegmentSelectorLabel
operationsRequireModifierSegment
def modifierSegmentID(self)
def createCursor(self, widget)
def setMRMLDefaults(self)
def updateMRMLFromGUI(self)
def updateGUIFromMRML(self)
def getInvertedBinaryLabelmap(self, modifierLabelmap)
def setupOptionsFrame(self)
def __init__(self, scriptedEffect)