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]
113 if not self.scriptedEffect.confirmCurrentSegmentVisible():
115 operationName = self.scriptedEffect.parameter(
"Operation")
116 minimumSize = self.scriptedEffect.integerParameter(
"MinimumSize")
117 if operationName == KEEP_LARGEST_ISLAND:
118 self.
splitSegments(minimumSize = minimumSize, maxNumberOfSegments = 1)
119 elif operationName == REMOVE_SMALL_ISLANDS:
121 elif operationName == SPLIT_ISLANDS_TO_SEGMENTS:
124 def splitSegments(self, minimumSize = 0, maxNumberOfSegments = 0, split = True):
126 minimumSize: if 0 then it means that all islands are kept, regardless of size 127 maxNumberOfSegments: if 0 then it means that all islands are kept, regardless of how many 130 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
132 self.scriptedEffect.saveStateForUndo()
135 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
137 castIn = vtk.vtkImageCast()
138 castIn.SetInputData(selectedSegmentLabelmap)
139 castIn.SetOutputScalarTypeToUnsignedInt()
143 islandMath = vtkITK.vtkITKIslandMath()
144 islandMath.SetInputConnection(castIn.GetOutputPort())
145 islandMath.SetFullyConnected(
False)
146 islandMath.SetMinimumSize(minimumSize)
149 islandImage = slicer.vtkOrientedImageData()
150 islandImage.ShallowCopy(islandMath.GetOutput())
151 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
152 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
153 islandImage.SetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
155 islandCount = islandMath.GetNumberOfIslands()
156 islandOrigCount = islandMath.GetOriginalNumberOfIslands()
157 ignoredIslands = islandOrigCount - islandCount
158 logging.info(
"%d islands created (%d ignored)" % (islandCount, ignoredIslands) )
160 baseSegmentName =
"Label" 161 selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
162 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
163 with slicer.util.NodeModify(segmentationNode):
164 segmentation = segmentationNode.GetSegmentation()
165 selectedSegment = segmentation.GetSegment(selectedSegmentID)
166 selectedSegmentName = selectedSegment.GetName()
167 if selectedSegmentName
is not None and selectedSegmentName !=
"":
168 baseSegmentName = selectedSegmentName
170 labelValues = vtk.vtkIntArray()
171 slicer.vtkSlicerSegmentationsModuleLogic.GetAllLabelValues(labelValues, islandImage)
175 threshold = vtk.vtkImageThreshold()
176 threshold.SetInputData(selectedSegmentLabelmap)
177 threshold.ThresholdBetween(0, 0)
178 threshold.SetInValue(0)
179 threshold.SetOutValue(0)
181 emptyLabelmap = slicer.vtkOrientedImageData()
182 emptyLabelmap.ShallowCopy(threshold.GetOutput())
183 emptyLabelmap.CopyDirections(selectedSegmentLabelmap)
184 self.scriptedEffect.modifySegmentByLabelmap(segmentationNode, selectedSegmentID, emptyLabelmap,
185 slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
187 for i
in range(labelValues.GetNumberOfTuples()):
188 if (maxNumberOfSegments > 0
and i >= maxNumberOfSegments):
193 labelValue = int(labelValues.GetTuple1(i))
194 segment = selectedSegment
195 segmentID = selectedSegmentID
197 segment = slicer.vtkSegment()
198 name = baseSegmentName +
"_" + str(i+1)
199 segment.SetName(name)
200 segment.AddRepresentation(slicer.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName(),
201 selectedSegment.GetRepresentation(slicer.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()))
202 segmentation.AddSegment(segment)
203 segmentID = segmentation.GetSegmentIdBySegment(segment)
204 segment.SetLabelValue(segmentation.GetUniqueLabelValueForSharedLabelmap(selectedSegmentID))
206 threshold = vtk.vtkImageThreshold()
207 threshold.SetInputData(islandMath.GetOutput())
208 if not split
and maxNumberOfSegments <= 0:
210 threshold.ThresholdByLower(0)
211 threshold.SetInValue(0)
212 threshold.SetOutValue(1)
215 threshold.ThresholdBetween(labelValue, labelValue)
216 threshold.SetInValue(1)
217 threshold.SetOutValue(0)
220 modificationMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd
222 modificationMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet
225 modifierImage = slicer.vtkOrientedImageData()
226 modifierImage.DeepCopy(threshold.GetOutput())
227 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
228 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
229 modifierImage.SetGeometryFromImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
233 self.scriptedEffect.modifySegmentByLabelmap(segmentationNode, segmentID, modifierImage, modificationMode)
235 if not split
and maxNumberOfSegments <= 0:
239 qt.QApplication.restoreOverrideCursor()
242 import vtkSegmentationCorePython
as vtkSegmentationCore
251 if viewWidget.className() !=
"qMRMLSliceWidget":
254 if eventId != vtk.vtkCommand.LeftButtonPressEvent:
258 if not self.scriptedEffect.confirmCurrentSegmentVisible():
264 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
265 visibleSegmentIds = vtk.vtkStringArray()
266 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(visibleSegmentIds)
267 if visibleSegmentIds.GetNumberOfValues() == 0:
268 logging.info(
"Smoothing operation skipped: there are no visible segments")
271 self.scriptedEffect.saveStateForUndo()
274 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
276 operationName = self.scriptedEffect.parameter(
"Operation")
278 if operationName == ADD_SELECTED_ISLAND:
279 inputLabelImage = slicer.vtkOrientedImageData()
280 if not segmentationNode.GenerateMergedLabelmapForAllSegments(inputLabelImage,
281 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS_PADDED,
282 None, visibleSegmentIds):
283 logging.error(
'Failed to apply smoothing: cannot get list of visible segments')
284 qt.QApplication.restoreOverrideCursor()
287 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
291 thresh = vtk.vtkImageThreshold()
292 thresh.SetInputData(selectedSegmentLabelmap)
293 thresh.ThresholdByLower(0)
294 thresh.SetInValue(backgroundValue)
295 thresh.SetOutValue(labelValue)
296 thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
299 import vtkSegmentationCorePython
as vtkSegmentationCore
300 inputLabelImage = slicer.vtkOrientedImageData()
301 inputLabelImage.ShallowCopy(thresh.GetOutput())
302 selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
303 selectedSegmentLabelmap.GetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
304 inputLabelImage.SetImageToWorldMatrix(selectedSegmentLabelmapImageToWorldMatrix)
306 xy = callerInteractor.GetEventPosition()
307 ijk = self.xyToIjk(xy, viewWidget, inputLabelImage, segmentationNode.GetParentTransformNode())
308 pixelValue = inputLabelImage.GetScalarComponentAsFloat(ijk[0], ijk[1], ijk[2], 0)
311 floodFillingFilter = vtk.vtkImageThresholdConnectivity()
312 floodFillingFilter.SetInputData(inputLabelImage)
313 seedPoints = vtk.vtkPoints()
314 origin = inputLabelImage.GetOrigin()
315 spacing = inputLabelImage.GetSpacing()
316 seedPoints.InsertNextPoint(origin[0]+ijk[0]*spacing[0], origin[1]+ijk[1]*spacing[1], origin[2]+ijk[2]*spacing[2])
317 floodFillingFilter.SetSeedPoints(seedPoints)
318 floodFillingFilter.ThresholdBetween(pixelValue, pixelValue)
320 if operationName == ADD_SELECTED_ISLAND:
321 floodFillingFilter.SetInValue(1)
322 floodFillingFilter.SetOutValue(0)
323 floodFillingFilter.Update()
324 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
325 modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput())
326 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd)
328 elif pixelValue != 0:
330 if operationName == KEEP_SELECTED_ISLAND:
331 floodFillingFilter.SetInValue(1)
332 floodFillingFilter.SetOutValue(0)
334 floodFillingFilter.SetInValue(1)
335 floodFillingFilter.SetOutValue(0)
337 floodFillingFilter.Update()
338 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
339 modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput())
341 if operationName == KEEP_SELECTED_ISLAND:
342 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
344 self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove)
347 logging.error(
'apply: Failed to threshold master volume!')
349 qt.QApplication.restoreOverrideCursor()
357 self.scriptedEffect.setParameterDefault(
"Operation", KEEP_LARGEST_ISLAND)
358 self.scriptedEffect.setParameterDefault(
"MinimumSize", 1000)
362 operationRadioButton.blockSignals(
True)
363 operationName = self.scriptedEffect.parameter(
"Operation")
365 currentOperationRadioButton.setChecked(
True)
367 operationRadioButton.blockSignals(
False)
370 self.
applyButton.setEnabled(
not segmentSelectionRequired)
371 if segmentSelectionRequired:
372 self.
applyButton.setToolTip(
"Click in a slice viewer to select segment")
380 showMinimumSizeOption = (operationName
in [REMOVE_SMALL_ISLANDS, SPLIT_ISLANDS_TO_SEGMENTS])
392 KEEP_LARGEST_ISLAND =
'KEEP_LARGEST_ISLAND' 393 KEEP_SELECTED_ISLAND =
'KEEP_SELECTED_ISLAND' 394 REMOVE_SMALL_ISLANDS =
'REMOVE_SMALL_ISLANDS' 395 REMOVE_SELECTED_ISLAND =
'REMOVE_SELECTED_ISLAND' 396 ADD_SELECTED_ISLAND =
'ADD_SELECTED_ISLAND' 397 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)