Commit 07e8eb5c6077aaad3ea50972af29d72dace3ef28

Authored by tatiana
1 parent a6dcdfb2

ADD: Raycasting feature (a very small part of it), adapted from Thiago's work

.gitignore
  1 +invesalius/*.log
  2 +invesalius/*.pyc
1 3 invesalius/data/*.log
2 4 invesalius/data/*.pyc
3 5 invesalius/gui/*.log
... ...
invesalius/control.py
... ... @@ -5,6 +5,7 @@ import project as prj
5 5  
6 6 import data.imagedata_utils as utils
7 7 import data.surface as surface
  8 +import data.volume as volume
8 9 import reader.dicom_reader as dicom
9 10 import reader.analyze_reader as analyze
10 11  
... ... @@ -14,6 +15,7 @@ class Controller():
14 15  
15 16 def __init__(self, frame):
16 17 self.surface_manager = surface.SurfaceManager()
  18 + self.volume = volume.Volume()
17 19 self.__bind_events()
18 20  
19 21 def __bind_events(self):
... ...
invesalius/data/viewer_volume.py
... ... @@ -63,17 +63,16 @@ class Viewer(wx.Panel):
63 63 ps.Publisher().subscribe(self.UpdateRender, 'Render volume viewer')
64 64 ps.Publisher().subscribe(self.ChangeBackgroundColour,
65 65 'Change volume viewer background colour')
66   - ps.Publisher().subscribe(self.ShowRaycastingVolume,
67   - 'Show raycasting volume')
68   - ps.Publisher().subscribe(self.HideRaycastingVolume,
69   - 'Hide raycasting volume')
70   -
71   -
72   - def ShowRaycastingVolume(self, pubsub_evt):
73   - pass
74   -
75   - def HideRaycastingVolume(self, pubsub_evt):
76   - pass
  66 + ps.Publisher().subscribe(self.LoadVolume, 'Load volume into viewer')
  67 +
  68 + def LoadVolume(self, pubsub_evt):
  69 + volume, colour = pubsub_evt.data
  70 + self.light = self.ren.GetLights().GetNextItem()
  71 + self.ren.AddVolume(volume)
  72 + #self.ren.SetBackground(colour)
  73 + self.ren.ResetCamera()
  74 + self.ren.ResetCameraClippingRange()
  75 + self.UpdateRender()
77 76  
78 77 def ChangeBackgroundColour(self, pubsub_evt):
79 78 colour = pubsub_evt.data
... ... @@ -81,7 +80,6 @@ class Viewer(wx.Panel):
81 80 self.UpdateRender()
82 81  
83 82 def LoadActor(self, pubsub_evt):
84   - print "****** Load actor"
85 83 actor = pubsub_evt.data
86 84  
87 85 ren = self.ren
... ...
invesalius/data/volume.py
... ... @@ -16,17 +16,287 @@
16 16 # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais
17 17 # detalhes.
18 18 #--------------------------------------------------------------------------
  19 +import plistlib
  20 +import os
19 21  
20 22 import vtk
  23 +import wx.lib.pubsub as ps
21 24  
22   -from utils import Singleton
  25 +from project import Project
  26 +
  27 +Kernels = {
  28 + "Basic Smooth 5x5" : [1.0, 1.0, 1.0, 1.0, 1.0,
  29 + 1.0, 4.0, 4.0, 4.0, 1.0,
  30 + 1.0, 4.0, 12.0, 4.0, 1.0,
  31 + 1.0, 4.0, 4.0, 4.0, 1.0,
  32 + 1.0, 1.0, 1.0, 1.0, 1.0]
  33 +}
  34 +
  35 +
  36 +PRESETS = ["Airways", "Airways II", "Bone + Skin", "Bone + Skin II", "Dark Bone",
  37 +"Gold Bone", "Skin On Blue", "Skin On Blue II", "Soft + Skin", "Soft + Skin II",
  38 +"Soft + Skin III", "Yellow Bone"]
23 39  
24 40 class Volume():
25   - __metaclass__= Singleton
26   - # Only one volume will be initialized per time. Therefore, we use
27   - # Singleton design pattern for implementing it
28 41  
29 42 def __init__(self):
30   - self.imagedata = None
  43 + self.config = None
  44 + self.exist = None
  45 +
31 46 self.__bind_events()
32 47  
  48 + def __bind_events(self):
  49 + #ps.Publisher().subscribe(self.OnLoadVolume, 'Create volume raycasting')
  50 + ps.Publisher().subscribe(self.OnShowVolume,
  51 + 'Show raycasting volume')
  52 + ps.Publisher().subscribe(self.OnHideVolume,
  53 + 'Hide raycasting volume')
  54 +
  55 +
  56 + def OnLoadVolume(self, pubsub_evt):
  57 + label = pubsub_evt.data
  58 + self.LoadConfig(label)
  59 + self.LoadVolume()
  60 +
  61 + def LoadConfig(self, label):
  62 + if not label:
  63 + label = "Skin on Blue"
  64 +
  65 + path = os.path.abspath("../presets/raycasting/"+label+".plist")
  66 + self.config = plistlib.readPlist(path)
  67 +
  68 + def OnHideVolume(self, pubsub_evt):
  69 + self.volume.SetVisibility(0)
  70 + ps.Publisher().sendMessage('Render volume viewer')
  71 +
  72 + def OnShowVolume(self, pubsub_evt):
  73 + if self.exist:
  74 + self.volume.SetVisibility(1)
  75 + ps.Publisher().sendMessage('Render volume viewer')
  76 + else:
  77 + self.LoadConfig(None)
  78 + self.LoadVolume()
  79 + self.exist = 1
  80 +
  81 +
  82 +#***************
  83 + def Create16bColorTable(self, scale):
  84 + color_transfer = vtk.vtkColorTransferFunction()
  85 + curve_table = self.config['16bitClutCurves']
  86 + color_table = self.config['16bitClutColors']
  87 + colors = []
  88 + for i, l in enumerate(curve_table):
  89 + for j, lopacity in enumerate(l):
  90 + gray_level = lopacity['x']
  91 + r = color_table[i][j]['red']
  92 + g = color_table[i][j]['green']
  93 + b = color_table[i][j]['blue']
  94 +
  95 + colors.append((gray_level, r, g, b))
  96 + color_transfer.AddRGBPoint(
  97 + self.TranslateScale(scale, gray_level),
  98 + r, g, b)
  99 + return color_transfer
  100 +
  101 + def Create8bColorTable(self):
  102 + color_transfer = vtk.vtkColorTransferFunction()
  103 + color_preset = self.config['CLUT']
  104 + p = plistlib.readPlist( os.path.join('ColorList', color_preset + '.plist'))
  105 + r = p['Red']
  106 + g = p['Green']
  107 + b = p['Blue']
  108 + colors = zip(r,g,b)
  109 + for i,rgb in enumerate(colors):
  110 + color_transfer.AddRGBPoint(i, *rgb)
  111 + return color_transfer
  112 +
  113 + def CreateOpacityTable(self, scale):
  114 + opacity_transfer_func = vtk.vtkPiecewiseFunction()
  115 + curve_table = self.config['16bitClutCurves']
  116 + opacities = []
  117 +
  118 + ww = self.config['ww']
  119 + wl = self.config['wl']
  120 +
  121 + l1 = wl - ww/2.0
  122 + l2 = wl + ww/2.0
  123 +
  124 + k1 = 0.0
  125 + k2 = 1.0
  126 +
  127 + for i, l in enumerate(curve_table):
  128 + for j, lopacity in enumerate(l):
  129 + gray_level = lopacity['x']
  130 + #if gray_level <= l1:
  131 + # opacity = k1
  132 + #elif gray_level > l2:
  133 + # opacity = k2
  134 + #else:
  135 + opacity = lopacity['y']
  136 + opacities.append((gray_level, opacity))
  137 + opacity_transfer_func.AddPoint(
  138 + self.TranslateScale(scale, gray_level), opacity)
  139 + return opacity_transfer_func
  140 +
  141 + def Create8bOpacityTable(self):
  142 + opacity_transfer_func = vtk.vtkPiecewiseFunction()
  143 + opacities = []
  144 +
  145 + ww = self.config['ww']
  146 + wl = self.config['wl']
  147 +
  148 + print ww, wl
  149 +
  150 + l1 = wl - ww/2.0
  151 + l2 = wl + ww/2.0
  152 +
  153 + k1 = 0.0
  154 + k2 = 1.0
  155 +
  156 + opacity_transfer_func.AddPoint(0, 0)
  157 + opacity_transfer_func.AddPoint(l1-1, 0)
  158 + opacity_transfer_func.AddPoint(l1, 1)
  159 + opacity_transfer_func.AddPoint(l2, 1)
  160 + opacity_transfer_func.AddPoint(l2+1, 0)
  161 + opacity_transfer_func.AddPoint(255, 0)
  162 +
  163 + return opacity_transfer_func
  164 +
  165 + def CreateBackgroundColor(self):
  166 + color_background = (self.config['backgroundColorRedComponent'],
  167 + self.config['backgroundColorGreenComponent'],
  168 + self.config['backgroundColorBlueComponent'])
  169 + return color_background
  170 +
  171 +
  172 + def BuildTable():
  173 + curve_table = p['16bitClutCurves']
  174 + color_background = (p['backgroundColorRedComponent'],
  175 + p['backgroundColorGreenComponent'],
  176 + p['backgroundColorBlueComponent'])
  177 + color_background = [i for i in color_background]
  178 + opacities = []
  179 + colors = []
  180 +
  181 + for i, l in enumerate(curve_table):
  182 + for j, lopacity in enumerate(l):
  183 + gray_level = lopacity['x']
  184 + opacity = lopacity['y']
  185 +
  186 + opacities.append((gray_level, opacity))
  187 +
  188 + r = color_table[i][j]['red']
  189 + g = color_table[i][j]['green']
  190 + b = color_table[i][j]['blue']
  191 +
  192 + colors.append((gray_level, r, g, b))
  193 +
  194 + return colors, opacities, color_background, p['useShading']
  195 +
  196 +
  197 +
  198 + def LoadVolume(self):
  199 + proj = Project()
  200 + image = proj.imagedata
  201 +
  202 + # Flip original vtkImageData
  203 + flip = vtk.vtkImageFlip()
  204 + flip.SetInput(image)
  205 + flip.SetFilteredAxis(1)
  206 + flip.FlipAboutOriginOn()
  207 + flip.Update()
  208 +
  209 + image = flip.GetOutput()
  210 +
  211 +
  212 + scale = image.GetScalarRange()
  213 +
  214 + cast = vtk.vtkImageShiftScale()
  215 + cast.SetInput(image)
  216 + if self.config['advancedCLUT']:
  217 + cast.SetShift(abs(scale[0]))
  218 + #cast.SetScale(2**16-1)
  219 + cast.SetOutputScalarTypeToUnsignedShort()
  220 + #scale = image.GetScalarRange()
  221 + color_transfer = self.Create16bColorTable(scale)
  222 + opacity_transfer_func = self.CreateOpacityTable(scale)
  223 + cast.Update()
  224 + image2 = cast
  225 + else:
  226 + cast.SetShift(abs(scale[0]))
  227 + cast.SetScale(255.0/(scale[1] - scale[0]))
  228 + cast.SetOutputScalarTypeToUnsignedChar()
  229 + color_transfer = self.Create8bColorTable()
  230 + opacity_transfer_func = self.Create8bOpacityTable()
  231 + cast.Update()
  232 + image2 = cast
  233 + #cast.ClampOverflowOff()
  234 +
  235 + convolve = vtk.vtkImageConvolve()
  236 + convolve.SetInput(image2.GetOutput())
  237 + convolve.SetKernel5x5([i/60.0 for i in Kernels[self.config['convolutionFilters'][0]]])
  238 + convolve.Update()
  239 +
  240 + image2 = convolve
  241 +
  242 + composite_function = vtk.vtkVolumeRayCastCompositeFunction()
  243 + composite_function.SetCompositeMethodToClassifyFirst()
  244 +
  245 + gradientEstimator = vtk.vtkFiniteDifferenceGradientEstimator()
  246 + gradientEstimator.SetGradientMagnitudeScale(1)
  247 +
  248 + volume_mapper = vtk.vtkVolumeRayCastMapper()
  249 + #volume_mapper.AutoAdjustSampleDistancesOff()
  250 + volume_mapper.SetInput(image2.GetOutput())
  251 + volume_mapper.SetVolumeRayCastFunction(composite_function)
  252 + volume_mapper.SetGradientEstimator(gradientEstimator)
  253 + volume_mapper.IntermixIntersectingGeometryOn()
  254 +
  255 + #clip = vtk.vtkPlane()
  256 +
  257 + #volume_mapper.AddClippingPlane(clip)
  258 +
  259 + self.color_transfer = color_transfer
  260 +
  261 + volume_properties = vtk.vtkVolumeProperty()
  262 + #volume_properties.IndependentComponentsOn()
  263 + if self.config['useShading']:
  264 + volume_properties.ShadeOn()
  265 + else:
  266 + volume_properties.ShadeOff()
  267 + volume_properties.SetAmbient(0.1)
  268 + volume_properties.SetDiffuse(0.6)
  269 + volume_properties.SetSpecular(0.5)
  270 + volume_properties.SetSpecularPower(44.0)
  271 +
  272 + volume_properties.SetInterpolationTypeToLinear()
  273 + volume_properties.SetColor(color_transfer)
  274 +
  275 + try:
  276 + volume_properties.SetScalarOpacity(opacity_transfer_func)
  277 + except NameError:
  278 + pass
  279 +
  280 + self.volume_properties = volume_properties
  281 +
  282 + volume = vtk.vtkVolume()
  283 + volume.SetMapper(volume_mapper)
  284 + volume.SetProperty(volume_properties)
  285 + self.volume = volume
  286 +
  287 + colour = self.CreateBackgroundColor()
  288 + ps.Publisher().sendMessage('Load volume into viewer', (volume, colour))
  289 +
  290 +
  291 + def TranslateScale(self, scale, value):
  292 + #if value < 0:
  293 + # valor = 2**16 - abs(value)
  294 + #else:
  295 + # valor = value
  296 +
  297 + return value - scale[0]
  298 +
  299 +
  300 +
  301 +
  302 +
... ...
invesalius/gui/default_viewers.py
... ... @@ -196,6 +196,7 @@ class VolumeToolPanel(wx.Panel):
196 196  
197 197 def OnToggleRaycasting(self, evt):
198 198 if self.button_raycasting.GetToggle():
  199 + #ps.Publisher().sendMessage('Create volume raycasting')
199 200 ps.Publisher().sendMessage('Show raycasting volume')
200 201 else:
201 202 ps.Publisher().sendMessage('Hide raycasting volume')
... ...
invesalius/gui/frame.py
... ... @@ -27,7 +27,6 @@ import default_tasks as tasks
27 27 import default_viewers as viewers
28 28  
29 29  
30   -
31 30 [ID_FILE_IMPORT, ID_FILE_LOAD_INTERNET, ID_FILE_SAVE, ID_FILE_PRINT] = [wx.NewId() for number in range(4)]
32 31  
33 32 class Frame(wx.Frame):
... ... @@ -322,7 +321,7 @@ class ObjectToolBar(wx.ToolBar):
322 321 BMP_TRANSLATE = wx.Bitmap("../icons/tool_translate_original.png", wx.BITMAP_TYPE_PNG)
323 322 BMP_ZOOM_IN = wx.Bitmap("../icons/tool_zoom_in_original.png", wx.BITMAP_TYPE_PNG)
324 323 BMP_ZOOM_OUT = wx.Bitmap("../icons/tool_zoom_out_original.png", wx.BITMAP_TYPE_PNG)
325   - BMP_CONTRAST = wx.Bitmap("../icons/tool_constrast.png", wx.BITMAP_TYPE_PNG)
  324 + BMP_CONTRAST = wx.Bitmap("../icons/tool_contrast.png", wx.BITMAP_TYPE_PNG)
326 325  
327 326 self.AddLabelTool(101, "Zoom in image", BMP_ZOOM_IN)
328 327 self.AddLabelTool(101, "Zoom out image", BMP_ZOOM_OUT)
... ...