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