9 from SegmentEditorEffects
import *
13 """ LogicalEffect is an MorphologyEffect to erode a layer of pixels from a segment 17 scriptedEffect.name =
'Logical operators' 19 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
22 import qSlicerSegmentationsEditorEffectsPythonQt
as effects
23 clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(
None)
24 clonedEffect.setPythonSource(__file__.replace(
'\\',
'/'))
28 iconPath = os.path.join(os.path.dirname(__file__),
'Resources/Icons/Logical.png')
29 if os.path.exists(iconPath):
30 return qt.QIcon(iconPath)
34 return """<html>Apply logical operators or combine segments<br>. Available operations:<p> 35 <ul style="margin: 0"> 36 <li><b>Copy:</b> replace the selected segment by the modifier segment.</li> 37 <li><b>Add:</b> add modifier segment to current segment.</li> 38 <li><b>Subtract:</b> subtract region of modifier segment from the selected segment.</li> 39 <li><b>Intersect:</b> only keeps those regions in the select segment that are common with the modifier segment.</li> 40 <li><b>Invert:</b> inverts selected segment.</li> 41 <li><b>Clear:</b> clears selected segment.</li> 42 <li><b>Fill:</b> completely fills selected segment.</li> 44 <b>Selected segment:</b> segment selected in the segment list - above. <b>Modifier segment:</b> segment chosen in segment list in effect options - below. 57 self.
methodSelectorComboBox.setToolTip(
'Click <dfn>Show details</dfn> link above for description of operations.')
60 self.
bypassMaskingCheckBox.setToolTip(
"Ignore all masking options and only modify the selected segment.")
64 self.
applyButton.objectName = self.__class__.__name__ +
'Apply' 66 operationFrame = qt.QHBoxLayout()
70 self.
marginSizeMmLabel = self.scriptedEffect.addLabeledOptionsWidget(
"Operation:", operationFrame)
82 self.
modifierSegmentSelector.setToolTip(
'Contents of this segment will be used for modifying the selected segment. This segment itself will not be changed.')
92 return slicer.util.mainWindow().cursor
95 self.scriptedEffect.setParameterDefault(
"Operation", LOGICAL_COPY)
96 self.scriptedEffect.setParameterDefault(
"ModifierSegmentID",
"")
97 self.scriptedEffect.setParameterDefault(
"BypassMasking", 1)
100 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
101 if not segmentationNode:
103 if not self.scriptedEffect.parameterDefined(
"ModifierSegmentID"):
106 modifierSegmentIDs = self.scriptedEffect.parameter(
"ModifierSegmentID").split(
';')
107 if not modifierSegmentIDs:
109 return modifierSegmentIDs[0]
112 operation = self.scriptedEffect.parameter(
"Operation")
119 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
129 if operation == LOGICAL_COPY:
131 elif operation == LOGICAL_UNION:
133 elif operation == LOGICAL_SUBTRACT:
135 elif operation == LOGICAL_INTERSECT:
140 if modifierSegmentRequired
and not modifierSegmentID:
141 self.
applyButton.setToolTip(
"Please select a modifier segment in the list below.")
147 bypassMasking = qt.Qt.Unchecked
if self.scriptedEffect.integerParameter(
"BypassMasking") == 0
else qt.Qt.Checked
155 self.scriptedEffect.setParameter(
"Operation", operation)
158 self.scriptedEffect.setParameter(
"BypassMasking", bypassMasking)
161 self.scriptedEffect.setParameter(
"ModifierSegmentID", modifierSegmentIDs)
166 inverter = vtk.vtkImageThreshold()
167 inverter.SetInputData(modifierLabelmap)
168 inverter.SetInValue(fillValue)
169 inverter.SetOutValue(eraseValue)
170 inverter.ReplaceInOn()
171 inverter.ThresholdByLower(0)
172 inverter.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
175 invertedModifierLabelmap = slicer.vtkOrientedImageData()
176 invertedModifierLabelmap.ShallowCopy(inverter.GetOutput())
177 imageToWorldMatrix = vtk.vtkMatrix4x4()
178 modifierLabelmap.GetImageToWorldMatrix(imageToWorldMatrix)
179 invertedModifierLabelmap.SetGeometryFromImageToWorldMatrix(imageToWorldMatrix)
180 return invertedModifierLabelmap
184 if not self.scriptedEffect.confirmCurrentSegmentVisible():
187 import vtkSegmentationCorePython
as vtkSegmentationCore
189 self.scriptedEffect.saveStateForUndo()
193 operation = self.scriptedEffect.parameter(
"Operation")
194 bypassMasking = (self.scriptedEffect.integerParameter(
"BypassMasking") != 0)
196 selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
198 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
199 segmentation = segmentationNode.GetSegmentation()
205 if not modifierSegmentID:
206 logging.error(f
"Operation {operation} requires a selected modifier segment")
208 modifierSegment = segmentation.GetSegment(modifierSegmentID)
209 modifierSegmentLabelmap = slicer.vtkOrientedImageData()
210 segmentationNode.GetBinaryLabelmapRepresentation(modifierSegmentID, modifierSegmentLabelmap)
213 commonGeometryString = segmentationNode.GetSegmentation().DetermineCommonLabelmapGeometry(
214 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS,
None)
215 if not commonGeometryString:
216 logging.info(
"Logical operation skipped: all segments are empty")
218 commonGeometryImage = slicer.vtkOrientedImageData()
219 vtkSegmentationCore.vtkSegmentationConverter.DeserializeImageGeometry(commonGeometryString, commonGeometryImage,
False)
223 if not vtkSegmentationCore.vtkOrientedImageDataResample.DoGeometriesMatch(commonGeometryImage, modifierSegmentLabelmap):
224 modifierSegmentLabelmap_CommonGeometry = slicer.vtkOrientedImageData()
225 vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
226 modifierSegmentLabelmap, commonGeometryImage, modifierSegmentLabelmap_CommonGeometry,
230 modifierSegmentLabelmap = modifierSegmentLabelmap_CommonGeometry
232 if operation == LOGICAL_COPY:
233 self.scriptedEffect.modifySelectedSegmentByLabelmap(
234 modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, bypassMasking)
235 elif operation == LOGICAL_UNION:
236 self.scriptedEffect.modifySelectedSegmentByLabelmap(
237 modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd, bypassMasking)
238 elif operation == LOGICAL_SUBTRACT:
239 self.scriptedEffect.modifySelectedSegmentByLabelmap(
240 modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove, bypassMasking)
241 elif operation == LOGICAL_INTERSECT:
242 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
243 intersectionLabelmap = slicer.vtkOrientedImageData()
244 vtkSegmentationCore.vtkOrientedImageDataResample.MergeImage(
245 selectedSegmentLabelmap, modifierSegmentLabelmap, intersectionLabelmap,
246 vtkSegmentationCore.vtkOrientedImageDataResample.OPERATION_MINIMUM, selectedSegmentLabelmap.GetExtent())
247 selectedSegmentLabelmapExtent = selectedSegmentLabelmap.GetExtent()
248 modifierSegmentLabelmapExtent = modifierSegmentLabelmap.GetExtent()
249 commonExtent = [max(selectedSegmentLabelmapExtent[0], modifierSegmentLabelmapExtent[0]),
250 min(selectedSegmentLabelmapExtent[1], modifierSegmentLabelmapExtent[1]),
251 max(selectedSegmentLabelmapExtent[2], modifierSegmentLabelmapExtent[2]),
252 min(selectedSegmentLabelmapExtent[3], modifierSegmentLabelmapExtent[3]),
253 max(selectedSegmentLabelmapExtent[4], modifierSegmentLabelmapExtent[4]),
254 min(selectedSegmentLabelmapExtent[5], modifierSegmentLabelmapExtent[5])]
255 self.scriptedEffect.modifySelectedSegmentByLabelmap(
256 intersectionLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, commonExtent, bypassMasking)
258 elif operation == LOGICAL_INVERT:
259 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
261 self.scriptedEffect.modifySelectedSegmentByLabelmap(
262 invertedSelectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, bypassMasking)
264 elif operation == LOGICAL_CLEAR
or operation == LOGICAL_FILL:
265 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
266 vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(selectedSegmentLabelmap, 1
if operation == LOGICAL_FILL
else 0, selectedSegmentLabelmap.GetExtent())
267 self.scriptedEffect.modifySelectedSegmentByLabelmap(
268 selectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, bypassMasking)
271 logging.error(f
"Unknown operation: {operation}")
274 LOGICAL_COPY =
'COPY' 275 LOGICAL_UNION =
'UNION' 276 LOGICAL_INTERSECT =
'INTERSECT' 277 LOGICAL_SUBTRACT =
'SUBTRACT' 278 LOGICAL_INVERT =
'INVERT' 279 LOGICAL_CLEAR =
'CLEAR' 280 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)