2 import vtk, qt, ctk, slicer
4 from SegmentEditorEffects
import *
8 """ Operate on connected components (islands) within a segment 12 scriptedEffect.name =
'Islands' 13 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
17 import qSlicerSegmentationsEditorEffectsPythonQt
as effects
18 clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(
None)
19 clonedEffect.setPythonSource(__file__.replace(
'\\',
'/'))
23 iconPath = os.path.join(os.path.dirname(__file__),
'Resources/Icons/Islands.png')
24 if os.path.exists(iconPath):
25 return qt.QIcon(iconPath)
29 return "Edit islands (connected components) in a segment." 36 "Keep only the largest island in selected segment, remove all other islands in the segment.")
42 "Click on an island in a slice viewer to keep that island and remove all other islands in selected segment.")
48 "Remove all islands from the selected segment that are smaller than the specified minimum size.")
54 "Click on an island to remove it from selected segment.")
60 "Click on a region to add it to selected segment.")
66 "Create a new segment for each island of selected segment. Islands smaller than minimum size will be removed. "+
67 "Segments will be ordered by island size.")
71 operationLayout = qt.QGridLayout()
80 self.scriptedEffect.addOptionsWidget(operationLayout)
90 self.
applyButton.objectName = self.__class__.__name__ +
'Apply' 91 self.scriptedEffect.addOptionsWidget(self.
applyButton)
94 operationRadioButton.connect(
'toggled(bool)',
104 self.scriptedEffect.setParameter(
"Operation", operationName)
107 operationName = self.scriptedEffect.parameter(
"Operation")
108 return operationName
in [KEEP_SELECTED_ISLAND, REMOVE_SELECTED_ISLAND, ADD_SELECTED_ISLAND]
111 operationName = self.scriptedEffect.parameter(
"Operation")
112 minimumSize = self.scriptedEffect.integerParameter(
"MinimumSize")
113 if operationName == KEEP_LARGEST_ISLAND:
114 self.
splitSegments(minimumSize = minimumSize, maxNumberOfSegments = 1)
115 elif operationName == REMOVE_SMALL_ISLANDS:
117 elif operationName == SPLIT_ISLANDS_TO_SEGMENTS:
120 def splitSegments(self, minimumSize = 0, maxNumberOfSegments = 0, split = True):
122 minimumSize: if 0 then it means that all islands are kept, regardless of size 123 maxNumberOfSegments: if 0 then it means that all islands are kept, regardless of how many 126 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
128 self.scriptedEffect.saveStateForUndo()
131 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
133 castIn = vtk.vtkImageCast()
134 castIn.SetInputData(selectedSegmentLabelmap)
135 castIn.SetOutputScalarTypeToUnsignedInt()
139 islandMath = vtkITK.vtkITKIslandMath()
140 islandMath.SetInputConnection(castIn.GetOutputPort())
141 islandMath.SetFullyConnected(
False)
142 islandMath.SetMinimumSize(minimumSize)
148 thresh = vtk.vtkImageThreshold()
150 thresh.ThresholdBetween(1, 1)
152 if maxNumberOfSegments != 0:
153 thresh.ThresholdBetween(1, maxNumberOfSegments)
155 thresh.ThresholdByUpper(1)
157 thresh.SetInputData(islandMath.GetOutput())
158 thresh.SetOutValue(backgroundValue)
159 thresh.SetInValue(labelValue)
160 thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
163 import vtkSegmentationCorePython
as vtkSegmentationCore
164 largestIslandImage = vtkSegmentationCore.vtkOrientedImageData()
165 largestIslandImage.ShallowCopy(thresh.GetOutput())
166 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
167 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
168 largestIslandImage.SetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
170 if split
and (maxNumberOfSegments != 1):
172 thresh2 = vtk.vtkImageThreshold()
174 if maxNumberOfSegments != 0:
175 thresh2.ThresholdBetween(2, maxNumberOfSegments)
177 thresh2.ThresholdByUpper(2)
178 thresh2.SetInputData(islandMath.GetOutput())
179 thresh2.SetOutValue(backgroundValue)
180 thresh2.ReplaceInOff()
183 islandCount = islandMath.GetNumberOfIslands()
184 islandOrigCount = islandMath.GetOriginalNumberOfIslands()
185 ignoredIslands = islandOrigCount - islandCount
186 logging.info(
"%d islands created (%d ignored)" % (islandCount, ignoredIslands) )
189 import vtkSegmentationCorePython
as vtkSegmentationCore
190 multiLabelImage = vtkSegmentationCore.vtkOrientedImageData()
191 multiLabelImage.DeepCopy(thresh2.GetOutput())
192 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
193 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
194 multiLabelImage.SetGeometryFromImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
197 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
198 selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
199 selectedSegmentIndex = segmentationNode.GetSegmentation().GetSegmentIndex(selectedSegmentID)
200 insertBeforeSegmentID = segmentationNode.GetSegmentation().GetNthSegmentID(selectedSegmentIndex + 1)
201 selectedSegmentName = segmentationNode.GetSegmentation().GetSegment(selectedSegmentID).GetName()
202 slicer.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode( \
203 multiLabelImage, segmentationNode, selectedSegmentName+
" -", insertBeforeSegmentID )
205 self.scriptedEffect.modifySelectedSegmentByLabelmap(largestIslandImage, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
207 qt.QApplication.restoreOverrideCursor()
210 import vtkSegmentationCorePython
as vtkSegmentationCore
219 if viewWidget.className() !=
"qMRMLSliceWidget":
222 if eventId != vtk.vtkCommand.LeftButtonPressEvent:
228 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
229 visibleSegmentIds = vtk.vtkStringArray()
230 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(visibleSegmentIds)
231 if visibleSegmentIds.GetNumberOfValues() == 0:
232 logging.info(
"Smoothing operation skipped: there are no visible segments")
235 self.scriptedEffect.saveStateForUndo()
238 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
240 operationName = self.scriptedEffect.parameter(
"Operation")
242 if operationName == ADD_SELECTED_ISLAND:
243 inputLabelImage = vtkSegmentationCore.vtkOrientedImageData()
244 if not segmentationNode.GenerateMergedLabelmapForAllSegments(inputLabelImage,
245 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS_PADDED,
246 None, visibleSegmentIds):
247 logging.error(
'Failed to apply smoothing: cannot get list of visible segments')
248 qt.QApplication.restoreOverrideCursor()
251 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
255 thresh = vtk.vtkImageThreshold()
256 thresh.SetInputData(selectedSegmentLabelmap)
257 thresh.ThresholdByLower(0)
258 thresh.SetInValue(backgroundValue)
259 thresh.SetOutValue(labelValue)
260 thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
263 import vtkSegmentationCorePython
as vtkSegmentationCore
264 inputLabelImage = vtkSegmentationCore.vtkOrientedImageData()
265 inputLabelImage.ShallowCopy(thresh.GetOutput())
266 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
267 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
268 inputLabelImage.SetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
270 xy = callerInteractor.GetEventPosition()
271 ijk = self.xyToIjk(xy, viewWidget, inputLabelImage)
272 pixelValue = inputLabelImage.GetScalarComponentAsFloat(ijk[0], ijk[1], ijk[2], 0)
276 floodFillingFilter = vtk.vtkImageThresholdConnectivity()
277 floodFillingFilter.SetInputData(inputLabelImage)
278 seedPoints = vtk.vtkPoints()
279 origin = inputLabelImage.GetOrigin()
280 spacing = inputLabelImage.GetSpacing()
281 seedPoints.InsertNextPoint(origin[0]+ijk[0]*spacing[0], origin[1]+ijk[1]*spacing[1], origin[2]+ijk[2]*spacing[2])
282 floodFillingFilter.SetSeedPoints(seedPoints)
283 floodFillingFilter.ThresholdBetween(pixelValue, pixelValue)
285 if operationName == ADD_SELECTED_ISLAND:
286 floodFillingFilter.SetInValue(1)
287 floodFillingFilter.SetOutValue(0)
288 floodFillingFilter.Update()
289 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
290 modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput())
291 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd)
293 elif pixelValue != 0:
295 if operationName == KEEP_SELECTED_ISLAND:
296 floodFillingFilter.SetInValue(1)
297 floodFillingFilter.SetOutValue(0)
299 floodFillingFilter.SetInValue(1)
300 floodFillingFilter.SetOutValue(0)
302 floodFillingFilter.Update()
303 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
304 modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput())
306 if operationName == KEEP_SELECTED_ISLAND:
307 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
309 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove)
312 logging.error(
'apply: Failed to threshold master volume!')
314 qt.QApplication.restoreOverrideCursor()
322 self.scriptedEffect.setParameterDefault(
"Operation", KEEP_LARGEST_ISLAND)
323 self.scriptedEffect.setParameterDefault(
"MinimumSize", 1000)
327 operationRadioButton.blockSignals(
True)
328 operationName = self.scriptedEffect.parameter(
"Operation")
330 currentOperationRadioButton.setChecked(
True)
332 operationRadioButton.blockSignals(
False)
335 self.
applyButton.setEnabled(
not segmentSelectionRequired)
336 if segmentSelectionRequired:
337 self.
applyButton.setToolTip(
"Click in a slice viewer to select segment")
345 showMinimumSizeOption = (operationName
in [REMOVE_SMALL_ISLANDS, SPLIT_ISLANDS_TO_SEGMENTS])
357 KEEP_LARGEST_ISLAND =
'KEEP_LARGEST_ISLAND' 358 KEEP_SELECTED_ISLAND =
'KEEP_SELECTED_ISLAND' 359 REMOVE_SMALL_ISLANDS =
'REMOVE_SMALL_ISLANDS' 360 REMOVE_SELECTED_ISLAND =
'REMOVE_SELECTED_ISLAND' 361 ADD_SELECTED_ISLAND =
'ADD_SELECTED_ISLAND' 362 SPLIT_ISLANDS_TO_SEGMENTS =
'SPLIT_ISLANDS_TO_SEGMENTS' removeSelectedOptionRadioButton
def currentOperationRequiresSegmentSelection(self)
addSelectedOptionRadioButton
def setupOptionsFrame(self)
def setMRMLDefaults(self)
keepSelectedOptionRadioButton
keepLargestOptionRadioButton
def __init__(self, scriptedEffect)
splitAllOptionRadioButton
removeSmallOptionRadioButton
def updateGUIFromMRML(self)
def updateMRMLFromGUI(self)
def onOperationSelectionChanged(self, operationName, toggle)
def processInteractionEvents(self, callerInteractor, eventId, viewWidget)
def splitSegments(self, minimumSize=0, maxNumberOfSegments=0, split=True)
def processViewNodeEvents(self, callerViewNode, eventId, viewWidget)