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)
83 self.
minimumSizeSpinBox.setToolTip(
"Islands consisting of less voxels than this minimum size, will be deleted.")
91 self.
applyButton.objectName = self.__class__.__name__ +
'Apply' 92 self.scriptedEffect.addOptionsWidget(self.
applyButton)
95 operationRadioButton.connect(
'toggled(bool)',
105 self.scriptedEffect.setParameter(
"Operation", operationName)
108 operationName = self.scriptedEffect.parameter(
"Operation")
109 return operationName
in [KEEP_SELECTED_ISLAND, REMOVE_SELECTED_ISLAND, ADD_SELECTED_ISLAND]
112 operationName = self.scriptedEffect.parameter(
"Operation")
113 minimumSize = self.scriptedEffect.integerParameter(
"MinimumSize")
114 if operationName == KEEP_LARGEST_ISLAND:
115 self.
splitSegments(minimumSize = minimumSize, maxNumberOfSegments = 1)
116 elif operationName == REMOVE_SMALL_ISLANDS:
118 elif operationName == SPLIT_ISLANDS_TO_SEGMENTS:
121 def splitSegments(self, minimumSize = 0, maxNumberOfSegments = 0, split = True):
123 minimumSize: if 0 then it means that all islands are kept, regardless of size 124 maxNumberOfSegments: if 0 then it means that all islands are kept, regardless of how many 127 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
129 self.scriptedEffect.saveStateForUndo()
132 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
134 castIn = vtk.vtkImageCast()
135 castIn.SetInputData(selectedSegmentLabelmap)
136 castIn.SetOutputScalarTypeToUnsignedInt()
140 islandMath = vtkITK.vtkITKIslandMath()
141 islandMath.SetInputConnection(castIn.GetOutputPort())
142 islandMath.SetFullyConnected(
False)
143 islandMath.SetMinimumSize(minimumSize)
149 thresh = vtk.vtkImageThreshold()
151 thresh.ThresholdBetween(1, 1)
153 if maxNumberOfSegments != 0:
154 thresh.ThresholdBetween(1, maxNumberOfSegments)
156 thresh.ThresholdByUpper(1)
158 thresh.SetInputData(islandMath.GetOutput())
159 thresh.SetOutValue(backgroundValue)
160 thresh.SetInValue(labelValue)
161 thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
164 import vtkSegmentationCorePython
as vtkSegmentationCore
165 largestIslandImage = vtkSegmentationCore.vtkOrientedImageData()
166 largestIslandImage.ShallowCopy(thresh.GetOutput())
167 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
168 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
169 largestIslandImage.SetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
171 if split
and (maxNumberOfSegments != 1):
173 thresh2 = vtk.vtkImageThreshold()
175 if maxNumberOfSegments != 0:
176 thresh2.ThresholdBetween(2, maxNumberOfSegments)
178 thresh2.ThresholdByUpper(2)
179 thresh2.SetInputData(islandMath.GetOutput())
180 thresh2.SetOutValue(backgroundValue)
181 thresh2.ReplaceInOff()
184 islandCount = islandMath.GetNumberOfIslands()
185 islandOrigCount = islandMath.GetOriginalNumberOfIslands()
186 ignoredIslands = islandOrigCount - islandCount
187 logging.info(
"%d islands created (%d ignored)" % (islandCount, ignoredIslands) )
190 import vtkSegmentationCorePython
as vtkSegmentationCore
191 multiLabelImage = vtkSegmentationCore.vtkOrientedImageData()
192 multiLabelImage.DeepCopy(thresh2.GetOutput())
193 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
194 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
195 multiLabelImage.SetGeometryFromImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
198 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
199 selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
200 selectedSegmentIndex = segmentationNode.GetSegmentation().GetSegmentIndex(selectedSegmentID)
201 insertBeforeSegmentID = segmentationNode.GetSegmentation().GetNthSegmentID(selectedSegmentIndex + 1)
202 selectedSegmentName = segmentationNode.GetSegmentation().GetSegment(selectedSegmentID).GetName()
203 slicer.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode( \
204 multiLabelImage, segmentationNode, selectedSegmentName+
" -", insertBeforeSegmentID )
206 self.scriptedEffect.modifySelectedSegmentByLabelmap(largestIslandImage, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
208 qt.QApplication.restoreOverrideCursor()
211 import vtkSegmentationCorePython
as vtkSegmentationCore
220 if viewWidget.className() !=
"qMRMLSliceWidget":
223 if eventId != vtk.vtkCommand.LeftButtonPressEvent:
229 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
230 visibleSegmentIds = vtk.vtkStringArray()
231 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(visibleSegmentIds)
232 if visibleSegmentIds.GetNumberOfValues() == 0:
233 logging.info(
"Smoothing operation skipped: there are no visible segments")
236 self.scriptedEffect.saveStateForUndo()
239 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
241 operationName = self.scriptedEffect.parameter(
"Operation")
243 if operationName == ADD_SELECTED_ISLAND:
244 inputLabelImage = vtkSegmentationCore.vtkOrientedImageData()
245 if not segmentationNode.GenerateMergedLabelmapForAllSegments(inputLabelImage,
246 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS_PADDED,
247 None, visibleSegmentIds):
248 logging.error(
'Failed to apply smoothing: cannot get list of visible segments')
249 qt.QApplication.restoreOverrideCursor()
252 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
256 thresh = vtk.vtkImageThreshold()
257 thresh.SetInputData(selectedSegmentLabelmap)
258 thresh.ThresholdByLower(0)
259 thresh.SetInValue(backgroundValue)
260 thresh.SetOutValue(labelValue)
261 thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
264 import vtkSegmentationCorePython
as vtkSegmentationCore
265 inputLabelImage = vtkSegmentationCore.vtkOrientedImageData()
266 inputLabelImage.ShallowCopy(thresh.GetOutput())
267 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
268 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
269 inputLabelImage.SetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
271 xy = callerInteractor.GetEventPosition()
272 ijk = self.xyToIjk(xy, viewWidget, inputLabelImage)
273 pixelValue = inputLabelImage.GetScalarComponentAsFloat(ijk[0], ijk[1], ijk[2], 0)
277 floodFillingFilter = vtk.vtkImageThresholdConnectivity()
278 floodFillingFilter.SetInputData(inputLabelImage)
279 seedPoints = vtk.vtkPoints()
280 origin = inputLabelImage.GetOrigin()
281 spacing = inputLabelImage.GetSpacing()
282 seedPoints.InsertNextPoint(origin[0]+ijk[0]*spacing[0], origin[1]+ijk[1]*spacing[1], origin[2]+ijk[2]*spacing[2])
283 floodFillingFilter.SetSeedPoints(seedPoints)
284 floodFillingFilter.ThresholdBetween(pixelValue, pixelValue)
286 if operationName == ADD_SELECTED_ISLAND:
287 floodFillingFilter.SetInValue(1)
288 floodFillingFilter.SetOutValue(0)
289 floodFillingFilter.Update()
290 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
291 modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput())
292 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd)
294 elif pixelValue != 0:
296 if operationName == KEEP_SELECTED_ISLAND:
297 floodFillingFilter.SetInValue(1)
298 floodFillingFilter.SetOutValue(0)
300 floodFillingFilter.SetInValue(1)
301 floodFillingFilter.SetOutValue(0)
303 floodFillingFilter.Update()
304 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
305 modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput())
307 if operationName == KEEP_SELECTED_ISLAND:
308 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
310 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove)
313 logging.error(
'apply: Failed to threshold master volume!')
315 qt.QApplication.restoreOverrideCursor()
323 self.scriptedEffect.setParameterDefault(
"Operation", KEEP_LARGEST_ISLAND)
324 self.scriptedEffect.setParameterDefault(
"MinimumSize", 1000)
328 operationRadioButton.blockSignals(
True)
329 operationName = self.scriptedEffect.parameter(
"Operation")
331 currentOperationRadioButton.setChecked(
True)
333 operationRadioButton.blockSignals(
False)
336 self.
applyButton.setEnabled(
not segmentSelectionRequired)
337 if segmentSelectionRequired:
338 self.
applyButton.setToolTip(
"Click in a slice viewer to select segment")
346 showMinimumSizeOption = (operationName
in [REMOVE_SMALL_ISLANDS, SPLIT_ISLANDS_TO_SEGMENTS])
358 KEEP_LARGEST_ISLAND =
'KEEP_LARGEST_ISLAND' 359 KEEP_SELECTED_ISLAND =
'KEEP_SELECTED_ISLAND' 360 REMOVE_SMALL_ISLANDS =
'REMOVE_SMALL_ISLANDS' 361 REMOVE_SELECTED_ISLAND =
'REMOVE_SELECTED_ISLAND' 362 ADD_SELECTED_ISLAND =
'ADD_SELECTED_ISLAND' 363 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)