Commit 9ab6e22819730b7536214aa6851bbabb05f75135

Authored by tfmoraes
1 parent a8886d17

ADD: Support to MIP and save raycasting presets

.gitattributes
@@ -14,6 +14,7 @@ docs/devel/example_singleton.py -text @@ -14,6 +14,7 @@ docs/devel/example_singleton.py -text
14 docs/devel/example_singleton_pubsub.py -text 14 docs/devel/example_singleton_pubsub.py -text
15 docs/devel/sendmessages.txt -text 15 docs/devel/sendmessages.txt -text
16 docs/devel/subscribes.txt -text 16 docs/devel/subscribes.txt -text
  17 +icons/Floppy.png -text
17 icons/annotation.png -text 18 icons/annotation.png -text
18 icons/brush_circle.jpg -text 19 icons/brush_circle.jpg -text
19 icons/brush_square.jpg -text 20 icons/brush_square.jpg -text
icons/Floppy.png 0 → 100755

3.29 KB

invesalius/control.py
@@ -403,9 +403,11 @@ class Controller(): @@ -403,9 +403,11 @@ class Controller():
403 ps.Publisher().sendMessage("Hide raycasting volume") 403 ps.Publisher().sendMessage("Hide raycasting volume")
404 404
405 def SaveRaycastingPreset(self, pubsub_evt): 405 def SaveRaycastingPreset(self, pubsub_evt):
406 - preset_name = pubsub_evt.data + '.plist' 406 + preset_name = pubsub_evt.data
407 preset = prj.Project().raycasting_preset 407 preset = prj.Project().raycasting_preset
408 - preset_dir = os.path.join(const.USER_RAYCASTING_PRESETS_DIRECTORY, preset_name) 408 + preset['name'] = preset_name
  409 + preset_dir = os.path.join(const.USER_RAYCASTING_PRESETS_DIRECTORY,
  410 + preset_name + '.plist')
409 plistlib.writePlist(preset, preset_dir) 411 plistlib.writePlist(preset, preset_dir)
410 412
411 413
invesalius/data/volume.py
@@ -157,6 +157,7 @@ class Volume(): @@ -157,6 +157,7 @@ class Volume():
157 157
158 # Update other information 158 # Update other information
159 self.SetShading() 159 self.SetShading()
  160 + self.SetTypeRaycasting()
160 colour = self.GetBackgroundColour() 161 colour = self.GetBackgroundColour()
161 ps.Publisher.sendMessage('Change volume viewer background colour', colour) 162 ps.Publisher.sendMessage('Change volume viewer background colour', colour)
162 ps.Publisher.sendMessage('Change volume viewer gui colour', colour) 163 ps.Publisher.sendMessage('Change volume viewer gui colour', colour)
@@ -385,6 +386,14 @@ class Volume(): @@ -385,6 +386,14 @@ class Volume():
385 self.volume_properties.SetSpecular(shading['specular']) 386 self.volume_properties.SetSpecular(shading['specular'])
386 self.volume_properties.SetSpecularPower(shading['specularPower']) 387 self.volume_properties.SetSpecularPower(shading['specularPower'])
387 388
  389 + def SetTypeRaycasting(self):
  390 + if self.config['name'].upper().startswith('MIP'):
  391 + print "MIP"
  392 + self.volume_mapper.SetBlendModeToMaximumIntensity()
  393 + else:
  394 + print "Composite"
  395 + self.volume_mapper.SetBlendModeToComposite()
  396 +
388 def ApplyConvolution(self, imagedata, update_progress = None): 397 def ApplyConvolution(self, imagedata, update_progress = None):
389 number_filters = len(self.config['convolutionFilters']) 398 number_filters = len(self.config['convolutionFilters'])
390 if number_filters: 399 if number_filters:
@@ -457,24 +466,23 @@ class Volume(): @@ -457,24 +466,23 @@ class Volume():
457 volume_mapper.SetVolumeRayCastFunction(composite_function) 466 volume_mapper.SetVolumeRayCastFunction(composite_function)
458 #volume_mapper.SetGradientEstimator(gradientEstimator) 467 #volume_mapper.SetGradientEstimator(gradientEstimator)
459 volume_mapper.IntermixIntersectingGeometryOn() 468 volume_mapper.IntermixIntersectingGeometryOn()
  469 + self.volume_mapper = volume_mapper
460 else: 470 else:
461 volume_mapper = vtk.vtkFixedPointVolumeRayCastMapper() 471 volume_mapper = vtk.vtkFixedPointVolumeRayCastMapper()
462 #volume_mapper.AutoAdjustSampleDistancesOff() 472 #volume_mapper.AutoAdjustSampleDistancesOff()
463 - 473 + self.volume_mapper = volume_mapper
  474 + self.SetTypeRaycasting()
464 volume_mapper.IntermixIntersectingGeometryOn() 475 volume_mapper.IntermixIntersectingGeometryOn()
465 - #volume_mapper.SetBlendModeToMaximumIntensity() 476 +
466 volume_mapper.SetInput(image2) 477 volume_mapper.SetInput(image2)
467 - self.volume_mapper = volume_mapper  
468 -  
469 -  
470 - 478 +
471 # TODO: Look to this 479 # TODO: Look to this
472 #volume_mapper_hw = vtk.vtkVolumeTextureMapper3D() 480 #volume_mapper_hw = vtk.vtkVolumeTextureMapper3D()
473 #volume_mapper_hw.SetInput(image2) 481 #volume_mapper_hw.SetInput(image2)
474 482
475 #Cut Plane 483 #Cut Plane
476 #CutPlane(image2, volume_mapper) 484 #CutPlane(image2, volume_mapper)
477 - 485 +
478 #self.color_transfer = color_transfer 486 #self.color_transfer = color_transfer
479 487
480 volume_properties = vtk.vtkVolumeProperty() 488 volume_properties = vtk.vtkVolumeProperty()
invesalius/gui/widgets/clut_raycasting.py
@@ -19,12 +19,17 @@ @@ -19,12 +19,17 @@
19 19
20 import bisect 20 import bisect
21 import math 21 import math
  22 +import os
22 import sys 23 import sys
23 24
24 import cairo 25 import cairo
25 import numpy 26 import numpy
26 import wx 27 import wx
27 import wx.lib.wxcairo 28 import wx.lib.wxcairo
  29 +import wx.lib.pubsub as ps
  30 +
  31 +import gui.dialogs as dialog
  32 +import constants as const
28 33
29 FONT_COLOUR = (1, 1, 1) 34 FONT_COLOUR = (1, 1, 1)
30 LINE_COLOUR = (0.5, 0.5, 0.5) 35 LINE_COLOUR = (0.5, 0.5, 0.5)
@@ -37,6 +42,7 @@ TEXT_COLOUR = (1, 1, 1) @@ -37,6 +42,7 @@ TEXT_COLOUR = (1, 1, 1)
37 GRADIENT_RGBA = 0.75 42 GRADIENT_RGBA = 0.75
38 RADIUS = 5 43 RADIUS = 5
39 SELECTION_SIZE = 10 44 SELECTION_SIZE = 10
  45 +TOOLBAR_SIZE = 30
40 46
41 class Node(object): 47 class Node(object):
42 """ 48 """
@@ -74,6 +80,32 @@ class Histogram(object): @@ -74,6 +80,32 @@ class Histogram(object):
74 def __init__(self): 80 def __init__(self):
75 self.init = -1024 81 self.init = -1024
76 self.end = 2000 82 self.end = 2000
  83 + self.points = ()
  84 +
  85 +
  86 +class Button(object):
  87 + """
  88 + The button in the clut raycasting.
  89 + """
  90 + def __init__(self):
  91 + self.image = None
  92 + self.position = (0, 0)
  93 + self.size = (24, 24)
  94 +
  95 + def HasClicked(self, position):
  96 + """
  97 + Test if the button was clicked.
  98 + """
  99 + print self.position
  100 + print self.size
  101 + m_x, m_y = position
  102 + i_x, i_y = self.position
  103 + w, h = self.size
  104 + if i_x < m_x < i_x + w and \
  105 + i_y < m_y < i_y + h:
  106 + return True
  107 + else:
  108 + return False
77 109
78 110
79 class CLUTRaycastingWidget(wx.Panel): 111 class CLUTRaycastingWidget(wx.Panel):
@@ -98,13 +130,14 @@ class CLUTRaycastingWidget(wx.Panel): @@ -98,13 +130,14 @@ class CLUTRaycastingWidget(wx.Panel):
98 self.previous_wl = 0 130 self.previous_wl = 0
99 self.to_render = False 131 self.to_render = False
100 self.dragged = False 132 self.dragged = False
  133 + self.middle_drag = False
101 self.to_draw_points = 0 134 self.to_draw_points = 0
102 self.point_dragged = None 135 self.point_dragged = None
103 self.curve_dragged = None 136 self.curve_dragged = None
104 - self.histogram_pixel_points = [[0,0]]  
105 self.histogram_array = [100,100] 137 self.histogram_array = [100,100]
106 self.CalculatePixelPoints() 138 self.CalculatePixelPoints()
107 self.__bind_events_wx() 139 self.__bind_events_wx()
  140 + self._build_buttons()
108 self.Show() 141 self.Show()
109 142
110 def SetRange(self, range): 143 def SetRange(self, range):
@@ -128,12 +161,19 @@ class CLUTRaycastingWidget(wx.Panel): @@ -128,12 +161,19 @@ class CLUTRaycastingWidget(wx.Panel):
128 self.Bind(wx.EVT_PAINT, self.OnPaint) 161 self.Bind(wx.EVT_PAINT, self.OnPaint)
129 self.Bind(wx.EVT_SIZE, self.OnSize) 162 self.Bind(wx.EVT_SIZE, self.OnSize)
130 self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel) 163 self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel)
  164 + self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleClick)
  165 + self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleRelease)
131 166
132 def OnEraseBackground(self, evt): 167 def OnEraseBackground(self, evt):
133 pass 168 pass
134 169
135 def OnClick(self, evt): 170 def OnClick(self, evt):
136 x, y = evt.GetPositionTuple() 171 x, y = evt.GetPositionTuple()
  172 + if self.save_button.HasClicked(evt.GetPositionTuple()):
  173 + print "Salvando"
  174 + filename = dialog.ShowSavePresetDialog()
  175 + if filename:
  176 + ps.Publisher().sendMessage('Save raycasting preset', filename)
137 point = self._has_clicked_in_a_point((x, y)) 177 point = self._has_clicked_in_a_point((x, y))
138 # A point has been selected. It can be dragged. 178 # A point has been selected. It can be dragged.
139 if point: 179 if point:
@@ -244,69 +284,25 @@ class CLUTRaycastingWidget(wx.Panel): @@ -244,69 +284,25 @@ class CLUTRaycastingWidget(wx.Panel):
244 self.SetRange((init, end)) 284 self.SetRange((init, end))
245 self.Refresh() 285 self.Refresh()
246 286
  287 + def OnMiddleClick(self, evt):
  288 + self.middle_drag = True
  289 + self.last_position = evt.GetX()
  290 +
  291 + def OnMiddleRelease(self, evt):
  292 + self.middle_drag = False
  293 +
247 def OnMotion(self, evt): 294 def OnMotion(self, evt):
248 # User dragging a point 295 # User dragging a point
249 x = evt.GetX() 296 x = evt.GetX()
250 y = evt.GetY() 297 y = evt.GetY()
251 if self.dragged and self.point_dragged: 298 if self.dragged and self.point_dragged:
252 - self.to_render = True  
253 - i,j = self.point_dragged  
254 -  
255 - width, height= self.GetVirtualSizeTuple()  
256 -  
257 - if y >= height - self.padding:  
258 - y = height - self.padding  
259 -  
260 - if y <= self.padding:  
261 - y = self.padding  
262 -  
263 - if x < 0:  
264 - x = 0  
265 -  
266 - if x > width:  
267 - x = width  
268 -  
269 - # A point must be greater than the previous one, but the first one  
270 - if j > 0 and x <= self.curves[i].nodes[j-1].x:  
271 - x = self.curves[i].nodes[j-1].x + 1  
272 -  
273 - # A point must be lower than the previous one, but the last one  
274 - if j < len(self.curves[i].nodes) -1 \  
275 - and x >= self.curves[i].nodes[j+1].x:  
276 - x = self.curves[i].nodes[j+1].x - 1  
277 -  
278 - graylevel = self.PixelToHounsfield(x)  
279 - opacity = self.PixelToOpacity(y)  
280 - self.points[i][j]['x'] = graylevel  
281 - self.points[i][j]['y'] = opacity  
282 - self.curves[i].nodes[j].x = x  
283 - self.curves[i].nodes[j].y = y  
284 - self.curves[i].nodes[j].graylevel = graylevel  
285 - self.curves[i].nodes[j].opacity = opacity  
286 - for curve in self.curves:  
287 - curve.CalculateWWWl()  
288 - curve.wl_px = (self.HounsfieldToPixel(curve.wl),  
289 - self.OpacityToPixel(0))  
290 - self.Refresh()  
291 -  
292 - # A point in the preset has been changed, raising a event  
293 - evt = CLUTEvent(myEVT_CLUT_POINT_MOVE , self.GetId(), i)  
294 - self.GetEventHandler().ProcessEvent(evt)  
295 - 299 + self._move_node(x, y)
296 elif self.dragged and self.curve_dragged is not None: 300 elif self.dragged and self.curve_dragged is not None:
297 - curve = self.curves[self.curve_dragged]  
298 - curve.wl = self.PixelToHounsfield(x)  
299 - curve.wl_px = x, self.OpacityToPixel(0)  
300 - for node in curve.nodes:  
301 - node.x += (x - self.previous_wl)  
302 - node.graylevel = self.PixelToHounsfield(node.x)  
303 -  
304 - # The window level has been changed, raising a event!  
305 - evt = CLUTEvent(myEVT_CLUT_CURVE_WL_CHANGE, self.GetId(),  
306 - self.curve_dragged)  
307 - self.GetEventHandler().ProcessEvent(evt)  
308 -  
309 - self.previous_wl = x 301 + self._move_curve(x, y)
  302 + elif self.middle_drag:
  303 + d = self.PixelToHounsfield(x) - self.PixelToHounsfield(self.last_position)
  304 + self.SetRange((self.init - d, self.end - d))
  305 + self.last_position = x
310 self.Refresh() 306 self.Refresh()
311 else: 307 else:
312 evt.Skip() 308 evt.Skip()
@@ -371,9 +367,82 @@ class CLUTRaycastingWidget(wx.Panel): @@ -371,9 +367,82 @@ class CLUTRaycastingWidget(wx.Panel):
371 return (n, position) 367 return (n, position)
372 return None 368 return None
373 369
  370 + def _has_clicked_in_save(self, clicked_point):
  371 + x, y = clicked_point
  372 + print x, y
  373 + if self.padding < x < self.padding + 24 and \
  374 + self.padding < y < self.padding + 24:
  375 + return True
  376 + else:
  377 + return False
  378 +
374 def _calculate_distance(self, p1, p2): 379 def _calculate_distance(self, p1, p2):
375 return ((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) ** 0.5 380 return ((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) ** 0.5
376 381
  382 + def _move_node(self, x, y):
  383 + self.to_render = True
  384 + i,j = self.point_dragged
  385 +
  386 + width, height= self.GetVirtualSizeTuple()
  387 +
  388 + if y >= height - self.padding:
  389 + y = height - self.padding
  390 +
  391 + if y <= self.padding:
  392 + y = self.padding
  393 +
  394 + if x < 0:
  395 + x = 0
  396 +
  397 + if x > width:
  398 + x = width
  399 +
  400 + if x < TOOLBAR_SIZE:
  401 + x = TOOLBAR_SIZE
  402 +
  403 + # A point must be greater than the previous one, but the first one
  404 + if j > 0 and x <= self.curves[i].nodes[j-1].x:
  405 + x = self.curves[i].nodes[j-1].x + 1
  406 +
  407 + # A point must be lower than the previous one, but the last one
  408 + if j < len(self.curves[i].nodes) -1 \
  409 + and x >= self.curves[i].nodes[j+1].x:
  410 + x = self.curves[i].nodes[j+1].x - 1
  411 +
  412 + graylevel = self.PixelToHounsfield(x)
  413 + opacity = self.PixelToOpacity(y)
  414 + self.points[i][j]['x'] = graylevel
  415 + self.points[i][j]['y'] = opacity
  416 + self.curves[i].nodes[j].x = x
  417 + self.curves[i].nodes[j].y = y
  418 + self.curves[i].nodes[j].graylevel = graylevel
  419 + self.curves[i].nodes[j].opacity = opacity
  420 + for curve in self.curves:
  421 + curve.CalculateWWWl()
  422 + curve.wl_px = (self.HounsfieldToPixel(curve.wl),
  423 + self.OpacityToPixel(0))
  424 + self.Refresh()
  425 +
  426 + # A point in the preset has been changed, raising a event
  427 + evt = CLUTEvent(myEVT_CLUT_POINT_MOVE , self.GetId(), i)
  428 + self.GetEventHandler().ProcessEvent(evt)
  429 +
  430 + def _move_curve(self, x, y):
  431 + curve = self.curves[self.curve_dragged]
  432 + curve.wl = self.PixelToHounsfield(x)
  433 + curve.wl_px = x, self.OpacityToPixel(0)
  434 + for node in curve.nodes:
  435 + node.x += (x - self.previous_wl)
  436 + node.graylevel = self.PixelToHounsfield(node.x)
  437 +
  438 + # The window level has been changed, raising a event!
  439 + evt = CLUTEvent(myEVT_CLUT_CURVE_WL_CHANGE, self.GetId(),
  440 + self.curve_dragged)
  441 + self.GetEventHandler().ProcessEvent(evt)
  442 +
  443 + self.previous_wl = x
  444 + self.Refresh()
  445 +
377 def RemovePoint(self, i, j): 446 def RemovePoint(self, i, j):
378 """ 447 """
379 The point the point in the given i,j index 448 The point the point in the given i,j index
@@ -506,17 +575,17 @@ class CLUTRaycastingWidget(wx.Panel): @@ -506,17 +575,17 @@ class CLUTRaycastingWidget(wx.Panel):
506 575
507 def _draw_histogram(self, ctx, height): 576 def _draw_histogram(self, ctx, height):
508 # The histogram 577 # The histogram
509 - x,y = self.histogram_pixel_points[0] 578 + x,y = self.Histogram.points[0]
510 print "=>", x,y 579 print "=>", x,y
511 ctx.move_to(x,y) 580 ctx.move_to(x,y)
512 ctx.set_line_width(HISTOGRAM_LINE_WIDTH) 581 ctx.set_line_width(HISTOGRAM_LINE_WIDTH)
513 - for x,y in self.histogram_pixel_points: 582 + for x,y in self.Histogram.points:
514 ctx.line_to(x,y) 583 ctx.line_to(x,y)
515 ctx.set_source_rgb(*HISTOGRAM_LINE_COLOUR) 584 ctx.set_source_rgb(*HISTOGRAM_LINE_COLOUR)
516 ctx.stroke_preserve() 585 ctx.stroke_preserve()
517 ctx.line_to(x, height + self.padding) 586 ctx.line_to(x, height + self.padding)
518 ctx.line_to(self.HounsfieldToPixel(self.Histogram.init), height + self.padding) 587 ctx.line_to(self.HounsfieldToPixel(self.Histogram.init), height + self.padding)
519 - x,y = self.histogram_pixel_points[0] 588 + x,y = self.Histogram.points[0]
520 ctx.line_to(x, y) 589 ctx.line_to(x, y)
521 ctx.set_source_rgb(*HISTOGRAM_FILL_COLOUR) 590 ctx.set_source_rgb(*HISTOGRAM_FILL_COLOUR)
522 ctx.fill() 591 ctx.fill()
@@ -531,6 +600,20 @@ class CLUTRaycastingWidget(wx.Panel): @@ -531,6 +600,20 @@ class CLUTRaycastingWidget(wx.Panel):
531 ctx.set_source_rgb(*LINE_COLOUR) 600 ctx.set_source_rgb(*LINE_COLOUR)
532 ctx.stroke() 601 ctx.stroke()
533 602
  603 + def _draw_tool_bar(self, ctx, height):
  604 + ctx.rectangle(0, 0, TOOLBAR_SIZE, height + self.padding * 2)
  605 + ctx.set_source_rgb(0.1,0.1,0.1)
  606 + ctx.fill()
  607 + #ctx.set_source_rgb(1, 1, 1)
  608 + #ctx.stroke()
  609 + image = self.save_button.image
  610 + w, h = self.save_button.size
  611 + x = (TOOLBAR_SIZE - w) / 2.0
  612 + y = self.padding
  613 + self.save_button.position = (x, y)
  614 + ctx.set_source_surface(image, x, y)
  615 + ctx.paint()
  616 +
534 def Render(self, dc): 617 def Render(self, dc):
535 ctx = wx.lib.wxcairo.ContextFromDC(dc) 618 ctx = wx.lib.wxcairo.ContextFromDC(dc)
536 width, height= self.GetVirtualSizeTuple() 619 width, height= self.GetVirtualSizeTuple()
@@ -543,6 +626,7 @@ class CLUTRaycastingWidget(wx.Panel): @@ -543,6 +626,7 @@ class CLUTRaycastingWidget(wx.Panel):
543 self._draw_curves(ctx) 626 self._draw_curves(ctx)
544 self._draw_points(ctx) 627 self._draw_points(ctx)
545 self._draw_selection_curve(ctx, height) 628 self._draw_selection_curve(ctx, height)
  629 + self._draw_tool_bar(ctx, height)
546 if sys.platform != "darwin": 630 if sys.platform != "darwin":
547 if self.point_dragged: 631 if self.point_dragged:
548 self._draw_selected_point_text(ctx) 632 self._draw_selected_point_text(ctx)
@@ -557,15 +641,23 @@ class CLUTRaycastingWidget(wx.Panel): @@ -557,15 +641,23 @@ class CLUTRaycastingWidget(wx.Panel):
557 y_end = math.log(max(self.histogram_array)) 641 y_end = math.log(max(self.histogram_array))
558 proportion_x = width * 1.0 / (x_end - x_init) 642 proportion_x = width * 1.0 / (x_end - x_init)
559 proportion_y = height * 1.0 / (y_end - y_init) 643 proportion_y = height * 1.0 / (y_end - y_init)
560 - self.histogram_pixel_points = []  
561 - for i in xrange(len(self.histogram_array)): 644 + self.Histogram.points = []
  645 + for i in xrange(0, len(self.histogram_array), 5):
562 if self.histogram_array[i]: 646 if self.histogram_array[i]:
563 y = math.log(self.histogram_array[i]) 647 y = math.log(self.histogram_array[i])
564 else: 648 else:
565 y = 0 649 y = 0
566 x = self.HounsfieldToPixel(x_init + i) 650 x = self.HounsfieldToPixel(x_init + i)
567 y = height - y * proportion_y + self.padding 651 y = height - y * proportion_y + self.padding
568 - self.histogram_pixel_points.append((x, y)) 652 + self.Histogram.points.append((x, y))
  653 +
  654 + def _build_buttons(self):
  655 + img = cairo.ImageSurface.create_from_png(os.path.join(const.ICON_DIR, 'Floppy.png'))
  656 + width = img.get_width()
  657 + height = img.get_height()
  658 + self.save_button = Button()
  659 + self.save_button.image = img
  660 + self.save_button.size = (width, height)
569 661
570 def __sort_pixel_points(self): 662 def __sort_pixel_points(self):
571 """ 663 """
@@ -609,9 +701,9 @@ class CLUTRaycastingWidget(wx.Panel): @@ -609,9 +701,9 @@ class CLUTRaycastingWidget(wx.Panel):
609 Given a Hounsfield point returns a pixel point in the canvas. 701 Given a Hounsfield point returns a pixel point in the canvas.
610 """ 702 """
611 width,height = self.GetVirtualSizeTuple() 703 width,height = self.GetVirtualSizeTuple()
612 - width -= self.padding 704 + width -= (TOOLBAR_SIZE)
613 proportion = width * 1.0 / (self.end - self.init) 705 proportion = width * 1.0 / (self.end - self.init)
614 - x = (graylevel - self.init) * proportion 706 + x = (graylevel - self.init) * proportion + TOOLBAR_SIZE
615 return x 707 return x
616 708
617 def OpacityToPixel(self, opacity): 709 def OpacityToPixel(self, opacity):
@@ -628,9 +720,9 @@ class CLUTRaycastingWidget(wx.Panel): @@ -628,9 +720,9 @@ class CLUTRaycastingWidget(wx.Panel):
628 Translate from pixel point to Hounsfield scale. 720 Translate from pixel point to Hounsfield scale.
629 """ 721 """
630 width, height= self.GetVirtualSizeTuple() 722 width, height= self.GetVirtualSizeTuple()
631 - width -= self.padding 723 + width -= (TOOLBAR_SIZE)
632 proportion = width * 1.0 / (self.end - self.init) 724 proportion = width * 1.0 / (self.end - self.init)
633 - graylevel = x / proportion - abs(self.init) 725 + graylevel = (x - TOOLBAR_SIZE) / proportion - abs(self.init)
634 return graylevel 726 return graylevel
635 727
636 def PixelToOpacity(self, y): 728 def PixelToOpacity(self, y):