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
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)