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 | 75 | self.opacity_transfer_func = None |
76 | 76 | self.ww = None |
77 | 77 | self.wl = None |
78 | - self.n = 0 | |
78 | + self.curve = 0 | |
79 | 79 | self.plane = None |
80 | 80 | self.plane_on = False |
81 | 81 | |
... | ... | @@ -86,6 +86,8 @@ class Volume(): |
86 | 86 | 'Hide raycasting volume') |
87 | 87 | ps.Publisher().subscribe(self.OnUpdatePreset, |
88 | 88 | 'Update raycasting preset') |
89 | + ps.Publisher().subscribe(self.OnSetCurve, | |
90 | + 'Set raycasting curve') | |
89 | 91 | ps.Publisher().subscribe(self.OnSetWindowLevel, |
90 | 92 | 'Set raycasting wwwl') |
91 | 93 | ps.Publisher().subscribe(self.Refresh, |
... | ... | @@ -157,6 +159,8 @@ class Volume(): |
157 | 159 | ps.Publisher.sendMessage('Change volume viewer background colour', colour) |
158 | 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 | 165 | def OnSetRelativeWindowLevel(self, pubsub_evt): |
162 | 166 | diff_ww, diff_wl = pubsub_evt.data |
... | ... | @@ -170,13 +174,13 @@ class Volume(): |
170 | 174 | |
171 | 175 | def OnSetWindowLevel(self, pubsub_evt): |
172 | 176 | ww, wl, n = pubsub_evt.data |
173 | - self.n = n | |
177 | + self.curve = n | |
174 | 178 | self.SetWWWL(ww,wl) |
175 | 179 | |
176 | 180 | def SetWWWL(self, ww, wl): |
177 | 181 | |
178 | 182 | if self.config['advancedCLUT']: |
179 | - curve = self.config['16bitClutCurves'][self.n] | |
183 | + curve = self.config['16bitClutCurves'][self.curve] | |
180 | 184 | |
181 | 185 | p1 = curve[0] |
182 | 186 | p2 = curve[-1] | ... | ... |
invesalius/gui/default_viewers.py
... | ... | @@ -26,7 +26,7 @@ import data.viewer_volume as volume_viewer |
26 | 26 | import widgets.slice_menu as slice_menu_ |
27 | 27 | |
28 | 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 | 31 | class Panel(wx.Panel): |
32 | 32 | def __init__(self, parent): |
... | ... | @@ -235,6 +235,7 @@ class VolumeInteraction(wx.Panel): |
235 | 235 | |
236 | 236 | def __bind_events_wx(self): |
237 | 237 | self.clut_raycasting.Bind(EVT_CLUT_POINT_CHANGED, self.OnPointChanged) |
238 | + self.clut_raycasting.Bind(EVT_CLUT_CURVE_SELECTED , self.OnCurveSelected) | |
238 | 239 | #self.Bind(wx.EVT_SIZE, self.OnSize) |
239 | 240 | #self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize) |
240 | 241 | |
... | ... | @@ -242,6 +243,9 @@ class VolumeInteraction(wx.Panel): |
242 | 243 | ps.Publisher.sendMessage('Set raycasting refresh', None) |
243 | 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 | 249 | import wx.lib.platebtn as pbtn |
246 | 250 | import wx.lib.buttons as btn |
247 | 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 | 20 | BACKGROUND_TEXT_COLOUR_RGBA = (1, 0, 0, 0.5) |
21 | 21 | GRADIENT_RGBA = 0.75 |
22 | 22 | RADIUS = 5 |
23 | +SELECTION_SIZE = 10 | |
23 | 24 | |
24 | 25 | class Node(object): |
25 | 26 | """ |
... | ... | @@ -41,8 +42,13 @@ class Curve(object): |
41 | 42 | def __init__(self): |
42 | 43 | self.wl = 0 |
43 | 44 | self.ww = 0 |
45 | + self.wl_px = 0 | |
44 | 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 | 52 | class CLUTRaycastingWidget(wx.Panel): |
47 | 53 | """ |
48 | 54 | This class represents the frame where images is showed |
... | ... | @@ -65,6 +71,7 @@ class CLUTRaycastingWidget(wx.Panel): |
65 | 71 | self.to_draw_points = 0 |
66 | 72 | self.histogram_pixel_points = [[0,0]] |
67 | 73 | self.histogram_array = [100,100] |
74 | + self.previous_wl = 0 | |
68 | 75 | self.CalculatePixelPoints() |
69 | 76 | self.dragged = False |
70 | 77 | self.point_dragged = None |
... | ... | @@ -104,18 +111,27 @@ class CLUTRaycastingWidget(wx.Panel): |
104 | 111 | pass |
105 | 112 | |
106 | 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 | 116 | # A point has been selected. It can be dragged. |
109 | 117 | if point: |
110 | 118 | self.dragged = True |
111 | 119 | self.point_dragged = point |
112 | 120 | self.Refresh() |
113 | 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 | 131 | else: |
115 | - p = self._has_clicked_in_line(evt.GetPositionTuple()) | |
132 | + p = self._has_clicked_in_line((x, y)) | |
116 | 133 | # The user clicked in the line. Insert a new point. |
117 | 134 | if p: |
118 | - x, y = evt.GetPositionTuple() | |
119 | 135 | n, p = p |
120 | 136 | self.points[n].insert(p, {'x': 0, 'y': 0}) |
121 | 137 | self.colours[n].insert(p, {'red': 0, 'green': 0, 'blue': 0}) |
... | ... | @@ -131,7 +147,7 @@ class CLUTRaycastingWidget(wx.Panel): |
131 | 147 | self.curves[n].nodes.insert(p, node) |
132 | 148 | |
133 | 149 | self.Refresh() |
134 | - nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) | |
150 | + nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), n) | |
135 | 151 | self.GetEventHandler().ProcessEvent(nevt) |
136 | 152 | return |
137 | 153 | evt.Skip() |
... | ... | @@ -152,7 +168,7 @@ class CLUTRaycastingWidget(wx.Panel): |
152 | 168 | print self.curves[i].nodes |
153 | 169 | self.curves[i].nodes[j].colour = (r, g, b) |
154 | 170 | self.Refresh() |
155 | - nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) | |
171 | + nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), i) | |
156 | 172 | self.GetEventHandler().ProcessEvent(nevt) |
157 | 173 | return |
158 | 174 | evt.Skip() |
... | ... | @@ -167,7 +183,7 @@ class CLUTRaycastingWidget(wx.Panel): |
167 | 183 | print "RightClick", i, j |
168 | 184 | self.RemovePoint(i, j) |
169 | 185 | self.Refresh() |
170 | - nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) | |
186 | + nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), i) | |
171 | 187 | self.GetEventHandler().ProcessEvent(nevt) |
172 | 188 | return |
173 | 189 | evt.Skip() |
... | ... | @@ -178,10 +194,13 @@ class CLUTRaycastingWidget(wx.Panel): |
178 | 194 | been occurred in the preset points. |
179 | 195 | """ |
180 | 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 | 198 | self.GetEventHandler().ProcessEvent(evt) |
183 | 199 | self.dragged = False |
200 | + self.curve_dragged = None | |
201 | + self.point_dragged = None | |
184 | 202 | self.to_render = False |
203 | + self.previous_wl = 0 | |
185 | 204 | |
186 | 205 | def OnWheel(self, evt): |
187 | 206 | """ |
... | ... | @@ -197,11 +216,11 @@ class CLUTRaycastingWidget(wx.Panel): |
197 | 216 | |
198 | 217 | def OnMotion(self, evt): |
199 | 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 | 222 | self.to_render = True |
202 | 223 | i,j = self.point_dragged |
203 | - x = evt.GetX() | |
204 | - y = evt.GetY() | |
205 | 224 | |
206 | 225 | width, height= self.GetVirtualSizeTuple() |
207 | 226 | |
... | ... | @@ -234,9 +253,25 @@ class CLUTRaycastingWidget(wx.Panel): |
234 | 253 | self.curves[i].nodes[j].y = y |
235 | 254 | self.curves[i].nodes[j].graylevel = graylevel |
236 | 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 | 260 | self.Refresh() |
238 | - evt = CLUTEvent(myEVT_CLUT_POINT , self.GetId()) | |
261 | + evt = CLUTEvent(myEVT_CLUT_POINT , self.GetId(), i) | |
239 | 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 | 275 | else: |
241 | 276 | evt.Skip() |
242 | 277 | |
... | ... | @@ -257,10 +292,18 @@ class CLUTRaycastingWidget(wx.Panel): |
257 | 292 | """ |
258 | 293 | for i, curve in enumerate(self.curves): |
259 | 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 | 296 | return (i, j) |
262 | 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 | 307 | def _has_clicked_in_line(self, position): |
265 | 308 | """ |
266 | 309 | Verify if was clicked in a line. If yes, it returns the insertion |
... | ... | @@ -278,7 +321,7 @@ class CLUTRaycastingWidget(wx.Panel): |
278 | 321 | return None |
279 | 322 | |
280 | 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 | 326 | def RemovePoint(self, i, j): |
284 | 327 | """ |
... | ... | @@ -413,12 +456,13 @@ class CLUTRaycastingWidget(wx.Panel): |
413 | 456 | |
414 | 457 | def _draw_selection_curve(self, ctx, height): |
415 | 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 | 462 | ctx.set_source_rgb(0,0,0) |
421 | 463 | ctx.fill_preserve() |
464 | + ctx.set_source_rgb(*LINE_COLOUR) | |
465 | + ctx.stroke() | |
422 | 466 | |
423 | 467 | def Render(self, dc): |
424 | 468 | ctx = wx.lib.wxcairo.ContextFromDC(dc) |
... | ... | @@ -488,6 +532,9 @@ class CLUTRaycastingWidget(wx.Panel): |
488 | 532 | node.opacity = point['y'] |
489 | 533 | node.colour = colour['red'], colour['green'], colour['blue'] |
490 | 534 | curve.nodes.append(node) |
535 | + curve.CalculateWWWl() | |
536 | + curve.wl_px = (self.HounsfieldToPixel(curve.wl), | |
537 | + self.OpacityToPixel(0)) | |
491 | 538 | self.curves.append(curve) |
492 | 539 | self._build_histogram() |
493 | 540 | |
... | ... | @@ -551,8 +598,11 @@ class CLUTRaycastingWidget(wx.Panel): |
551 | 598 | self.histogram_array = h_array |
552 | 599 | |
553 | 600 | class CLUTEvent(wx.PyCommandEvent): |
554 | - def __init__(self , evtType, id): | |
601 | + def __init__(self , evtType, id, curve): | |
555 | 602 | wx.PyCommandEvent.__init__(self, evtType, id) |
603 | + self.curve = curve | |
604 | + def GetCurve(self): | |
605 | + return self.curve | |
556 | 606 | |
557 | 607 | |
558 | 608 | # Occurs when CLUT is sliding |
... | ... | @@ -570,3 +620,7 @@ EVT_CLUT_POINT = wx.PyEventBinder(myEVT_CLUT_POINT, 1) |
570 | 620 | # Occurs when a CLUT point was changed |
571 | 621 | myEVT_CLUT_POINT_CHANGED = wx.NewEventType() |
572 | 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) | ... | ... |