Commit a2350755deb300f540a1088388803108d1b714f1
1 parent
dd2cd7d0
Exists in
master
and in
5 other branches
Improved cursor_actors, better handlings and more accurate
Showing
5 changed files
with
336 additions
and
198 deletions
Show diff stats
invesalius/data/cursor_actors.py
... | ... | @@ -17,127 +17,174 @@ |
17 | 17 | # detalhes. |
18 | 18 | #-------------------------------------------------------------------------- |
19 | 19 | |
20 | -from math import * | |
20 | +import math | |
21 | 21 | |
22 | 22 | import numpy |
23 | 23 | import vtk |
24 | -import wx.lib.pubsub as ps | |
24 | +import imagedata_utils | |
25 | 25 | from project import Project |
26 | 26 | import constants as const |
27 | -import utils | |
28 | 27 | |
29 | -class CursorCircle: | |
30 | - # TODO: Think and try to change this class to an actor | |
31 | - # CursorCircleActor(vtk.vtkActor) | |
28 | +from vtk.util import numpy_support | |
29 | + | |
30 | +ORIENTATION = {'AXIAL': 2, | |
31 | + 'CORONAL': 1, | |
32 | + 'SAGITAL': 0} | |
33 | + | |
34 | +def to_vtk(n_array, spacing, slice_number, orientation): | |
35 | + """ | |
36 | + It transforms a numpy array into a vtkImageData. | |
37 | + """ | |
38 | + # TODO Merge this function with imagedata_utils.to_vtk to eliminate | |
39 | + # duplicated code | |
40 | + try: | |
41 | + dz, dy, dx = n_array.shape | |
42 | + except ValueError: | |
43 | + dy, dx = n_array.shape | |
44 | + dz = 1 | |
45 | + | |
46 | + v_image = numpy_support.numpy_to_vtk(n_array.flat) | |
47 | + | |
48 | + if orientation == 'AXIAL': | |
49 | + extent = (0, dx -1, 0, dy -1, slice_number, slice_number + dz - 1) | |
50 | + elif orientation == 'SAGITAL': | |
51 | + extent = (slice_number, slice_number + dx - 1, 0, dy - 1, 0, dz - 1) | |
52 | + elif orientation == 'CORONAL': | |
53 | + extent = (0, dx - 1, slice_number, slice_number + dy - 1, 0, dz - 1) | |
54 | + | |
55 | + image = vtk.vtkImageData() | |
56 | + image.SetOrigin(0, 0, 0) | |
57 | + image.SetSpacing(spacing) | |
58 | + image.SetNumberOfScalarComponents(1) | |
59 | + image.SetDimensions(dx, dy, dz) | |
60 | + image.SetExtent(extent) | |
61 | + image.SetScalarType(numpy_support.get_vtk_array_type(n_array.dtype)) | |
62 | + image.AllocateScalars() | |
63 | + image.Update() | |
64 | + image.GetCellData().SetScalars(v_image) | |
65 | + image.GetPointData().SetScalars(v_image) | |
66 | + image.Update() | |
67 | + | |
68 | + image_copy = vtk.vtkImageData() | |
69 | + image_copy.DeepCopy(image) | |
70 | + image_copy.Update() | |
71 | + | |
72 | + return image_copy | |
73 | + | |
74 | +class CursorBase(object): | |
32 | 75 | def __init__(self): |
33 | 76 | self.colour = (0.0, 0.0, 1.0) |
34 | 77 | self.opacity = 1 |
35 | - self.radius = 15.0 | |
36 | - #self.position = (0.5,0.5, 1) | |
37 | - self.points = [] | |
78 | + self.size = 15.0 | |
38 | 79 | self.orientation = "AXIAL" |
39 | 80 | self.spacing = (1, 1, 1) |
40 | - | |
41 | - self.mapper = vtk.vtkPolyDataMapper() | |
42 | - self.actor = vtk.vtkActor() | |
43 | - self.property = vtk.vtkProperty() | |
44 | - | |
45 | - self.__build_actor() | |
46 | - self.__calculate_area_pixels() | |
47 | - | |
48 | - def __build_actor(self): | |
49 | - """ | |
50 | - Function to plot the circle | |
51 | - """ | |
52 | - print "Building circle cursor", self.orientation | |
53 | - r = self.radius | |
54 | - t = 0 | |
55 | - | |
56 | - self.posc_a = 0 | |
57 | - self.posc_b = 0 | |
58 | - | |
59 | - self.segment = vtk.vtkAppendPolyData() | |
60 | - | |
61 | - self.xa = self.posc_a + r * cos(t) | |
62 | - self.ya = self.posc_a + r * sin(t) | |
63 | - | |
64 | - while(t <= 2 * pi): | |
65 | - self.GenerateCicleSegment(t) | |
66 | - t = t + 0.05 | |
67 | - | |
68 | - self.GenerateCicleSegment(0) | |
69 | - | |
70 | - self.mapper.SetInputConnection(self.segment.GetOutputPort()) | |
71 | - self.actor.SetMapper(self.mapper) | |
72 | - self.actor.GetProperty().SetOpacity(self.opacity) | |
73 | - self.actor.GetProperty().SetColor(self.colour) | |
74 | - self.actor.PickableOff() | |
75 | - | |
76 | - def GenerateCicleSegment(self, t): | |
77 | - """ | |
78 | - Generate cicle segment | |
79 | - """ | |
80 | - x = self.posc_a + self.radius * cos(t) | |
81 | - y = self.posc_b + self.radius * sin(t) | |
82 | - | |
83 | - ls = vtk.vtkLineSource() | |
84 | - ls.SetPoint1(self.xa, self.ya, 0) | |
85 | - ls.SetPoint2(x, y, 0) | |
86 | - | |
87 | - self.segment.AddInput(ls.GetOutput()) | |
88 | - self.xa, self.ya = x, y | |
89 | - | |
90 | - def __calculate_area_pixels(self): | |
91 | - """ | |
92 | - Return the cursor's pixels. | |
93 | - """ | |
94 | - radius = self.radius | |
95 | - if self.orientation == 'AXIAL': | |
96 | - sx = self.spacing[0] | |
97 | - sy = self.spacing[1] | |
98 | - elif self.orientation == 'CORONAL': | |
99 | - sx = self.spacing[0] | |
100 | - sy = self.spacing[2] | |
101 | - elif self.orientation == 'SAGITAL': | |
102 | - sx = self.spacing[1] | |
103 | - sy = self.spacing[2] | |
104 | - | |
105 | - y,x = numpy.ogrid[-radius/sy:+radius/sy, | |
106 | - -radius/sx:+radius/sx] | |
107 | - | |
108 | - index = (y*sy)**2 + (x*sx)**2 <= radius**2 | |
109 | - self.points = index | |
81 | + if vtk.vtkVersion().GetVTKVersion() > '5.8.0': | |
82 | + self.mapper = vtk.vtkImageSliceMapper() | |
83 | + cursor_property = vtk.vtkImageProperty() | |
84 | + cursor_property.SetInterpolationTypeToNearest() | |
85 | + self.actor = vtk.vtkImageSlice() | |
86 | + self.actor.SetMapper(self.mapper) | |
87 | + self.actor.SetProperty(cursor_property) | |
88 | + else: | |
89 | + self.actor = vtk.vtkImageActor() | |
90 | + self.mapper = None | |
91 | + self._build_actor() | |
92 | + self._calculate_area_pixels() | |
110 | 93 | |
111 | 94 | def SetSize(self, diameter): |
112 | - radius = self.radius = diameter/2.0 | |
113 | - #self.disk.SetInnerRadius(radius-1) # filled = self.radius | |
114 | - #self.disk.SetOuterRadius(radius) # filled = 0 | |
115 | - self.__build_actor() | |
116 | - self.__calculate_area_pixels() | |
95 | + self.radius = diameter/2.0 | |
96 | + self._build_actor() | |
97 | + self._calculate_area_pixels() | |
117 | 98 | |
118 | 99 | def SetColour(self, colour): |
119 | 100 | self.colour = colour |
120 | - self.actor.GetProperty().SetColor(colour) | |
101 | + self._build_actor() | |
121 | 102 | |
122 | 103 | def SetOrientation(self, orientation): |
123 | 104 | self.orientation = orientation |
124 | - proj = Project() | |
125 | - orig_orien = proj.original_orientation | |
126 | - if orientation == "CORONAL": | |
127 | - self.actor.RotateX(90) | |
128 | - if orientation == "SAGITAL": | |
129 | - self.actor.RotateY(90) | |
105 | + self._build_actor() | |
106 | + self._calculate_area_pixels() | |
130 | 107 | |
131 | 108 | def SetPosition(self, position): |
132 | - self.position = position | |
133 | - self.actor.SetPosition(position) | |
109 | + # Overriding SetPosition method because in rectangles with odd | |
110 | + # dimensions there is no half position. | |
111 | + px, py, pz = position | |
112 | + sx, sy, sz = self.spacing | |
113 | + tx = self.actor.GetXRange()[1] - self.actor.GetXRange()[0] | |
114 | + ty = self.actor.GetYRange()[1] - self.actor.GetYRange()[0] | |
115 | + tz = self.actor.GetZRange()[1] - self.actor.GetZRange()[0] | |
116 | + | |
117 | + if self.orientation == 'AXIAL': | |
118 | + if self.points.shape[0] % 2: | |
119 | + y = py - ty / 2.0 | |
120 | + else: | |
121 | + y = py - ty / 2.0 + self.spacing[1] / 2.0 | |
122 | + | |
123 | + if self.points.shape[1] % 2: | |
124 | + x = px - tx / 2.0 | |
125 | + else: | |
126 | + x = px - tx / 2.0 + self.spacing[0] / 2.0 | |
127 | + z = pz | |
128 | + | |
129 | + if self.mapper: | |
130 | + x += sx / 2.0 | |
131 | + y += sy / 2.0 | |
132 | + | |
133 | + elif self.orientation == 'CORONAL': | |
134 | + if self.points.shape[0] % 2: | |
135 | + z = pz - tz / 2.0 | |
136 | + else: | |
137 | + z = pz - tz / 2.0 + self.spacing[2] / 2.0 | |
138 | + | |
139 | + if self.points.shape[1] % 2: | |
140 | + x = px - tx / 2.0 | |
141 | + else: | |
142 | + x = px - tx / 2.0 + self.spacing[0] / 2.0 | |
143 | + y = py | |
144 | + | |
145 | + if self.mapper: | |
146 | + x += sx / 2.0 | |
147 | + z += sz / 2.0 | |
134 | 148 | |
135 | - def SetEditionPosition(self, position): | |
136 | - self.edition_position = position | |
149 | + elif self.orientation == 'SAGITAL': | |
150 | + # height shape is odd | |
151 | + if self.points.shape[1] % 2: | |
152 | + y = py - ty / 2.0 | |
153 | + else: | |
154 | + y = py - ty / 2.0 + self.spacing[1] / 2.0 | |
155 | + | |
156 | + if self.points.shape[0] % 2: | |
157 | + z = pz - tz / 2.0 | |
158 | + else: | |
159 | + z = pz - tz / 2.0 + self.spacing[2] / 2.0 | |
160 | + x = px | |
161 | + | |
162 | + if self.mapper: | |
163 | + y += sy / 2.0 | |
164 | + z += sz / 2.0 | |
165 | + | |
166 | + else: | |
167 | + if self.points.shape[0] % 2: | |
168 | + y = py - ty / 2.0 | |
169 | + else: | |
170 | + y = py - ty / 2.0 + self.spacing[1] / 2.0 | |
171 | + | |
172 | + if self.points.shape[1] % 2: | |
173 | + x = px - tx / 2.0 | |
174 | + else: | |
175 | + x = px - tx / 2.0 + self.spacing[0] / 2.0 | |
176 | + z = pz | |
177 | + | |
178 | + if self.mapper: | |
179 | + x += sx / 2.0 | |
180 | + y += sy / 2.0 | |
181 | + | |
182 | + self.actor.SetPosition(x, y, z) | |
137 | 183 | |
138 | 184 | def SetSpacing(self, spacing): |
139 | 185 | self.spacing = spacing |
140 | - self.__calculate_area_pixels() | |
186 | + self._build_actor() | |
187 | + self._calculate_area_pixels() | |
141 | 188 | |
142 | 189 | def Show(self, value=1): |
143 | 190 | if value: |
... | ... | @@ -148,90 +195,104 @@ class CursorCircle: |
148 | 195 | def GetPixels(self): |
149 | 196 | return self.points |
150 | 197 | |
198 | + def _build_actor(self): | |
199 | + pass | |
200 | + | |
201 | + def _calculate_area_pixels(self): | |
202 | + pass | |
151 | 203 | |
152 | -class CursorRectangle: | |
204 | + def _set_colour(self, imagedata, colour): | |
205 | + scalar_range = int(imagedata.GetScalarRange()[1]) | |
206 | + r, g, b = colour | |
207 | + | |
208 | + # map scalar values into colors | |
209 | + lut_mask = vtk.vtkLookupTable() | |
210 | + lut_mask.SetNumberOfColors(256) | |
211 | + lut_mask.SetHueRange(const.THRESHOLD_HUE_RANGE) | |
212 | + lut_mask.SetSaturationRange(1, 1) | |
213 | + lut_mask.SetValueRange(0, 255) | |
214 | + lut_mask.SetRange(0, 255) | |
215 | + lut_mask.SetNumberOfTableValues(256) | |
216 | + lut_mask.SetTableValue(0, 0, 0, 0, 0.0) | |
217 | + lut_mask.SetTableValue(1.0, 1-r, 1-g, 1-b, 0.50) | |
218 | + lut_mask.SetRampToLinear() | |
219 | + lut_mask.Build() | |
220 | + | |
221 | + # map the input image through a lookup table | |
222 | + img_colours_mask = vtk.vtkImageMapToColors() | |
223 | + img_colours_mask.SetLookupTable(lut_mask) | |
224 | + img_colours_mask.SetOutputFormatToRGBA() | |
225 | + img_colours_mask.SetInput(imagedata) | |
226 | + img_colours_mask.Update() | |
227 | + | |
228 | + return img_colours_mask.GetOutput() | |
229 | + | |
230 | + | |
231 | +class CursorCircle(CursorBase): | |
232 | + # TODO: Think and try to change this class to an actor | |
233 | + # CursorCircleActor(vtk.vtkActor) | |
153 | 234 | def __init__(self): |
154 | - | |
155 | - self.colour = (0.0, 0.0, 1.0) | |
156 | - self.opacity = 1 | |
157 | - | |
158 | - self.x_length = 30 | |
159 | - self.y_length = 30 | |
160 | - self.radius = 15 | |
161 | - | |
162 | - self.dimension = (self.x_length, self.y_length) | |
163 | - self.position = (0 ,0) | |
164 | - self.orientation = "AXIAL" | |
165 | - self.spacing = (1, 1, 1) | |
166 | - | |
167 | - self.__build_actor() | |
168 | - self.__calculate_area_pixels() | |
169 | - | |
170 | - def SetSize(self, size): | |
171 | - self.x_length = size | |
172 | - self.y_length = size | |
173 | - self.radius = size / 2 | |
174 | - retangle = self.retangle | |
175 | - retangle.SetXLength(size) | |
176 | - retangle.SetYLength(size) | |
177 | - self.__calculate_area_pixels() | |
178 | - | |
179 | - def SetOrientation(self, orientation): | |
180 | - self.orientation = orientation | |
181 | - | |
182 | - def SetColour(self, colour): | |
183 | - self.colour = colour | |
184 | - self.actor.GetProperty().SetColor(colour) | |
235 | + self.radius = 15.0 | |
236 | + super(CursorCircle, self).__init__() | |
185 | 237 | |
186 | - def SetOrientation(self, orientation): | |
187 | - self.orientation = orientation | |
188 | - proj = Project() | |
189 | - orig_orien = proj.original_orientation | |
190 | - if orientation == "CORONAL": | |
191 | - self.actor.RotateX(90) | |
192 | - if orientation == "SAGITAL": | |
193 | - self.actor.RotateY(90) | |
238 | + def _build_actor(self): | |
239 | + """ | |
240 | + Function to plot the circle | |
241 | + """ | |
242 | + print "Building circle cursor", self.orientation | |
243 | + r = self.radius | |
244 | + sx, sy, sz = self.spacing | |
245 | + if self.orientation == 'AXIAL': | |
246 | + xi = math.floor(-r/sx) | |
247 | + xf = math.ceil(r/sx) + 1 | |
248 | + yi = math.floor(-r/sy) | |
249 | + yf = math.ceil(r/sy) + 1 | |
250 | + zi = 0 | |
251 | + zf = 1 | |
252 | + elif self.orientation == 'CORONAL': | |
253 | + xi = math.floor(-r/sx) | |
254 | + xf = math.ceil(r/sx) + 1 | |
255 | + yi = 0 | |
256 | + yf = 1 | |
257 | + zi = math.floor(-r/sz) | |
258 | + zf = math.ceil(r/sz) + 1 | |
259 | + elif self.orientation == 'SAGITAL': | |
260 | + xi = 0 | |
261 | + xf = 1 | |
262 | + yi = math.floor(-r/sy) | |
263 | + yf = math.ceil(r/sy) + 1 | |
264 | + zi = math.floor(-r/sz) | |
265 | + zf = math.ceil(r/sz) + 1 | |
194 | 266 | |
195 | - def SetPosition(self, position): | |
196 | - x,y,z = position | |
197 | - self.position = position | |
198 | - self.actor.SetPosition(x,y,z) | |
199 | - | |
200 | - def SetEditionPosition(self, position): | |
201 | - self.edition_position = position | |
267 | + z,y,x = numpy.ogrid[zi:zf,yi:yf, xi:xf] | |
202 | 268 | |
203 | - def SetSpacing(self, spacing): | |
204 | - self.spacing = spacing | |
269 | + circle_m = (z*sz)**2 + (y*sy)**2 + (x*sx)**2 <= r**2 | |
270 | + circle_i = to_vtk(circle_m.astype('uint8'), | |
271 | + self.spacing, 0, self.orientation) | |
272 | + circle_ci = self._set_colour(circle_i, self.colour) | |
205 | 273 | |
206 | - def Show(self, value=1): | |
207 | - if value: | |
208 | - self.actor.VisibilityOn() | |
274 | + if self.mapper is None: | |
275 | + self.actor.SetInput(circle_ci) | |
276 | + self.actor.InterpolateOff() | |
277 | + self.actor.PickableOff() | |
278 | + self.actor.SetDisplayExtent(circle_ci.GetExtent()) | |
209 | 279 | else: |
210 | - self.actor.VisibilityOff() | |
280 | + self.mapper.SetInput(circle_ci) | |
281 | + self.mapper.BorderOn() | |
282 | + | |
283 | + self.mapper.SetOrientation(ORIENTATION[self.orientation]) | |
211 | 284 | |
212 | - def __build_actor(self): | |
285 | + print '====================================' | |
286 | + print self.orientation | |
287 | + print circle_ci.GetSpacing() | |
288 | + print xi, xf, yi, yf, zi, zf | |
289 | + print '====================================' | |
290 | + | |
291 | + def _calculate_area_pixels(self): | |
213 | 292 | """ |
214 | - Function to plot the Retangle | |
293 | + Return the cursor's pixels. | |
215 | 294 | """ |
216 | - print "Building rectangle cursor", self.orientation | |
217 | - mapper = vtk.vtkPolyDataMapper() | |
218 | - self.retangle = vtk.vtkCubeSource() | |
219 | - self.actor = actor = vtk.vtkActor() | |
220 | - | |
221 | - prop = vtk.vtkProperty() | |
222 | - prop.SetRepresentationToWireframe() | |
223 | - self.actor.SetProperty(prop) | |
224 | - | |
225 | - mapper.SetInput(self.retangle.GetOutput()) | |
226 | - actor.SetPosition(self.position[0] - self.x_length,\ | |
227 | - self.position[1] - self.y_length, 1) | |
228 | - | |
229 | - actor.SetMapper(mapper) | |
230 | - actor.GetProperty().SetOpacity(self.opacity) | |
231 | - actor.GetProperty().SetColor(self.colour) | |
232 | - actor.SetVisibility(0) | |
233 | - | |
234 | - def __calculate_area_pixels(self): | |
295 | + r = self.radius | |
235 | 296 | if self.orientation == 'AXIAL': |
236 | 297 | sx = self.spacing[0] |
237 | 298 | sy = self.spacing[1] |
... | ... | @@ -241,12 +302,71 @@ class CursorRectangle: |
241 | 302 | elif self.orientation == 'SAGITAL': |
242 | 303 | sx = self.spacing[1] |
243 | 304 | sy = self.spacing[2] |
244 | - shape = (self.y_length/sy, self.x_length/sx) | |
245 | - self.points = numpy.empty(shape, dtype='bool') | |
246 | - self.points.fill(True) | |
247 | 305 | |
248 | - def GetPixels(self): | |
306 | + xi = math.floor(-r/sx) | |
307 | + xf = math.ceil(r/sx) + 1 | |
308 | + yi = math.floor(-r/sy) | |
309 | + yf = math.ceil(r/sy) + 1 | |
310 | + | |
311 | + y,x = numpy.ogrid[yi:yf, xi:xf] | |
312 | + | |
313 | + print "AREA", x | |
314 | + | |
315 | + index = (y*sy)**2 + (x*sx)**2 <= r**2 | |
316 | + self.points = index | |
317 | + | |
318 | + | |
319 | +class CursorRectangle(CursorBase): | |
320 | + def __init__(self): | |
321 | + self.radius = 15.0 | |
322 | + super(CursorRectangle, self).__init__() | |
323 | + | |
324 | + | |
325 | + def _build_actor(self): | |
249 | 326 | """ |
250 | - Return the points of the rectangle | |
327 | + Function to plot the Retangle | |
251 | 328 | """ |
252 | - return self.points | |
329 | + print "Building rectangle cursor", self.orientation | |
330 | + r = self.radius | |
331 | + sx, sy, sz = self.spacing | |
332 | + if self.orientation == 'AXIAL': | |
333 | + x = math.floor(2*r/sx) | |
334 | + y = math.floor(2*r/sy) | |
335 | + z = 1 | |
336 | + elif self.orientation == 'CORONAL': | |
337 | + x = math.floor(r/sx) | |
338 | + y = 1 | |
339 | + z = math.floor(r/sz) | |
340 | + elif self.orientation == 'SAGITAL': | |
341 | + x = 1 | |
342 | + y = math.floor(r/sy) | |
343 | + z = math.floor(r/sz) | |
344 | + | |
345 | + rectangle_m = numpy.ones((z, y, x), dtype='uint8') | |
346 | + rectangle_i = to_vtk(rectangle_m, self.spacing, 0, self.orientation) | |
347 | + rectangle_ci = self._set_colour(rectangle_i, self.colour) | |
348 | + | |
349 | + if self.mapper is None: | |
350 | + self.actor.SetInput(rectangle_ci) | |
351 | + self.actor.InterpolateOff() | |
352 | + self.actor.PickableOff() | |
353 | + self.actor.SetDisplayExtent(rectangle_ci.GetExtent()) | |
354 | + else: | |
355 | + self.mapper.SetInput(rectangle_ci) | |
356 | + self.mapper.BorderOn() | |
357 | + self.mapper.SetOrientation(ORIENTATION[self.orientation]) | |
358 | + | |
359 | + def _calculate_area_pixels(self): | |
360 | + r = self.radius | |
361 | + sx, sy, sz = self.spacing | |
362 | + if self.orientation == 'AXIAL': | |
363 | + x = math.floor(2*r/sx) | |
364 | + y = math.floor(2*r/sy) | |
365 | + elif self.orientation == 'CORONAL': | |
366 | + x = math.floor(r/sx) | |
367 | + y = math.floor(r/sz) | |
368 | + elif self.orientation == 'SAGITAL': | |
369 | + x = math.floor(r/sy) | |
370 | + y = math.floor(r/sz) | |
371 | + | |
372 | + self.points = numpy.ones((y, x), dtype='bool') | ... | ... |
invesalius/data/imagedata_utils.py
invesalius/data/slice_.py
... | ... | @@ -294,8 +294,8 @@ class Slice(object): |
294 | 294 | sx = self.spacing[0] |
295 | 295 | sy = self.spacing[2] |
296 | 296 | elif orientation == 'SAGITAL': |
297 | - sx = self.spacing[1] | |
298 | - sy = self.spacing[2] | |
297 | + sx = self.spacing[2] | |
298 | + sy = self.spacing[1] | |
299 | 299 | |
300 | 300 | else: |
301 | 301 | if orientation == 'AXIAL': |
... | ... | @@ -309,15 +309,17 @@ class Slice(object): |
309 | 309 | py = position / mask.shape[1] |
310 | 310 | px = position % mask.shape[1] |
311 | 311 | elif orientation == 'SAGITAL': |
312 | - sx = self.spacing[1] | |
313 | - sy = self.spacing[2] | |
312 | + sx = self.spacing[2] | |
313 | + sy = self.spacing[1] | |
314 | 314 | py = position / mask.shape[1] |
315 | 315 | px = position % mask.shape[1] |
316 | 316 | |
317 | - xi = px - math.ceil(radius/sx) | |
318 | - xf = px + math.ceil(radius/sx) | |
319 | - yi = py - math.ceil(radius/sy) | |
320 | - yf = py + math.ceil(radius/sy) | |
317 | + cx = index.shape[1] / 2 + 1 | |
318 | + cy = index.shape[0] / 2 + 1 | |
319 | + xi = px - index.shape[1] + cx | |
320 | + xf = xi + index.shape[1] | |
321 | + yi = py - index.shape[0] + cy | |
322 | + yf = yi + index.shape[0] | |
321 | 323 | |
322 | 324 | if yi < 0: |
323 | 325 | index = index[abs(yi):,:] |
... | ... | @@ -341,6 +343,12 @@ class Slice(object): |
341 | 343 | roi_m = mask[yi:yf,xi:xf] |
342 | 344 | roi_i = image[yi:yf, xi:xf] |
343 | 345 | |
346 | ||
347 | + print"IMAGE", roi_m.shape | |
348 | + print "BRUSH", index.shape | |
349 | + print "IMAGE[BRUSH]", roi_m[index].shape | |
350 | ||
351 | + | |
344 | 352 | if operation == const.BRUSH_THRESH: |
345 | 353 | # It's a trick to make points between threshold gets value 254 |
346 | 354 | # (1 * 253 + 1) and out ones gets value 1 (0 * 253 + 1). | ... | ... |
invesalius/data/slice_data.py
... | ... | @@ -38,6 +38,7 @@ class SliceData(object): |
38 | 38 | self.number = 0 |
39 | 39 | self.orientation = 'AXIAL' |
40 | 40 | self.renderer = None |
41 | + self.overlay_renderer = None | |
41 | 42 | self.__create_text() |
42 | 43 | self.__create_box() |
43 | 44 | |
... | ... | @@ -131,8 +132,8 @@ class SliceData(object): |
131 | 132 | |
132 | 133 | def SetCursor(self, cursor): |
133 | 134 | if self.cursor: |
134 | - self.renderer.RemoveActor(self.cursor.actor) | |
135 | - self.renderer.AddActor(cursor.actor) | |
135 | + self.overlay_renderer.RemoveActor(self.cursor.actor) | |
136 | + self.overlay_renderer.AddActor(cursor.actor) | |
136 | 137 | self.cursor = cursor |
137 | 138 | |
138 | 139 | def SetNumber(self, number): |
... | ... | @@ -166,7 +167,7 @@ class SliceData(object): |
166 | 167 | self.line_r.SetPoint2((xf, yf, 0)) |
167 | 168 | |
168 | 169 | def Hide(self): |
169 | - self.renderer.RemoveActor(self.actor) | |
170 | + self.overlay_renderer.RemoveActor(self.actor) | |
170 | 171 | self.renderer.RemoveActor(self.text.actor) |
171 | 172 | |
172 | 173 | def Show(self): | ... | ... |
invesalius/data/viewer_slice.py
... | ... | @@ -725,13 +725,13 @@ class Viewer(wx.Panel): |
725 | 725 | self.pick.Pick(mouse_x, mouse_y, 0, render) |
726 | 726 | |
727 | 727 | coord = self.get_coordinate_cursor() |
728 | - slice_data.cursor.SetPosition(coord) | |
729 | - slice_data.cursor.SetEditionPosition( | |
730 | - self.get_coordinate_cursor_edition(slice_data)) | |
731 | - self.__update_cursor_position(slice_data, coord) | |
728 | + position = self.slice_data.actor.GetInput().FindPoint(coord) | |
729 | + | |
730 | + if position != -1: | |
731 | + coord = self.slice_data.actor.GetInput().GetPoint(position) | |
732 | 732 | |
733 | + slice_data.cursor.SetPosition(coord) | |
733 | 734 | cursor = self.slice_data.cursor |
734 | - position = self.slice_data.actor.GetInput().FindPoint(coord) | |
735 | 735 | radius = cursor.radius |
736 | 736 | |
737 | 737 | if position < 0: |
... | ... | @@ -771,9 +771,7 @@ class Viewer(wx.Panel): |
771 | 771 | if position != -1: |
772 | 772 | coord = self.slice_data.actor.GetInput().GetPoint(position) |
773 | 773 | slice_data.cursor.SetPosition(coord) |
774 | - slice_data.cursor.SetEditionPosition( | |
775 | - self.get_coordinate_cursor_edition(slice_data)) | |
776 | - self.__update_cursor_position(slice_data, coord) | |
774 | + #self.__update_cursor_position(slice_data, coord) | |
777 | 775 | |
778 | 776 | if (self.left_pressed): |
779 | 777 | cursor = self.slice_data.cursor |
... | ... | @@ -1280,12 +1278,25 @@ class Viewer(wx.Panel): |
1280 | 1278 | |
1281 | 1279 | def create_slice_window(self): |
1282 | 1280 | renderer = vtk.vtkRenderer() |
1281 | + renderer.SetLayer(0) | |
1282 | + cam = renderer.GetActiveCamera() | |
1283 | + | |
1284 | + overlay_renderer = vtk.vtkRenderer() | |
1285 | + overlay_renderer.SetLayer(1) | |
1286 | + overlay_renderer.SetActiveCamera(cam) | |
1287 | + overlay_renderer.SetInteractive(0) | |
1288 | + | |
1289 | + | |
1290 | + self.interactor.GetRenderWindow().SetNumberOfLayers(2) | |
1291 | + self.interactor.GetRenderWindow().AddRenderer(overlay_renderer) | |
1283 | 1292 | self.interactor.GetRenderWindow().AddRenderer(renderer) |
1293 | + | |
1284 | 1294 | actor = vtk.vtkImageActor() |
1285 | 1295 | actor.InterpolateOff() |
1286 | 1296 | slice_data = sd.SliceData() |
1287 | 1297 | slice_data.SetOrientation(self.orientation) |
1288 | 1298 | slice_data.renderer = renderer |
1299 | + slice_data.overlay_renderer = overlay_renderer | |
1289 | 1300 | slice_data.actor = actor |
1290 | 1301 | slice_data.SetBorderStyle(sd.BORDER_ALL) |
1291 | 1302 | renderer.AddActor(actor) |
... | ... | @@ -1301,7 +1312,7 @@ class Viewer(wx.Panel): |
1301 | 1312 | self.cam.SetFocalPoint(0, 0, 0) |
1302 | 1313 | self.cam.SetViewUp(const.SLICE_POSITION[orig_orien][0][self.orientation]) |
1303 | 1314 | self.cam.SetPosition(const.SLICE_POSITION[orig_orien][1][self.orientation]) |
1304 | - self.cam.ComputeViewPlaneNormal() | |
1315 | + #self.cam.ComputeViewPlaneNormal() | |
1305 | 1316 | #self.cam.OrthogonalizeViewUp() |
1306 | 1317 | self.cam.ParallelProjectionOn() |
1307 | 1318 | ... | ... |