Slicer  4.8
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  selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
108  if selectedSegmentLabelmap:
109  import math
110  selectedSegmentLabelmapSpacing = selectedSegmentLabelmap.GetSpacing()
111  singleStep = min(selectedSegmentLabelmapSpacing)
112  # round to power of 10 (for example: 0.2 -> 0.1; 0.09 -> 0.01) to show "nice" values
113  singleStep = pow(10,math.floor(math.log(singleStep)/math.log(10)))
114  self.marginSizeMmSpinBox.singleStep = singleStep
115 
116  def growOperationToggled(self, toggled):
117  if toggled:
118  self.scriptedEffect.setParameter("MarginSizeMm", self.marginSizeMmSpinBox.value)
119 
120  def shrinkOperationToggled(self, toggled):
121  if toggled:
122  self.scriptedEffect.setParameter("MarginSizeMm", -self.marginSizeMmSpinBox.value)
123 
124  def updateMRMLFromGUI(self):
125  marginSizeMm = (self.marginSizeMmSpinBox.value) if self.growOptionRadioButton.checked else (-self.marginSizeMmSpinBox.value)
126  self.scriptedEffect.setParameter("MarginSizeMm", marginSizeMm)
127 
128  def onApply(self):
129 
130  self.scriptedEffect.saveStateForUndo()
131 
132  # Get modifier labelmap and parameters
133  modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
134  selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
135 
136  marginSizeMm = self.scriptedEffect.doubleParameter("MarginSizeMm")
137  kernelSizePixel = self.getKernelSizePixel()
138 
139  # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value
140  labelValue = 1
141  backgroundValue = 0
142  thresh = vtk.vtkImageThreshold()
143  thresh.SetInputData(selectedSegmentLabelmap)
144  thresh.ThresholdByLower(0)
145  thresh.SetInValue(backgroundValue)
146  thresh.SetOutValue(labelValue)
147  thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
148 
149  erodeDilate = vtk.vtkImageDilateErode3D()
150  erodeDilate.SetInputConnection(thresh.GetOutputPort())
151  if marginSizeMm>0:
152  # grow
153  erodeDilate.SetDilateValue(labelValue)
154  erodeDilate.SetErodeValue(backgroundValue)
155  else:
156  # shrink
157  erodeDilate.SetDilateValue(backgroundValue)
158  erodeDilate.SetErodeValue(labelValue)
159 
160  # This can be a long operation - indicate it to the user
161  qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
162 
163  erodeDilate.SetKernelSize(kernelSizePixel[0],kernelSizePixel[1],kernelSizePixel[2])
164  erodeDilate.Update()
165  modifierLabelmap.DeepCopy(erodeDilate.GetOutput())
166 
167  # Apply changes
168  self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
169 
170  qt.QApplication.restoreOverrideCursor()