10 from SegmentEditorEffects
import *
14 """ Operate on connected components (islands) within a segment 18 scriptedEffect.name =
'Islands' 19 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
23 import qSlicerSegmentationsEditorEffectsPythonQt
as effects
24 clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(
None)
25 clonedEffect.setPythonSource(__file__.replace(
'\\',
'/'))
29 iconPath = os.path.join(os.path.dirname(__file__),
'Resources/Icons/Islands.png')
30 if os.path.exists(iconPath):
31 return qt.QIcon(iconPath)
35 return """<html>Edit islands (connected components) in a segment<br>. To get more information 36 about each operation, hover the mouse over the option and wait for the tooltip to appear.</html>""" 43 "Keep only the largest island in selected segment, remove all other islands in the segment.")
49 "Click on an island in a slice view to keep that island and remove all other islands in selected segment.")
55 "Remove all islands from the selected segment that are smaller than the specified minimum size.")
61 "Click on an island in a slice view to remove it from selected segment.")
67 "Click on a region in a slice view to add it to selected segment.")
73 "Create a new segment for each island of selected segment. Islands smaller than minimum size will be removed. " +
74 "Segments will be ordered by island size.")
78 operationLayout = qt.QGridLayout()
87 self.scriptedEffect.addOptionsWidget(operationLayout)
90 self.
minimumSizeSpinBox.setToolTip(
"Islands consisting of less voxels than this minimum size, will be deleted.")
98 self.
applyButton.objectName = self.__class__.__name__ +
'Apply' 99 self.scriptedEffect.addOptionsWidget(self.
applyButton)
102 operationRadioButton.connect(
'toggled(bool)',
112 self.scriptedEffect.setParameter(
"Operation", operationName)
115 operationName = self.scriptedEffect.parameter(
"Operation")
116 return operationName
in [KEEP_SELECTED_ISLAND, REMOVE_SELECTED_ISLAND, ADD_SELECTED_ISLAND]
120 if not self.scriptedEffect.confirmCurrentSegmentVisible():
122 operationName = self.scriptedEffect.parameter(
"Operation")
123 minimumSize = self.scriptedEffect.integerParameter(
"MinimumSize")
124 if operationName == KEEP_LARGEST_ISLAND:
125 self.
splitSegments(minimumSize=minimumSize, maxNumberOfSegments=1)
126 elif operationName == REMOVE_SMALL_ISLANDS:
128 elif operationName == SPLIT_ISLANDS_TO_SEGMENTS:
133 minimumSize: if 0 then it means that all islands are kept, regardless of size 134 maxNumberOfSegments: if 0 then it means that all islands are kept, regardless of how many 137 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
139 self.scriptedEffect.saveStateForUndo()
142 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
144 castIn = vtk.vtkImageCast()
145 castIn.SetInputData(selectedSegmentLabelmap)
146 castIn.SetOutputScalarTypeToUnsignedInt()
150 islandMath = vtkITK.vtkITKIslandMath()
151 islandMath.SetInputConnection(castIn.GetOutputPort())
152 islandMath.SetFullyConnected(
False)
153 islandMath.SetMinimumSize(minimumSize)
156 islandImage = slicer.vtkOrientedImageData()
157 islandImage.ShallowCopy(islandMath.GetOutput())
158 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
159 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
160 islandImage.SetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
162 islandCount = islandMath.GetNumberOfIslands()
163 islandOrigCount = islandMath.GetOriginalNumberOfIslands()
164 ignoredIslands = islandOrigCount - islandCount
165 logging.info(
"%d islands created (%d ignored)" % (islandCount, ignoredIslands))
167 baseSegmentName =
"Label" 168 selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
169 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
170 with slicer.util.NodeModify(segmentationNode):
171 segmentation = segmentationNode.GetSegmentation()
172 selectedSegment = segmentation.GetSegment(selectedSegmentID)
173 selectedSegmentName = selectedSegment.GetName()
174 if selectedSegmentName
is not None and selectedSegmentName !=
"":
175 baseSegmentName = selectedSegmentName
177 labelValues = vtk.vtkIntArray()
178 slicer.vtkSlicerSegmentationsModuleLogic.GetAllLabelValues(labelValues, islandImage)
182 threshold = vtk.vtkImageThreshold()
183 threshold.SetInputData(selectedSegmentLabelmap)
184 threshold.ThresholdBetween(0, 0)
185 threshold.SetInValue(0)
186 threshold.SetOutValue(0)
188 emptyLabelmap = slicer.vtkOrientedImageData()
189 emptyLabelmap.ShallowCopy(threshold.GetOutput())
190 emptyLabelmap.CopyDirections(selectedSegmentLabelmap)
191 self.scriptedEffect.modifySegmentByLabelmap(segmentationNode, selectedSegmentID, emptyLabelmap,
192 slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
194 for i
in range(labelValues.GetNumberOfTuples()):
195 if (maxNumberOfSegments > 0
and i >= maxNumberOfSegments):
200 labelValue = int(labelValues.GetTuple1(i))
201 segment = selectedSegment
202 segmentID = selectedSegmentID
204 segment = slicer.vtkSegment()
205 name = baseSegmentName +
"_" + str(i + 1)
206 segment.SetName(name)
207 segment.AddRepresentation(slicer.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName(),
208 selectedSegment.GetRepresentation(slicer.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()))
209 segmentation.AddSegment(segment)
210 segmentID = segmentation.GetSegmentIdBySegment(segment)
211 segment.SetLabelValue(segmentation.GetUniqueLabelValueForSharedLabelmap(selectedSegmentID))
213 threshold = vtk.vtkImageThreshold()
214 threshold.SetInputData(islandMath.GetOutput())
215 if not split
and maxNumberOfSegments <= 0:
217 threshold.ThresholdByLower(0)
218 threshold.SetInValue(0)
219 threshold.SetOutValue(1)
222 threshold.ThresholdBetween(labelValue, labelValue)
223 threshold.SetInValue(1)
224 threshold.SetOutValue(0)
227 modificationMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd
229 modificationMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet
232 modifierImage = slicer.vtkOrientedImageData()
233 modifierImage.DeepCopy(threshold.GetOutput())
234 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
235 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
236 modifierImage.SetGeometryFromImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
240 self.scriptedEffect.modifySegmentByLabelmap(segmentationNode, segmentID, modifierImage, modificationMode)
242 if not split
and maxNumberOfSegments <= 0:
246 qt.QApplication.restoreOverrideCursor()
249 import vtkSegmentationCorePython
as vtkSegmentationCore
258 if viewWidget.className() !=
"qMRMLSliceWidget":
261 if eventId != vtk.vtkCommand.LeftButtonPressEvent
or callerInteractor.GetShiftKey()
or callerInteractor.GetControlKey()
or callerInteractor.GetAltKey():
265 if not self.scriptedEffect.confirmCurrentSegmentVisible():
271 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
272 visibleSegmentIds = vtk.vtkStringArray()
273 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(visibleSegmentIds)
274 if visibleSegmentIds.GetNumberOfValues() == 0:
275 logging.info(
"Island operation skipped: there are no visible segments")
278 self.scriptedEffect.saveStateForUndo()
281 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
283 operationName = self.scriptedEffect.parameter(
"Operation")
285 if operationName == ADD_SELECTED_ISLAND:
286 inputLabelImage = slicer.vtkOrientedImageData()
287 if not segmentationNode.GenerateMergedLabelmapForAllSegments(inputLabelImage,
288 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS_PADDED,
289 None, visibleSegmentIds):
290 logging.error(
'Failed to apply island operation: cannot get list of visible segments')
291 qt.QApplication.restoreOverrideCursor()
294 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
298 thresh = vtk.vtkImageThreshold()
299 thresh.SetInputData(selectedSegmentLabelmap)
300 thresh.ThresholdByLower(0)
301 thresh.SetInValue(backgroundValue)
302 thresh.SetOutValue(labelValue)
303 thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
306 import vtkSegmentationCorePython
as vtkSegmentationCore
307 inputLabelImage = slicer.vtkOrientedImageData()
308 inputLabelImage.ShallowCopy(thresh.GetOutput())
309 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
310 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
311 inputLabelImage.SetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
313 xy = callerInteractor.GetEventPosition()
314 ijk = self.xyToIjk(xy, viewWidget, inputLabelImage, segmentationNode.GetParentTransformNode())
315 pixelValue = inputLabelImage.GetScalarComponentAsFloat(ijk[0], ijk[1], ijk[2], 0)
318 floodFillingFilter = vtk.vtkImageThresholdConnectivity()
319 floodFillingFilter.SetInputData(inputLabelImage)
320 seedPoints = vtk.vtkPoints()
321 origin = inputLabelImage.GetOrigin()
322 spacing = inputLabelImage.GetSpacing()
323 seedPoints.InsertNextPoint(origin[0] + ijk[0] * spacing[0], origin[1] + ijk[1] * spacing[1], origin[2] + ijk[2] * spacing[2])
324 floodFillingFilter.SetSeedPoints(seedPoints)
325 floodFillingFilter.ThresholdBetween(pixelValue, pixelValue)
327 if operationName == ADD_SELECTED_ISLAND:
328 floodFillingFilter.SetInValue(1)
329 floodFillingFilter.SetOutValue(0)
330 floodFillingFilter.Update()
331 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
332 modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput())
333 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd)
335 elif pixelValue != 0:
337 if operationName == KEEP_SELECTED_ISLAND:
338 floodFillingFilter.SetInValue(1)
339 floodFillingFilter.SetOutValue(0)
341 floodFillingFilter.SetInValue(1)
342 floodFillingFilter.SetOutValue(0)
344 floodFillingFilter.Update()
345 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
346 modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput())
348 if operationName == KEEP_SELECTED_ISLAND:
349 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
351 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove)
354 logging.error(
'Island processing failed')
356 qt.QApplication.restoreOverrideCursor()
364 self.scriptedEffect.setParameterDefault(
"Operation", KEEP_LARGEST_ISLAND)
365 self.scriptedEffect.setParameterDefault(
"MinimumSize", 1000)
369 operationRadioButton.blockSignals(
True)
370 operationName = self.scriptedEffect.parameter(
"Operation")
372 currentOperationRadioButton.setChecked(
True)
374 operationRadioButton.blockSignals(
False)
377 self.
applyButton.setEnabled(
not segmentSelectionRequired)
378 if segmentSelectionRequired:
379 self.
applyButton.setToolTip(
"Click in a slice view to select an island.")
387 showMinimumSizeOption = (operationName
in [KEEP_LARGEST_ISLAND, REMOVE_SMALL_ISLANDS, SPLIT_ISLANDS_TO_SEGMENTS])
400 KEEP_LARGEST_ISLAND =
'KEEP_LARGEST_ISLAND' 401 KEEP_SELECTED_ISLAND =
'KEEP_SELECTED_ISLAND' 402 REMOVE_SMALL_ISLANDS =
'REMOVE_SMALL_ISLANDS' 403 REMOVE_SELECTED_ISLAND =
'REMOVE_SELECTED_ISLAND' 404 ADD_SELECTED_ISLAND =
'ADD_SELECTED_ISLAND' 405 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)