Commit 333c4fd41e655127e2e6bc9ffd5b984809c2a112
1 parent
46b08a73
Exists in
master
and in
68 other branches
ENH: Select a curve in clut raycasting widget to indicate in wich curve the wwwl…
… operation will be applied
Showing
3 changed files
with
84 additions
and
22 deletions
Show diff stats
invesalius/data/volume.py
@@ -75,7 +75,7 @@ class Volume(): | @@ -75,7 +75,7 @@ class Volume(): | ||
75 | self.opacity_transfer_func = None | 75 | self.opacity_transfer_func = None |
76 | self.ww = None | 76 | self.ww = None |
77 | self.wl = None | 77 | self.wl = None |
78 | - self.n = 0 | 78 | + self.curve = 0 |
79 | self.plane = None | 79 | self.plane = None |
80 | self.plane_on = False | 80 | self.plane_on = False |
81 | 81 | ||
@@ -86,6 +86,8 @@ class Volume(): | @@ -86,6 +86,8 @@ class Volume(): | ||
86 | 'Hide raycasting volume') | 86 | 'Hide raycasting volume') |
87 | ps.Publisher().subscribe(self.OnUpdatePreset, | 87 | ps.Publisher().subscribe(self.OnUpdatePreset, |
88 | 'Update raycasting preset') | 88 | 'Update raycasting preset') |
89 | + ps.Publisher().subscribe(self.OnSetCurve, | ||
90 | + 'Set raycasting curve') | ||
89 | ps.Publisher().subscribe(self.OnSetWindowLevel, | 91 | ps.Publisher().subscribe(self.OnSetWindowLevel, |
90 | 'Set raycasting wwwl') | 92 | 'Set raycasting wwwl') |
91 | ps.Publisher().subscribe(self.Refresh, | 93 | ps.Publisher().subscribe(self.Refresh, |
@@ -157,6 +159,8 @@ class Volume(): | @@ -157,6 +159,8 @@ class Volume(): | ||
157 | ps.Publisher.sendMessage('Change volume viewer background colour', colour) | 159 | ps.Publisher.sendMessage('Change volume viewer background colour', colour) |
158 | ps.Publisher.sendMessage('Change volume viewer gui colour', colour) | 160 | ps.Publisher.sendMessage('Change volume viewer gui colour', colour) |
159 | 161 | ||
162 | + def OnSetCurve(self, pubsub_evt): | ||
163 | + self.curve = pubsub_evt.data | ||
160 | 164 | ||
161 | def OnSetRelativeWindowLevel(self, pubsub_evt): | 165 | def OnSetRelativeWindowLevel(self, pubsub_evt): |
162 | diff_ww, diff_wl = pubsub_evt.data | 166 | diff_ww, diff_wl = pubsub_evt.data |
@@ -170,13 +174,13 @@ class Volume(): | @@ -170,13 +174,13 @@ class Volume(): | ||
170 | 174 | ||
171 | def OnSetWindowLevel(self, pubsub_evt): | 175 | def OnSetWindowLevel(self, pubsub_evt): |
172 | ww, wl, n = pubsub_evt.data | 176 | ww, wl, n = pubsub_evt.data |
173 | - self.n = n | 177 | + self.curve = n |
174 | self.SetWWWL(ww,wl) | 178 | self.SetWWWL(ww,wl) |
175 | 179 | ||
176 | def SetWWWL(self, ww, wl): | 180 | def SetWWWL(self, ww, wl): |
177 | 181 | ||
178 | if self.config['advancedCLUT']: | 182 | if self.config['advancedCLUT']: |
179 | - curve = self.config['16bitClutCurves'][self.n] | 183 | + curve = self.config['16bitClutCurves'][self.curve] |
180 | 184 | ||
181 | p1 = curve[0] | 185 | p1 = curve[0] |
182 | p2 = curve[-1] | 186 | p2 = curve[-1] |
invesalius/gui/default_viewers.py
@@ -26,7 +26,7 @@ import data.viewer_volume as volume_viewer | @@ -26,7 +26,7 @@ import data.viewer_volume as volume_viewer | ||
26 | import widgets.slice_menu as slice_menu_ | 26 | import widgets.slice_menu as slice_menu_ |
27 | 27 | ||
28 | from gui.widgets.clut_raycasting import CLUTRaycastingWidget, \ | 28 | from gui.widgets.clut_raycasting import CLUTRaycastingWidget, \ |
29 | - EVT_CLUT_POINT_CHANGED | 29 | + EVT_CLUT_POINT_CHANGED, EVT_CLUT_CURVE_SELECTED |
30 | 30 | ||
31 | class Panel(wx.Panel): | 31 | class Panel(wx.Panel): |
32 | def __init__(self, parent): | 32 | def __init__(self, parent): |
@@ -235,6 +235,7 @@ class VolumeInteraction(wx.Panel): | @@ -235,6 +235,7 @@ class VolumeInteraction(wx.Panel): | ||
235 | 235 | ||
236 | def __bind_events_wx(self): | 236 | def __bind_events_wx(self): |
237 | self.clut_raycasting.Bind(EVT_CLUT_POINT_CHANGED, self.OnPointChanged) | 237 | self.clut_raycasting.Bind(EVT_CLUT_POINT_CHANGED, self.OnPointChanged) |
238 | + self.clut_raycasting.Bind(EVT_CLUT_CURVE_SELECTED , self.OnCurveSelected) | ||
238 | #self.Bind(wx.EVT_SIZE, self.OnSize) | 239 | #self.Bind(wx.EVT_SIZE, self.OnSize) |
239 | #self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize) | 240 | #self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize) |
240 | 241 | ||
@@ -242,6 +243,9 @@ class VolumeInteraction(wx.Panel): | @@ -242,6 +243,9 @@ class VolumeInteraction(wx.Panel): | ||
242 | ps.Publisher.sendMessage('Set raycasting refresh', None) | 243 | ps.Publisher.sendMessage('Set raycasting refresh', None) |
243 | ps.Publisher().sendMessage('Render volume viewer', None) | 244 | ps.Publisher().sendMessage('Render volume viewer', None) |
244 | 245 | ||
246 | + def OnCurveSelected(self, evt): | ||
247 | + ps.Publisher.sendMessage('Set raycasting curve', evt.GetCurve()) | ||
248 | + | ||
245 | import wx.lib.platebtn as pbtn | 249 | import wx.lib.platebtn as pbtn |
246 | import wx.lib.buttons as btn | 250 | import wx.lib.buttons as btn |
247 | import wx.lib.pubsub as ps | 251 | import wx.lib.pubsub as ps |
invesalius/gui/widgets/clut_raycasting.py
@@ -20,6 +20,7 @@ HISTOGRAM_FILL_COLOUR = (0.25, 0.25, 0.25) | @@ -20,6 +20,7 @@ HISTOGRAM_FILL_COLOUR = (0.25, 0.25, 0.25) | ||
20 | BACKGROUND_TEXT_COLOUR_RGBA = (1, 0, 0, 0.5) | 20 | BACKGROUND_TEXT_COLOUR_RGBA = (1, 0, 0, 0.5) |
21 | GRADIENT_RGBA = 0.75 | 21 | GRADIENT_RGBA = 0.75 |
22 | RADIUS = 5 | 22 | RADIUS = 5 |
23 | +SELECTION_SIZE = 10 | ||
23 | 24 | ||
24 | class Node(object): | 25 | class Node(object): |
25 | """ | 26 | """ |
@@ -41,8 +42,13 @@ class Curve(object): | @@ -41,8 +42,13 @@ class Curve(object): | ||
41 | def __init__(self): | 42 | def __init__(self): |
42 | self.wl = 0 | 43 | self.wl = 0 |
43 | self.ww = 0 | 44 | self.ww = 0 |
45 | + self.wl_px = 0 | ||
44 | self.nodes = [] | 46 | self.nodes = [] |
45 | 47 | ||
48 | + def CalculateWWWl(self): | ||
49 | + self.ww = self.nodes[-1].graylevel - self.nodes[0].graylevel | ||
50 | + self.wl = self.nodes[0].graylevel + self.ww / 2.0 | ||
51 | + | ||
46 | class CLUTRaycastingWidget(wx.Panel): | 52 | class CLUTRaycastingWidget(wx.Panel): |
47 | """ | 53 | """ |
48 | This class represents the frame where images is showed | 54 | This class represents the frame where images is showed |
@@ -65,6 +71,7 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -65,6 +71,7 @@ class CLUTRaycastingWidget(wx.Panel): | ||
65 | self.to_draw_points = 0 | 71 | self.to_draw_points = 0 |
66 | self.histogram_pixel_points = [[0,0]] | 72 | self.histogram_pixel_points = [[0,0]] |
67 | self.histogram_array = [100,100] | 73 | self.histogram_array = [100,100] |
74 | + self.previous_wl = 0 | ||
68 | self.CalculatePixelPoints() | 75 | self.CalculatePixelPoints() |
69 | self.dragged = False | 76 | self.dragged = False |
70 | self.point_dragged = None | 77 | self.point_dragged = None |
@@ -104,18 +111,27 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -104,18 +111,27 @@ class CLUTRaycastingWidget(wx.Panel): | ||
104 | pass | 111 | pass |
105 | 112 | ||
106 | def OnClick(self, evt): | 113 | def OnClick(self, evt): |
107 | - point = self._has_clicked_in_a_point(evt.GetPositionTuple()) | 114 | + x, y = evt.GetPositionTuple() |
115 | + point = self._has_clicked_in_a_point((x, y)) | ||
108 | # A point has been selected. It can be dragged. | 116 | # A point has been selected. It can be dragged. |
109 | if point: | 117 | if point: |
110 | self.dragged = True | 118 | self.dragged = True |
111 | self.point_dragged = point | 119 | self.point_dragged = point |
112 | self.Refresh() | 120 | self.Refresh() |
113 | return | 121 | return |
122 | + curve = self._has_clicked_in_selection_curve((x, y)) | ||
123 | + if curve is not None: | ||
124 | + print "Selecionou a curva", curve | ||
125 | + self.dragged = True | ||
126 | + self.previous_wl = x | ||
127 | + self.curve_dragged = curve | ||
128 | + evt = CLUTEvent(myEVT_CLUT_CURVE_SELECTED, self.GetId(), curve) | ||
129 | + self.GetEventHandler().ProcessEvent(evt) | ||
130 | + return | ||
114 | else: | 131 | else: |
115 | - p = self._has_clicked_in_line(evt.GetPositionTuple()) | 132 | + p = self._has_clicked_in_line((x, y)) |
116 | # The user clicked in the line. Insert a new point. | 133 | # The user clicked in the line. Insert a new point. |
117 | if p: | 134 | if p: |
118 | - x, y = evt.GetPositionTuple() | ||
119 | n, p = p | 135 | n, p = p |
120 | self.points[n].insert(p, {'x': 0, 'y': 0}) | 136 | self.points[n].insert(p, {'x': 0, 'y': 0}) |
121 | self.colours[n].insert(p, {'red': 0, 'green': 0, 'blue': 0}) | 137 | self.colours[n].insert(p, {'red': 0, 'green': 0, 'blue': 0}) |
@@ -131,7 +147,7 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -131,7 +147,7 @@ class CLUTRaycastingWidget(wx.Panel): | ||
131 | self.curves[n].nodes.insert(p, node) | 147 | self.curves[n].nodes.insert(p, node) |
132 | 148 | ||
133 | self.Refresh() | 149 | self.Refresh() |
134 | - nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) | 150 | + nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), n) |
135 | self.GetEventHandler().ProcessEvent(nevt) | 151 | self.GetEventHandler().ProcessEvent(nevt) |
136 | return | 152 | return |
137 | evt.Skip() | 153 | evt.Skip() |
@@ -152,7 +168,7 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -152,7 +168,7 @@ class CLUTRaycastingWidget(wx.Panel): | ||
152 | print self.curves[i].nodes | 168 | print self.curves[i].nodes |
153 | self.curves[i].nodes[j].colour = (r, g, b) | 169 | self.curves[i].nodes[j].colour = (r, g, b) |
154 | self.Refresh() | 170 | self.Refresh() |
155 | - nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) | 171 | + nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), i) |
156 | self.GetEventHandler().ProcessEvent(nevt) | 172 | self.GetEventHandler().ProcessEvent(nevt) |
157 | return | 173 | return |
158 | evt.Skip() | 174 | evt.Skip() |
@@ -167,7 +183,7 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -167,7 +183,7 @@ class CLUTRaycastingWidget(wx.Panel): | ||
167 | print "RightClick", i, j | 183 | print "RightClick", i, j |
168 | self.RemovePoint(i, j) | 184 | self.RemovePoint(i, j) |
169 | self.Refresh() | 185 | self.Refresh() |
170 | - nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) | 186 | + nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), i) |
171 | self.GetEventHandler().ProcessEvent(nevt) | 187 | self.GetEventHandler().ProcessEvent(nevt) |
172 | return | 188 | return |
173 | evt.Skip() | 189 | evt.Skip() |
@@ -178,10 +194,13 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -178,10 +194,13 @@ class CLUTRaycastingWidget(wx.Panel): | ||
178 | been occurred in the preset points. | 194 | been occurred in the preset points. |
179 | """ | 195 | """ |
180 | if self.to_render: | 196 | if self.to_render: |
181 | - evt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) | 197 | + evt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), 0) |
182 | self.GetEventHandler().ProcessEvent(evt) | 198 | self.GetEventHandler().ProcessEvent(evt) |
183 | self.dragged = False | 199 | self.dragged = False |
200 | + self.curve_dragged = None | ||
201 | + self.point_dragged = None | ||
184 | self.to_render = False | 202 | self.to_render = False |
203 | + self.previous_wl = 0 | ||
185 | 204 | ||
186 | def OnWheel(self, evt): | 205 | def OnWheel(self, evt): |
187 | """ | 206 | """ |
@@ -197,11 +216,11 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -197,11 +216,11 @@ class CLUTRaycastingWidget(wx.Panel): | ||
197 | 216 | ||
198 | def OnMotion(self, evt): | 217 | def OnMotion(self, evt): |
199 | # User dragging a point | 218 | # User dragging a point |
200 | - if self.dragged: | 219 | + x = evt.GetX() |
220 | + y = evt.GetY() | ||
221 | + if self.dragged and self.point_dragged: | ||
201 | self.to_render = True | 222 | self.to_render = True |
202 | i,j = self.point_dragged | 223 | i,j = self.point_dragged |
203 | - x = evt.GetX() | ||
204 | - y = evt.GetY() | ||
205 | 224 | ||
206 | width, height= self.GetVirtualSizeTuple() | 225 | width, height= self.GetVirtualSizeTuple() |
207 | 226 | ||
@@ -234,9 +253,25 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -234,9 +253,25 @@ class CLUTRaycastingWidget(wx.Panel): | ||
234 | self.curves[i].nodes[j].y = y | 253 | self.curves[i].nodes[j].y = y |
235 | self.curves[i].nodes[j].graylevel = graylevel | 254 | self.curves[i].nodes[j].graylevel = graylevel |
236 | self.curves[i].nodes[j].opacity = opacity | 255 | self.curves[i].nodes[j].opacity = opacity |
256 | + for curve in self.curves: | ||
257 | + curve.CalculateWWWl() | ||
258 | + curve.wl_px = (self.HounsfieldToPixel(curve.wl), | ||
259 | + self.OpacityToPixel(0)) | ||
237 | self.Refresh() | 260 | self.Refresh() |
238 | - evt = CLUTEvent(myEVT_CLUT_POINT , self.GetId()) | 261 | + evt = CLUTEvent(myEVT_CLUT_POINT , self.GetId(), i) |
239 | self.GetEventHandler().ProcessEvent(evt) | 262 | self.GetEventHandler().ProcessEvent(evt) |
263 | + | ||
264 | + elif self.dragged and self.curve_dragged is not None: | ||
265 | + curve = self.curves[self.curve_dragged] | ||
266 | + curve.wl = self.PixelToHounsfield(x) | ||
267 | + curve.wl_px = x, self.OpacityToPixel(0) | ||
268 | + for node in curve.nodes: | ||
269 | + node.x += (x - self.previous_wl) | ||
270 | + node.graylevel = self.PixelToHounsfield(node.x) | ||
271 | + ps.Publisher().sendMessage('Set raycasting wwwl', | ||
272 | + (curve.ww, curve.wl, self.curve_dragged)) | ||
273 | + self.previous_wl = x | ||
274 | + self.Refresh() | ||
240 | else: | 275 | else: |
241 | evt.Skip() | 276 | evt.Skip() |
242 | 277 | ||
@@ -257,10 +292,18 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -257,10 +292,18 @@ class CLUTRaycastingWidget(wx.Panel): | ||
257 | """ | 292 | """ |
258 | for i, curve in enumerate(self.curves): | 293 | for i, curve in enumerate(self.curves): |
259 | for j, node in enumerate(curve.nodes): | 294 | for j, node in enumerate(curve.nodes): |
260 | - if self._calculate_distance(node, position) <= RADIUS: | 295 | + if self._calculate_distance((node.x, node.y), position) <= RADIUS: |
261 | return (i, j) | 296 | return (i, j) |
262 | return None | 297 | return None |
263 | 298 | ||
299 | + def _has_clicked_in_selection_curve(self, position): | ||
300 | + x, y = position | ||
301 | + for i, curve in enumerate(self.curves): | ||
302 | + if self._calculate_distance(curve.wl_px, position) <= RADIUS: | ||
303 | + return i | ||
304 | + return None | ||
305 | + | ||
306 | + | ||
264 | def _has_clicked_in_line(self, position): | 307 | def _has_clicked_in_line(self, position): |
265 | """ | 308 | """ |
266 | Verify if was clicked in a line. If yes, it returns the insertion | 309 | Verify if was clicked in a line. If yes, it returns the insertion |
@@ -278,7 +321,7 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -278,7 +321,7 @@ class CLUTRaycastingWidget(wx.Panel): | ||
278 | return None | 321 | return None |
279 | 322 | ||
280 | def _calculate_distance(self, p1, p2): | 323 | def _calculate_distance(self, p1, p2): |
281 | - return ((p1.x-p2[0])**2 + (p1.y-p2[1])**2) ** 0.5 | 324 | + return ((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) ** 0.5 |
282 | 325 | ||
283 | def RemovePoint(self, i, j): | 326 | def RemovePoint(self, i, j): |
284 | """ | 327 | """ |
@@ -413,12 +456,13 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -413,12 +456,13 @@ class CLUTRaycastingWidget(wx.Panel): | ||
413 | 456 | ||
414 | def _draw_selection_curve(self, ctx, height): | 457 | def _draw_selection_curve(self, ctx, height): |
415 | for curve in self.curves: | 458 | for curve in self.curves: |
416 | - x_center = (curve.nodes[0].x + curve.nodes[-1].x)/2.0 | ||
417 | - ctx.set_source_rgb(*LINE_COLOUR) | ||
418 | - ctx.stroke() | ||
419 | - ctx.rectangle(x_center-5, height, 10, 10) | 459 | + x_center, y_center = curve.wl_px |
460 | + ctx.rectangle(x_center-SELECTION_SIZE/2.0, y_center, SELECTION_SIZE, | ||
461 | + SELECTION_SIZE) | ||
420 | ctx.set_source_rgb(0,0,0) | 462 | ctx.set_source_rgb(0,0,0) |
421 | ctx.fill_preserve() | 463 | ctx.fill_preserve() |
464 | + ctx.set_source_rgb(*LINE_COLOUR) | ||
465 | + ctx.stroke() | ||
422 | 466 | ||
423 | def Render(self, dc): | 467 | def Render(self, dc): |
424 | ctx = wx.lib.wxcairo.ContextFromDC(dc) | 468 | ctx = wx.lib.wxcairo.ContextFromDC(dc) |
@@ -488,6 +532,9 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -488,6 +532,9 @@ class CLUTRaycastingWidget(wx.Panel): | ||
488 | node.opacity = point['y'] | 532 | node.opacity = point['y'] |
489 | node.colour = colour['red'], colour['green'], colour['blue'] | 533 | node.colour = colour['red'], colour['green'], colour['blue'] |
490 | curve.nodes.append(node) | 534 | curve.nodes.append(node) |
535 | + curve.CalculateWWWl() | ||
536 | + curve.wl_px = (self.HounsfieldToPixel(curve.wl), | ||
537 | + self.OpacityToPixel(0)) | ||
491 | self.curves.append(curve) | 538 | self.curves.append(curve) |
492 | self._build_histogram() | 539 | self._build_histogram() |
493 | 540 | ||
@@ -551,8 +598,11 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -551,8 +598,11 @@ class CLUTRaycastingWidget(wx.Panel): | ||
551 | self.histogram_array = h_array | 598 | self.histogram_array = h_array |
552 | 599 | ||
553 | class CLUTEvent(wx.PyCommandEvent): | 600 | class CLUTEvent(wx.PyCommandEvent): |
554 | - def __init__(self , evtType, id): | 601 | + def __init__(self , evtType, id, curve): |
555 | wx.PyCommandEvent.__init__(self, evtType, id) | 602 | wx.PyCommandEvent.__init__(self, evtType, id) |
603 | + self.curve = curve | ||
604 | + def GetCurve(self): | ||
605 | + return self.curve | ||
556 | 606 | ||
557 | 607 | ||
558 | # Occurs when CLUT is sliding | 608 | # Occurs when CLUT is sliding |
@@ -570,3 +620,7 @@ EVT_CLUT_POINT = wx.PyEventBinder(myEVT_CLUT_POINT, 1) | @@ -570,3 +620,7 @@ EVT_CLUT_POINT = wx.PyEventBinder(myEVT_CLUT_POINT, 1) | ||
570 | # Occurs when a CLUT point was changed | 620 | # Occurs when a CLUT point was changed |
571 | myEVT_CLUT_POINT_CHANGED = wx.NewEventType() | 621 | myEVT_CLUT_POINT_CHANGED = wx.NewEventType() |
572 | EVT_CLUT_POINT_CHANGED = wx.PyEventBinder(myEVT_CLUT_POINT_CHANGED, 1) | 622 | EVT_CLUT_POINT_CHANGED = wx.PyEventBinder(myEVT_CLUT_POINT_CHANGED, 1) |
623 | + | ||
624 | +# Selected a curve | ||
625 | +myEVT_CLUT_CURVE_SELECTED = wx.NewEventType() | ||
626 | +EVT_CLUT_CURVE_SELECTED = wx.PyEventBinder(myEVT_CLUT_CURVE_SELECTED, 1) |