Slicer  4.10
Slicer is a multi-platform, free and open source software package for visualization and medical image computing
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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()