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
SegmentEditorLevelTracingEffect.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 import vtkITK
6 
8  """ LevelTracingEffect is a LabelEffect implementing level tracing fill
9  using intensity-based isolines
10  """
11 
12  def __init__(self, scriptedEffect):
13  scriptedEffect.name = 'Level tracing'
14  AbstractScriptedSegmentEditorLabelEffect.__init__(self, scriptedEffect)
15 
16  # Effect-specific members
18  self.lastXY = None
19 
20  def clone(self):
21  import qSlicerSegmentationsEditorEffectsPythonQt as effects
22  clonedEffect = effects.qSlicerSegmentEditorScriptedLabelEffect(None)
23  clonedEffect.setPythonSource(__file__.replace('\\','/'))
24  return clonedEffect
25 
26  def icon(self):
27  iconPath = os.path.join(os.path.dirname(__file__), 'Resources/Icons/LevelTracing.png')
28  if os.path.exists(iconPath):
29  return qt.QIcon(iconPath)
30  return qt.QIcon()
31 
32  def helpText(self):
33  return """<html>Add uniform intensity region to selected segment<br>.
34 <p><ul style="margin: 0">
35 <li><b>Mouse move:</b> current background voxel is used to find a closed path that
36 follows the same intensity value back to the starting point within the current slice.</li>
37 <li><b>Left-click:</b> add the previewed region to the current segment.</li>
38 </ul><p></html>"""
39 
40  def deactivate(self):
41  # Clear draw pipelines
42  for sliceWidget, pipeline in self.levelTracingPipelines.iteritems():
43  self.scriptedEffect.removeActor2D(sliceWidget, pipeline.actor)
44  self.levelTracingPipelines = {}
45  self.lastXY = None
46 
47  def processInteractionEvents(self, callerInteractor, eventId, viewWidget):
48  abortEvent = False
49 
50  # Only allow for slice views
51  if viewWidget.className() != "qMRMLSliceWidget":
52  return abortEvent
53  # Get draw pipeline for current slice
54  pipeline = self.pipelineForWidget(viewWidget)
55  if pipeline is None:
56  return abortEvent
57 
58  if eventId == vtk.vtkCommand.LeftButtonPressEvent:
59  self.scriptedEffect.saveStateForUndo()
60 
61  # Get modifier labelmap
62  import vtkSegmentationCorePython as vtkSegmentationCore
63  modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
64 
65  # Apply poly data on modifier labelmap
66  pipeline.appendPolyMask(modifierLabelmap)
67  # TODO: it would be nice to reduce extent
68  self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd)
69  abortEvent = True
70  elif eventId == vtk.vtkCommand.MouseMoveEvent:
71  if pipeline.actionState == '':
72  xy = callerInteractor.GetEventPosition()
73  pipeline.preview(xy)
74  abortEvent = True
75  self.lastXY = xy
76  elif eventId == vtk.vtkCommand.RightButtonPressEvent or eventId == vtk.vtkCommand.MiddleButtonPressEvent:
77  pipeline.actionState = 'interacting'
78  elif eventId == vtk.vtkCommand.RightButtonReleaseEvent or eventId == vtk.vtkCommand.MiddleButtonReleaseEvent:
79  pipeline.actionState = ''
80  elif eventId == vtk.vtkCommand.EnterEvent:
81  pipeline.actor.VisibilityOn()
82  elif eventId == vtk.vtkCommand.LeaveEvent:
83  pipeline.actor.VisibilityOff()
84  self.lastXY = None
85 
86  return abortEvent
87 
88  def processViewNodeEvents(self, callerViewNode, eventId, viewWidget):
89  if callerViewNode and callerViewNode.IsA('vtkMRMLSliceNode'):
90  # Get draw pipeline for current slice
91  pipeline = self.pipelineForWidget(viewWidget)
92  if pipeline is None:
93  logging.error('processViewNodeEvents: Invalid pipeline')
94  return
95 
96  # Update the preview to the new slice
97  if pipeline.actionState == '' and self.lastXY:
98  pipeline.preview(self.lastXY)
99 
100  def pipelineForWidget(self, sliceWidget):
101  if sliceWidget in self.levelTracingPipelines:
102  return self.levelTracingPipelines[sliceWidget]
103 
104  # Create pipeline if does not yet exist
105  pipeline = LevelTracingPipeline(self, sliceWidget)
106 
107  # Add actor
108  renderer = self.scriptedEffect.renderer(sliceWidget)
109  if renderer is None:
110  logging.error("setupPreviewDisplay: Failed to get renderer!")
111  return None
112  self.scriptedEffect.addActor2D(sliceWidget, pipeline.actor)
113 
114  self.levelTracingPipelines[sliceWidget] = pipeline
115  return pipeline
116 
117 #
118 # LevelTracingPipeline
119 #
121  """ Visualization objects and pipeline for each slice view for level tracing
122  """
123  def __init__(self, effect, sliceWidget):
124  self.effect = effect
125  self.sliceWidget = sliceWidget
126  self.actionState = ''
127 
128  self.xyPoints = vtk.vtkPoints()
129  self.rasPoints = vtk.vtkPoints()
130  self.polyData = vtk.vtkPolyData()
131 
132  self.tracingFilter = vtkITK.vtkITKLevelTracingImageFilter()
133  self.ijkToXY = vtk.vtkGeneralTransform()
134 
135  self.mapper = vtk.vtkPolyDataMapper2D()
136  self.actor = vtk.vtkActor2D()
137  actorProperty = self.actor.GetProperty()
138  actorProperty.SetColor( 107/255., 190/255., 99/255. )
139  actorProperty.SetLineWidth( 1 )
140  self.mapper.SetInputData(self.polyData)
141  self.actor.SetMapper(self.mapper)
142  actorProperty = self.actor.GetProperty()
143  actorProperty.SetColor(1,1,0)
144  actorProperty.SetLineWidth(1)
145 
146  def preview(self,xy):
147  # Calculate the current level trace view if the mouse is inside the volume extent
148 
149  # Get master volume image data
150  import vtkSegmentationCorePython as vtkSegmentationCore
151  masterImageData = self.effect.scriptedEffect.masterVolumeImageData()
152 
153  self.xyPoints.Reset()
154  ijk = self.effect.xyToIjk(xy, self.sliceWidget, masterImageData)
155  dimensions = masterImageData.GetDimensions()
156 
157  for index in xrange(3):
158  # TracingFilter crashes if it receives a seed point at the edge of the image,
159  # so only accept the point if it is inside the image and is at least one pixel away from the edge
160  if ijk[index] < 1 or ijk[index] >= dimensions[index]-1:
161  return
162  self.tracingFilter.SetInputData(masterImageData)
163  self.tracingFilter.SetSeed(ijk)
164 
165  # Select the plane corresponding to current slice orientation
166  # for the input volume
167  sliceNode = self.effect.scriptedEffect.viewNode(self.sliceWidget)
168  offset = max(sliceNode.GetDimensions())
169  i0,j0,k0 = self.effect.xyToIjk((0,0), self.sliceWidget, masterImageData)
170  i1,j1,k1 = self.effect.xyToIjk((offset,offset), self.sliceWidget, masterImageData)
171  if i0 == i1:
172  self.tracingFilter.SetPlaneToJK()
173  if j0 == j1:
174  self.tracingFilter.SetPlaneToIK()
175  if k0 == k1:
176  self.tracingFilter.SetPlaneToIJ()
177 
178  self.tracingFilter.Update()
179  polyData = self.tracingFilter.GetOutput()
180 
181  # Get master volume IJK to slice XY transform
182  xyToRas = sliceNode.GetXYToRAS()
183  rasToIjk = vtk.vtkMatrix4x4()
184  masterImageData.GetImageToWorldMatrix(rasToIjk)
185  rasToIjk.Invert()
186  xyToIjk = vtk.vtkGeneralTransform()
187  xyToIjk.PostMultiply()
188  xyToIjk.Concatenate(xyToRas)
189  xyToIjk.Concatenate(rasToIjk)
190  ijkToXy = xyToIjk.GetInverse()
191  ijkToXy.TransformPoints(polyData.GetPoints(), self.xyPoints)
192 
193  self.polyData.DeepCopy(polyData)
194  self.polyData.GetPoints().DeepCopy(self.xyPoints)
195  self.sliceWidget.sliceView().scheduleRender()
196 
197  def appendPolyMask(self, modifierLabelmap):
198  lines = self.polyData.GetLines()
199  if lines.GetNumberOfCells() == 0:
200  return
201 
202  # Apply poly data on modifier labelmap
203  self.effect.scriptedEffect.appendPolyMask(modifierLabelmap, self.polyData, self.sliceWidget)
def processInteractionEvents(self, callerInteractor, eventId, viewWidget)