Slicer  4.10
Slicer is a multi-platform, free and open source software package for visualization and medical image computing
SegmentEditorMarginEffect.py
Go to the documentation of this file.
1 import os
2 import vtk, qt, ctk, slicer
3 import logging
4 from SegmentEditorEffects import *
5 
7  """ MaringEffect grows or shrinks the segment by a specified margin
8  """
9 
10  def __init__(self, scriptedEffect):
11  scriptedEffect.name = 'Margin'
12  AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
13 
14  def clone(self):
15  import qSlicerSegmentationsEditorEffectsPythonQt as effects
16  clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(None)
17  clonedEffect.setPythonSource(__file__.replace('\\','/'))
18  return clonedEffect
19 
20  def icon(self):
21  iconPath = os.path.join(os.path.dirname(__file__), 'Resources/Icons/Margin.png')
22  if os.path.exists(iconPath):
23  return qt.QIcon(iconPath)
24  return qt.QIcon()
25 
26  def helpText(self):
27  return "Grow or shrink selected segment by specified margin size."
28 
29  def setupOptionsFrame(self):
30 
31  operationLayout = qt.QVBoxLayout()
32 
33  self.shrinkOptionRadioButton = qt.QRadioButton("Shrink")
34  self.growOptionRadioButton = qt.QRadioButton("Grow")
35  operationLayout.addWidget(self.shrinkOptionRadioButton)
36  operationLayout.addWidget(self.growOptionRadioButton)
37  self.growOptionRadioButton.setChecked(True)
38 
39  self.scriptedEffect.addLabeledOptionsWidget("Operation:", operationLayout)
40 
41  self.marginSizeMmSpinBox = slicer.qMRMLSpinBox()
42  self.marginSizeMmSpinBox.setMRMLScene(slicer.mrmlScene)
43  self.marginSizeMmSpinBox.setToolTip("Segment boundaries will be shifted by this distance. Positive value means the segments will grow, negative value means segment will shrink.")
44  self.marginSizeMmSpinBox.quantity = "length"
45  self.marginSizeMmSpinBox.value = 3.0
46  self.marginSizeMmSpinBox.singleStep = 1.0
47 
48  self.kernelSizePixel = qt.QLabel()
49  self.kernelSizePixel.setToolTip("Size change in pixels. Computed from the segment's spacing and the specified margin size.")
50 
51  marginSizeFrame = qt.QHBoxLayout()
52  marginSizeFrame.addWidget(self.marginSizeMmSpinBox)
53  marginSizeFrame.addWidget(self.kernelSizePixel)
54  self.marginSizeMmLabel = self.scriptedEffect.addLabeledOptionsWidget("Margin size:", marginSizeFrame)
55 
56  self.applyButton = qt.QPushButton("Apply")
57  self.applyButton.objectName = self.__class__.__name__ + 'Apply'
58  self.applyButton.setToolTip("Grows or shrinks selected segment by the specified margin.")
59  self.scriptedEffect.addOptionsWidget(self.applyButton)
60 
61  self.applyButton.connect('clicked()', self.onApply)
62  self.marginSizeMmSpinBox.connect("valueChanged(double)", self.updateMRMLFromGUI)
63  self.growOptionRadioButton.connect("toggled(bool)", self.growOperationToggled)
64  self.shrinkOptionRadioButton.connect("toggled(bool)", self.shrinkOperationToggled)
65 
66  def createCursor(self, widget):
67  # Turn off effect-specific cursor for this effect
68  return slicer.util.mainWindow().cursor
69 
70  def setMRMLDefaults(self):
71  self.scriptedEffect.setParameterDefault("MarginSizeMm", 3)
72 
73  def getKernelSizePixel(self):
74  selectedSegmentLabelmapSpacing = [1.0, 1.0, 1.0]
75  selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
76  if selectedSegmentLabelmap:
77  selectedSegmentLabelmapSpacing = selectedSegmentLabelmap.GetSpacing()
78 
79  # size rounded to nearest odd number. If kernel size is even then image gets shifted.
80  marginSizeMm = abs(self.scriptedEffect.doubleParameter("MarginSizeMm"))
81  kernelSizePixel = [int(round((marginSizeMm / selectedSegmentLabelmapSpacing[componentIndex]+1)/2)*2-1) for componentIndex in range(3)]
82  return kernelSizePixel
83 
84  def updateGUIFromMRML(self):
85  marginSizeMm = self.scriptedEffect.doubleParameter("MarginSizeMm")
86  wasBlocked = self.marginSizeMmSpinBox.blockSignals(True)
87  self.marginSizeMmSpinBox.value = abs(marginSizeMm)
88  self.marginSizeMmSpinBox.blockSignals(wasBlocked)
89 
90  wasBlocked = self.growOptionRadioButton.blockSignals(True)
91  self.growOptionRadioButton.setChecked(marginSizeMm > 0)
92  self.growOptionRadioButton.blockSignals(wasBlocked)
93 
94  wasBlocked = self.shrinkOptionRadioButton.blockSignals(True)
95  self.shrinkOptionRadioButton.setChecked(marginSizeMm < 0)
96  self.shrinkOptionRadioButton.blockSignals(wasBlocked)
97 
98  kernelSizePixel = self.getKernelSizePixel()
99 
100  if kernelSizePixel[0]<=1 and kernelSizePixel[1]<=1 and kernelSizePixel[2]<=1:
101  self.kernelSizePixel.text = "margin too small"
102  self.applyButton.setEnabled(False)
103  else:
104  self.kernelSizePixel.text = "{0}x{1}x{2} pixels".format(abs(kernelSizePixel[0]), abs(kernelSizePixel[1]), abs(kernelSizePixel[2]))
105  self.applyButton.setEnabled(True)
106 
107  self.setWidgetMinMaxStepFromImageSpacing(self.marginSizeMmSpinBox, self.scriptedEffect.selectedSegmentLabelmap())
108 
109  def growOperationToggled(self, toggled):
110  if toggled:
111  self.scriptedEffect.setParameter("MarginSizeMm", self.marginSizeMmSpinBox.value)
112 
113  def shrinkOperationToggled(self, toggled):
114  if toggled:
115  self.scriptedEffect.setParameter("MarginSizeMm", -self.marginSizeMmSpinBox.value)
116 
117  def updateMRMLFromGUI(self):
118  marginSizeMm = (self.marginSizeMmSpinBox.value) if self.growOptionRadioButton.checked else (-self.marginSizeMmSpinBox.value)
119  self.scriptedEffect.setParameter("MarginSizeMm", marginSizeMm)
120 
121  def onApply(self):
122 
123  self.scriptedEffect.saveStateForUndo()
124 
125  # Get modifier labelmap and parameters
126  modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
127  selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
128 
129  marginSizeMm = self.scriptedEffect.doubleParameter("MarginSizeMm")
130  kernelSizePixel = self.getKernelSizePixel()
131 
132  # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value
133  labelValue = 1
134  backgroundValue = 0
135  thresh = vtk.vtkImageThreshold()
136  thresh.SetInputData(selectedSegmentLabelmap)
137  thresh.ThresholdByLower(0)
138  thresh.SetInValue(backgroundValue)
139  thresh.SetOutValue(labelValue)
140  thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
141 
142  erodeDilate = vtk.vtkImageDilateErode3D()
143  erodeDilate.SetInputConnection(thresh.GetOutputPort())
144  if marginSizeMm>0:
145  # grow
146  erodeDilate.SetDilateValue(labelValue)
147  erodeDilate.SetErodeValue(backgroundValue)
148  else:
149  # shrink
150  erodeDilate.SetDilateValue(backgroundValue)
151  erodeDilate.SetErodeValue(labelValue)
152 
153  # This can be a long operation - indicate it to the user
154  qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
155 
156  erodeDilate.SetKernelSize(kernelSizePixel[0],kernelSizePixel[1],kernelSizePixel[2])
157  erodeDilate.Update()
158  modifierLabelmap.DeepCopy(erodeDilate.GetOutput())
159 
160  # Apply changes
161  self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
162 
163  qt.QApplication.restoreOverrideCursor()