Slicer  4.8
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  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()