Commit c24db7d3ff62e2dc843811a4239fc6cf05fc0bb9
Exists in
master
and in
55 other branches
Merge pull request #19 from tfmoraes/MIPs
MIPs
Showing
8 changed files
with
873 additions
and
23 deletions
Show diff stats
invesalius/constants.py
... | ... | @@ -292,7 +292,8 @@ WINDOW_LEVEL = {_("Abdomen"):(350,50), |
292 | 292 | _("Pelvis"): (450,50), |
293 | 293 | _("Sinus"):(4000, 400), |
294 | 294 | _("Vasculature - Hard"):(240,80), |
295 | - _("Vasculature - Soft"):(650,160)} | |
295 | + _("Vasculature - Soft"):(650,160), | |
296 | + _("Contour"): (255, 127)} | |
296 | 297 | |
297 | 298 | REDUCE_IMAGEDATA_QUALITY = 0 |
298 | 299 | |
... | ... | @@ -542,3 +543,18 @@ DICOM_ENCODING_TO_PYTHON = { |
542 | 543 | 'ISO_IR 138': 'iso_ir_138', |
543 | 544 | 'ISO_IR 144': 'iso_ir_144', |
544 | 545 | } |
546 | + | |
547 | +#-------------------- Projections type ---------------- | |
548 | +PROJECTION_NORMAL=0 | |
549 | +PROJECTION_MaxIP=1 | |
550 | +PROJECTION_MinIP=2 | |
551 | +PROJECTION_MeanIP=3 | |
552 | +PROJECTION_LMIP=4 | |
553 | +PROJECTION_MIDA=5 | |
554 | +PROJECTION_CONTOUR_MIP=6 | |
555 | +PROJECTION_CONTOUR_LMIP=7 | |
556 | +PROJECTION_CONTOUR_MIDA=8 | |
557 | + | |
558 | +#------------ Projections defaults ------------------ | |
559 | +PROJECTION_BORDER_SIZE=1.0 | |
560 | +PROJECTION_MIP_SIZE=2 | ... | ... |
... | ... | @@ -0,0 +1,379 @@ |
1 | +#http://en.wikipedia.org/wiki/Local_maximum_intensity_projection | |
2 | +import numpy as np | |
3 | +cimport numpy as np | |
4 | +cimport cython | |
5 | + | |
6 | +from libc.math cimport floor, ceil, sqrt, fabs | |
7 | +from cython.parallel import prange | |
8 | + | |
9 | +DTYPE = np.uint8 | |
10 | +ctypedef np.uint8_t DTYPE_t | |
11 | + | |
12 | +DTYPE16 = np.int16 | |
13 | +ctypedef np.int16_t DTYPE16_t | |
14 | + | |
15 | +DTYPEF32 = np.float32 | |
16 | +ctypedef np.float32_t DTYPEF32_t | |
17 | + | |
18 | +@cython.boundscheck(False) # turn of bounds-checking for entire function | |
19 | +def lmip(np.ndarray[DTYPE16_t, ndim=3] image, int axis, DTYPE16_t tmin, | |
20 | + DTYPE16_t tmax, np.ndarray[DTYPE16_t, ndim=2] out): | |
21 | + cdef DTYPE16_t max | |
22 | + cdef int start | |
23 | + cdef int sz = image.shape[0] | |
24 | + cdef int sy = image.shape[1] | |
25 | + cdef int sx = image.shape[2] | |
26 | + | |
27 | + # AXIAL | |
28 | + if axis == 0: | |
29 | + for x in xrange(sx): | |
30 | + for y in xrange(sy): | |
31 | + max = image[0, y, x] | |
32 | + if max >= tmin and max <= tmax: | |
33 | + start = 1 | |
34 | + else: | |
35 | + start = 0 | |
36 | + for z in xrange(sz): | |
37 | + if image[z, y, x] > max: | |
38 | + max = image[z, y, x] | |
39 | + | |
40 | + elif image[z, y, x] < max and start: | |
41 | + break | |
42 | + | |
43 | + if image[z, y, x] >= tmin and image[z, y, x] <= tmax: | |
44 | + start = 1 | |
45 | + | |
46 | + out[y, x] = max | |
47 | + | |
48 | + #CORONAL | |
49 | + elif axis == 1: | |
50 | + for z in xrange(sz): | |
51 | + for x in xrange(sx): | |
52 | + max = image[z, 0, x] | |
53 | + if max >= tmin and max <= tmax: | |
54 | + start = 1 | |
55 | + else: | |
56 | + start = 0 | |
57 | + for y in xrange(sy): | |
58 | + if image[z, y, x] > max: | |
59 | + max = image[z, y, x] | |
60 | + | |
61 | + elif image[z, y, x] < max and start: | |
62 | + break | |
63 | + | |
64 | + if image[z, y, x] >= tmin and image[z, y, x] <= tmax: | |
65 | + start = 1 | |
66 | + | |
67 | + out[z, x] = max | |
68 | + | |
69 | + #CORONAL | |
70 | + elif axis == 2: | |
71 | + for z in xrange(sz): | |
72 | + for y in xrange(sy): | |
73 | + max = image[z, y, 0] | |
74 | + if max >= tmin and max <= tmax: | |
75 | + start = 1 | |
76 | + else: | |
77 | + start = 0 | |
78 | + for x in xrange(sx): | |
79 | + if image[z, y, x] > max: | |
80 | + max = image[z, y, x] | |
81 | + | |
82 | + elif image[z, y, x] < max and start: | |
83 | + break | |
84 | + | |
85 | + if image[z, y, x] >= tmin and image[z, y, x] <= tmax: | |
86 | + start = 1 | |
87 | + | |
88 | + out[z, y] = max | |
89 | + | |
90 | + | |
91 | +cdef DTYPE16_t get_colour(DTYPE16_t vl, DTYPE16_t wl, DTYPE16_t ww): | |
92 | + cdef DTYPE16_t out_colour | |
93 | + cdef DTYPE16_t min_value = wl - (ww / 2) | |
94 | + cdef DTYPE16_t max_value = wl + (ww / 2) | |
95 | + if vl < min_value: | |
96 | + out_colour = min_value | |
97 | + elif vl > max_value: | |
98 | + out_colour = max_value | |
99 | + else: | |
100 | + out_colour = vl | |
101 | + | |
102 | + return out_colour | |
103 | + | |
104 | +@cython.boundscheck(False) # turn of bounds-checking for entire function | |
105 | +@cython.cdivision(True) | |
106 | +cdef float get_opacity(DTYPE16_t vl, DTYPE16_t wl, DTYPE16_t ww) nogil: | |
107 | + cdef float out_opacity | |
108 | + cdef DTYPE16_t min_value = wl - (ww / 2) | |
109 | + cdef DTYPE16_t max_value = wl + (ww / 2) | |
110 | + if vl < min_value: | |
111 | + out_opacity = 0.0 | |
112 | + elif vl > max_value: | |
113 | + out_opacity = 1.0 | |
114 | + else: | |
115 | + out_opacity = 1.0/(max_value - min_value) * (vl - min_value) | |
116 | + | |
117 | + return out_opacity | |
118 | + | |
119 | +@cython.boundscheck(False) # turn of bounds-checking for entire function | |
120 | +@cython.cdivision(True) | |
121 | +cdef float get_opacity_f32(DTYPEF32_t vl, DTYPE16_t wl, DTYPE16_t ww) nogil: | |
122 | + cdef float out_opacity | |
123 | + cdef DTYPE16_t min_value = wl - (ww / 2) | |
124 | + cdef DTYPE16_t max_value = wl + (ww / 2) | |
125 | + if vl < min_value: | |
126 | + out_opacity = 0.0 | |
127 | + elif vl > max_value: | |
128 | + out_opacity = 1.0 | |
129 | + else: | |
130 | + out_opacity = 1.0/(max_value - min_value) * (vl - min_value) | |
131 | + | |
132 | + return out_opacity | |
133 | + | |
134 | + | |
135 | +@cython.boundscheck(False) # turn of bounds-checking for entire function | |
136 | +@cython.cdivision(True) | |
137 | +def mida(np.ndarray[DTYPE16_t, ndim=3] image, int axis, DTYPE16_t wl, | |
138 | + DTYPE16_t ww, np.ndarray[DTYPE16_t, ndim=2] out): | |
139 | + cdef int sz = image.shape[0] | |
140 | + cdef int sy = image.shape[1] | |
141 | + cdef int sx = image.shape[2] | |
142 | + | |
143 | + cdef DTYPE16_t min = image.min() | |
144 | + cdef DTYPE16_t max = image.max() | |
145 | + cdef DTYPE16_t vl | |
146 | + | |
147 | + cdef DTYPE16_t min_value = wl - (ww / 2) | |
148 | + cdef DTYPE16_t max_value = wl + (ww / 2) | |
149 | + | |
150 | + cdef float fmax=0.0 | |
151 | + cdef float fpi | |
152 | + cdef float dl | |
153 | + cdef float bt | |
154 | + | |
155 | + cdef float alpha | |
156 | + cdef float alpha_p = 0.0 | |
157 | + cdef float colour | |
158 | + cdef float colour_p = 0 | |
159 | + | |
160 | + cdef int x, y, z | |
161 | + | |
162 | + # AXIAL | |
163 | + if axis == 0: | |
164 | + for x in prange(sx, nogil=True): | |
165 | + for y in xrange(sy): | |
166 | + fmax = 0.0 | |
167 | + alpha_p = 0.0 | |
168 | + colour_p = 0.0 | |
169 | + for z in xrange(sz): | |
170 | + vl = image[z, y, x] | |
171 | + fpi = 1.0/(max - min) * (vl - min) | |
172 | + if fpi > fmax: | |
173 | + dl = fpi - fmax | |
174 | + fmax = fpi | |
175 | + else: | |
176 | + dl = 0.0 | |
177 | + | |
178 | + bt = 1.0 - dl | |
179 | + | |
180 | + colour = fpi | |
181 | + alpha = get_opacity(vl, wl, ww) | |
182 | + colour = (bt * colour_p) + (1 - bt * alpha_p) * colour * alpha | |
183 | + alpha = (bt * alpha_p) + (1 - bt * alpha_p) * alpha | |
184 | + | |
185 | + colour_p = colour | |
186 | + alpha_p = alpha | |
187 | + | |
188 | + if alpha >= 1.0: | |
189 | + break | |
190 | + | |
191 | + | |
192 | + #out[y, x] = <DTYPE16_t>((max_value - min_value) * colour + min_value) | |
193 | + out[y, x] = <DTYPE16_t>((max - min) * colour + min) | |
194 | + | |
195 | + | |
196 | + #CORONAL | |
197 | + elif axis == 1: | |
198 | + for z in prange(sz, nogil=True): | |
199 | + for x in xrange(sx): | |
200 | + fmax = 0.0 | |
201 | + alpha_p = 0.0 | |
202 | + colour_p = 0.0 | |
203 | + for y in xrange(sy): | |
204 | + vl = image[z, y, x] | |
205 | + fpi = 1.0/(max - min) * (vl - min) | |
206 | + if fpi > fmax: | |
207 | + dl = fpi - fmax | |
208 | + fmax = fpi | |
209 | + else: | |
210 | + dl = 0.0 | |
211 | + | |
212 | + bt = 1.0 - dl | |
213 | + | |
214 | + colour = fpi | |
215 | + alpha = get_opacity(vl, wl, ww) | |
216 | + colour = (bt * colour_p) + (1 - bt * alpha_p) * colour * alpha | |
217 | + alpha = (bt * alpha_p) + (1 - bt * alpha_p) * alpha | |
218 | + | |
219 | + colour_p = colour | |
220 | + alpha_p = alpha | |
221 | + | |
222 | + if alpha >= 1.0: | |
223 | + break | |
224 | + | |
225 | + out[z, x] = <DTYPE16_t>((max - min) * colour + min) | |
226 | + | |
227 | + #AXIAL | |
228 | + elif axis == 2: | |
229 | + for z in prange(sz, nogil=True): | |
230 | + for y in xrange(sy): | |
231 | + fmax = 0.0 | |
232 | + alpha_p = 0.0 | |
233 | + colour_p = 0.0 | |
234 | + for x in xrange(sx): | |
235 | + vl = image[z, y, x] | |
236 | + fpi = 1.0/(max - min) * (vl - min) | |
237 | + if fpi > fmax: | |
238 | + dl = fpi - fmax | |
239 | + fmax = fpi | |
240 | + else: | |
241 | + dl = 0.0 | |
242 | + | |
243 | + bt = 1.0 - dl | |
244 | + | |
245 | + colour = fpi | |
246 | + alpha = get_opacity(vl, wl, ww) | |
247 | + colour = (bt * colour_p) + (1 - bt * alpha_p) * colour * alpha | |
248 | + alpha = (bt * alpha_p) + (1 - bt * alpha_p) * alpha | |
249 | + | |
250 | + colour_p = colour | |
251 | + alpha_p = alpha | |
252 | + | |
253 | + if alpha >= 1.0: | |
254 | + break | |
255 | + | |
256 | + out[z, y] = <DTYPE16_t>((max - min) * colour + min) | |
257 | + | |
258 | + | |
259 | + | |
260 | +@cython.boundscheck(False) # turn of bounds-checking for entire function | |
261 | +@cython.cdivision(True) | |
262 | +cdef inline void finite_difference(DTYPE16_t[:, :, :] image, | |
263 | + int x, int y, int z, float h, float *g) nogil: | |
264 | + cdef int px, py, pz, fx, fy, fz | |
265 | + | |
266 | + cdef int sz = image.shape[0] | |
267 | + cdef int sy = image.shape[1] | |
268 | + cdef int sx = image.shape[2] | |
269 | + | |
270 | + cdef float gx, gy, gz | |
271 | + | |
272 | + if x == 0: | |
273 | + px = 0 | |
274 | + fx = 1 | |
275 | + elif x == sx - 1: | |
276 | + px = x - 1 | |
277 | + fx = x | |
278 | + else: | |
279 | + px = x - 1 | |
280 | + fx = x + 1 | |
281 | + | |
282 | + if y == 0: | |
283 | + py = 0 | |
284 | + fy = 1 | |
285 | + elif y == sy - 1: | |
286 | + py = y - 1 | |
287 | + fy = y | |
288 | + else: | |
289 | + py = y - 1 | |
290 | + fy = y + 1 | |
291 | + | |
292 | + if z == 0: | |
293 | + pz = 0 | |
294 | + fz = 1 | |
295 | + elif z == sz - 1: | |
296 | + pz = z - 1 | |
297 | + fz = z | |
298 | + else: | |
299 | + pz = z - 1 | |
300 | + fz = z + 1 | |
301 | + | |
302 | + gx = (image[z, y, fx] - image[z, y, px]) / (2*h) | |
303 | + gy = (image[z, fy, x] - image[z, py, x]) / (2*h) | |
304 | + gz = (image[fz, y, x] - image[pz, y, x]) / (2*h) | |
305 | + | |
306 | + g[0] = gx | |
307 | + g[1] = gy | |
308 | + g[2] = gz | |
309 | + | |
310 | + | |
311 | + | |
312 | +@cython.boundscheck(False) # turn of bounds-checking for entire function | |
313 | +@cython.cdivision(True) | |
314 | +cdef inline float calc_fcm_itensity(DTYPE16_t[:, :, :] image, | |
315 | + int x, int y, int z, float n, float* dir) nogil: | |
316 | + cdef float g[3] | |
317 | + finite_difference(image, x, y, z, 1.0, g) | |
318 | + cdef float gm = sqrt(g[0]*g[0] + g[1]*g[1] + g[2]*g[2]) | |
319 | + cdef float d = g[0]*dir[0] + g[1]*dir[1] + g[2]*dir[2] | |
320 | + cdef float sf = (1.0 - fabs(d/gm))**n | |
321 | + #alpha = get_opacity_f32(gm, wl, ww) | |
322 | + cdef float vl = gm * sf | |
323 | + return vl | |
324 | + | |
325 | +@cython.boundscheck(False) # turn of bounds-checking for entire function | |
326 | +@cython.cdivision(True) | |
327 | +def fast_countour_mip(np.ndarray[DTYPE16_t, ndim=3] image, | |
328 | + float n, | |
329 | + int axis, | |
330 | + DTYPE16_t wl, DTYPE16_t ww, | |
331 | + int tmip, | |
332 | + np.ndarray[DTYPE16_t, ndim=2] out): | |
333 | + cdef int sz = image.shape[0] | |
334 | + cdef int sy = image.shape[1] | |
335 | + cdef int sx = image.shape[2] | |
336 | + cdef float gm | |
337 | + cdef float alpha | |
338 | + cdef float sf | |
339 | + cdef float d | |
340 | + | |
341 | + cdef float* g | |
342 | + cdef float* dir = [ 0, 0, 0 ] | |
343 | + | |
344 | + cdef DTYPE16_t[:, :, :] vimage = image | |
345 | + cdef np.ndarray[DTYPE16_t, ndim=3] tmp = np.empty_like(image) | |
346 | + | |
347 | + cdef DTYPE16_t min = image.min() | |
348 | + cdef DTYPE16_t max = image.max() | |
349 | + cdef float fmin = <float>min | |
350 | + cdef float fmax = <float>max | |
351 | + cdef float vl | |
352 | + cdef DTYPE16_t V | |
353 | + | |
354 | + cdef int x, y, z | |
355 | + | |
356 | + if axis == 0: | |
357 | + dir[2] = 1.0 | |
358 | + elif axis == 1: | |
359 | + dir[1] = 1.0 | |
360 | + elif axis == 2: | |
361 | + dir[0] = 1.0 | |
362 | + | |
363 | + for z in prange(sz, nogil=True): | |
364 | + for y in range(sy): | |
365 | + for x in range(sx): | |
366 | + vl = calc_fcm_itensity(vimage, x, y, z, n, dir) | |
367 | + tmp[z, y, x] = <DTYPE16_t>vl | |
368 | + | |
369 | + cdef DTYPE16_t tmin = tmp.min() | |
370 | + cdef DTYPE16_t tmax = tmp.max() | |
371 | + | |
372 | + #tmp = ((max - min)/<float>(tmax - tmin)) * (tmp - tmin) + min | |
373 | + | |
374 | + if tmip == 0: | |
375 | + out[:] = tmp.max(axis) | |
376 | + elif tmip == 1: | |
377 | + lmip(tmp, axis, 700, 3033, out) | |
378 | + elif tmip == 2: | |
379 | + mida(tmp, axis, wl, ww, out) | ... | ... |
invesalius/data/slice_.py
... | ... | @@ -33,6 +33,7 @@ import utils |
33 | 33 | |
34 | 34 | from mask import Mask |
35 | 35 | from project import Project |
36 | +from data import mips | |
36 | 37 | |
37 | 38 | OTHER=0 |
38 | 39 | PLIST=1 |
... | ... | @@ -83,6 +84,10 @@ class Slice(object): |
83 | 84 | self.blend_filter = None |
84 | 85 | self.histogram = None |
85 | 86 | self._matrix = None |
87 | + | |
88 | + self._type_projection = const.PROJECTION_NORMAL | |
89 | + self.n_border = const.PROJECTION_BORDER_SIZE | |
90 | + | |
86 | 91 | self.spacing = (1.0, 1.0, 1.0) |
87 | 92 | |
88 | 93 | self.number_of_colours = 256 |
... | ... | @@ -111,7 +116,7 @@ class Slice(object): |
111 | 116 | def matrix(self, value): |
112 | 117 | self._matrix = value |
113 | 118 | i, e = value.min(), value.max() |
114 | - r = e - i | |
119 | + r = int(e) - int(i) | |
115 | 120 | self.histogram = numpy.histogram(self._matrix, r, (i, e))[0] |
116 | 121 | |
117 | 122 | def __bind_events(self): |
... | ... | @@ -133,6 +138,7 @@ class Slice(object): |
133 | 138 | 'Change mask colour') |
134 | 139 | Publisher.subscribe(self.__set_mask_name, 'Change mask name') |
135 | 140 | Publisher.subscribe(self.__show_mask, 'Show mask') |
141 | + Publisher.subscribe(self.__hide_current_mask, 'Hide current mask') | |
136 | 142 | |
137 | 143 | Publisher.subscribe(self.__set_current_mask_threshold_limits, |
138 | 144 | 'Update threshold limits') |
... | ... | @@ -148,6 +154,7 @@ class Slice(object): |
148 | 154 | |
149 | 155 | Publisher.subscribe(self.UpdateColourTableBackgroundWidget,\ |
150 | 156 | 'Change colour table from background image from widget') |
157 | + Publisher.subscribe(self._set_projection_type, 'Set projection type') | |
151 | 158 | |
152 | 159 | Publisher.subscribe(self.InputImageWidget, 'Input Image in the widget') |
153 | 160 | |
... | ... | @@ -346,6 +353,16 @@ class Slice(object): |
346 | 353 | if not value: |
347 | 354 | Publisher.sendMessage('Select mask name in combo', -1) |
348 | 355 | |
356 | + if self._type_projection != const.PROJECTION_NORMAL: | |
357 | + self.SetTypeProjection(const.PROJECTION_NORMAL) | |
358 | + Publisher.sendMessage('Reload actual slice') | |
359 | + | |
360 | + def __hide_current_mask(self, pubsub_evt): | |
361 | + if self.current_mask: | |
362 | + index = self.current_mask.index | |
363 | + value = False | |
364 | + Publisher.sendMessage('Show mask', (index, value)) | |
365 | + | |
349 | 366 | def edit_mask_pixel(self, operation, index, position, radius, orientation): |
350 | 367 | mask = self.buffer_slices[orientation].mask |
351 | 368 | image = self.buffer_slices[orientation].image |
... | ... | @@ -421,12 +438,16 @@ class Slice(object): |
421 | 438 | self.buffer_slices[orientation].discard_vtk_mask() |
422 | 439 | |
423 | 440 | |
424 | - def GetSlices(self, orientation, slice_number): | |
425 | - if self.buffer_slices[orientation].index == slice_number: | |
441 | + def GetSlices(self, orientation, slice_number, number_slices, | |
442 | + inverted=False, border_size=1.0): | |
443 | + if self.buffer_slices[orientation].index == slice_number and \ | |
444 | + self._type_projection == const.PROJECTION_NORMAL: | |
426 | 445 | if self.buffer_slices[orientation].vtk_image: |
427 | 446 | image = self.buffer_slices[orientation].vtk_image |
428 | 447 | else: |
429 | - n_image = self.get_image_slice(orientation, slice_number) | |
448 | + n_image = self.get_image_slice(orientation, slice_number, | |
449 | + number_slices, inverted, | |
450 | + border_size) | |
430 | 451 | image = converters.to_vtk(n_image, self.spacing, slice_number, orientation) |
431 | 452 | ww_wl_image = self.do_ww_wl(image) |
432 | 453 | image = self.do_colour_image(ww_wl_image) |
... | ... | @@ -446,7 +467,8 @@ class Slice(object): |
446 | 467 | final_image = image |
447 | 468 | self.buffer_slices[orientation].vtk_image = image |
448 | 469 | else: |
449 | - n_image = self.get_image_slice(orientation, slice_number) | |
470 | + n_image = self.get_image_slice(orientation, slice_number, | |
471 | + number_slices, inverted, border_size) | |
450 | 472 | image = converters.to_vtk(n_image, self.spacing, slice_number, orientation) |
451 | 473 | ww_wl_image = self.do_ww_wl(image) |
452 | 474 | image = self.do_colour_image(ww_wl_image) |
... | ... | @@ -469,17 +491,151 @@ class Slice(object): |
469 | 491 | |
470 | 492 | return final_image |
471 | 493 | |
472 | - def get_image_slice(self, orientation, slice_number): | |
494 | + def get_image_slice(self, orientation, slice_number, number_slices=1, | |
495 | + inverted=False, border_size=1.0): | |
473 | 496 | if self.buffer_slices[orientation].index == slice_number \ |
474 | 497 | and self.buffer_slices[orientation].image is not None: |
475 | 498 | n_image = self.buffer_slices[orientation].image |
476 | 499 | else: |
500 | + | |
477 | 501 | if orientation == 'AXIAL': |
478 | - n_image = numpy.array(self.matrix[slice_number]) | |
502 | + if self._type_projection == const.PROJECTION_NORMAL: | |
503 | + n_image = numpy.array(self.matrix[slice_number]) | |
504 | + else: | |
505 | + tmp_array = numpy.array(self.matrix[slice_number: | |
506 | + slice_number + number_slices]) | |
507 | + if inverted: | |
508 | + tmp_array = tmp_array[::-1] | |
509 | + | |
510 | + if self._type_projection == const.PROJECTION_MaxIP: | |
511 | + n_image = numpy.array(tmp_array).max(0) | |
512 | + elif self._type_projection == const.PROJECTION_MinIP: | |
513 | + n_image = numpy.array(tmp_array).min(0) | |
514 | + elif self._type_projection == const.PROJECTION_MeanIP: | |
515 | + n_image = numpy.array(tmp_array).mean(0) | |
516 | + elif self._type_projection == const.PROJECTION_LMIP: | |
517 | + n_image = numpy.empty(shape=(tmp_array.shape[1], | |
518 | + tmp_array.shape[2]), | |
519 | + dtype=tmp_array.dtype) | |
520 | + mips.lmip(tmp_array, 0, self.window_level, self.window_level, n_image) | |
521 | + elif self._type_projection == const.PROJECTION_MIDA: | |
522 | + n_image = numpy.empty(shape=(tmp_array.shape[1], | |
523 | + tmp_array.shape[2]), | |
524 | + dtype=tmp_array.dtype) | |
525 | + mips.mida(tmp_array, 0, self.window_level, self.window_level, n_image) | |
526 | + elif self._type_projection == const.PROJECTION_CONTOUR_MIP: | |
527 | + n_image = numpy.empty(shape=(tmp_array.shape[1], | |
528 | + tmp_array.shape[2]), | |
529 | + dtype=tmp_array.dtype) | |
530 | + mips.fast_countour_mip(tmp_array, border_size, 0, self.window_level, | |
531 | + self.window_level, 0, n_image) | |
532 | + elif self._type_projection == const.PROJECTION_CONTOUR_LMIP: | |
533 | + n_image = numpy.empty(shape=(tmp_array.shape[1], | |
534 | + tmp_array.shape[2]), | |
535 | + dtype=tmp_array.dtype) | |
536 | + mips.fast_countour_mip(tmp_array, border_size, 0, self.window_level, | |
537 | + self.window_level, 1, n_image) | |
538 | + elif self._type_projection == const.PROJECTION_CONTOUR_MIDA: | |
539 | + n_image = numpy.empty(shape=(tmp_array.shape[1], | |
540 | + tmp_array.shape[2]), | |
541 | + dtype=tmp_array.dtype) | |
542 | + mips.fast_countour_mip(tmp_array, border_size, 0, self.window_level, | |
543 | + self.window_level, 2, n_image) | |
544 | + else: | |
545 | + n_image = numpy.array(self.matrix[slice_number]) | |
546 | + | |
479 | 547 | elif orientation == 'CORONAL': |
480 | - n_image = numpy.array(self.matrix[..., slice_number, ...]) | |
548 | + if self._type_projection == const.PROJECTION_NORMAL: | |
549 | + n_image = numpy.array(self.matrix[..., slice_number, ...]) | |
550 | + else: | |
551 | + #if slice_number == 0: | |
552 | + #slice_number = 1 | |
553 | + #if slice_number - number_slices < 0: | |
554 | + #number_slices = slice_number | |
555 | + tmp_array = numpy.array(self.matrix[..., slice_number: slice_number + number_slices, ...]) | |
556 | + if inverted: | |
557 | + tmp_array = tmp_array[..., ::-1, ...] | |
558 | + if self._type_projection == const.PROJECTION_MaxIP: | |
559 | + n_image = numpy.array(tmp_array).max(1) | |
560 | + elif self._type_projection == const.PROJECTION_MinIP: | |
561 | + n_image = numpy.array(tmp_array).min(1) | |
562 | + elif self._type_projection == const.PROJECTION_MeanIP: | |
563 | + n_image = numpy.array(tmp_array).mean(1) | |
564 | + elif self._type_projection == const.PROJECTION_LMIP: | |
565 | + n_image = numpy.empty(shape=(tmp_array.shape[0], | |
566 | + tmp_array.shape[2]), | |
567 | + dtype=tmp_array.dtype) | |
568 | + mips.lmip(tmp_array, 1, self.window_level, self.window_level, n_image) | |
569 | + elif self._type_projection == const.PROJECTION_MIDA: | |
570 | + n_image = numpy.empty(shape=(tmp_array.shape[0], | |
571 | + tmp_array.shape[2]), | |
572 | + dtype=tmp_array.dtype) | |
573 | + mips.mida(tmp_array, 1, self.window_level, self.window_level, n_image) | |
574 | + elif self._type_projection == const.PROJECTION_CONTOUR_MIP: | |
575 | + n_image = numpy.empty(shape=(tmp_array.shape[0], | |
576 | + tmp_array.shape[2]), | |
577 | + dtype=tmp_array.dtype) | |
578 | + mips.fast_countour_mip(tmp_array, border_size, 1, self.window_level, | |
579 | + self.window_level, 0, n_image) | |
580 | + elif self._type_projection == const.PROJECTION_CONTOUR_LMIP: | |
581 | + n_image = numpy.empty(shape=(tmp_array.shape[0], | |
582 | + tmp_array.shape[2]), | |
583 | + dtype=tmp_array.dtype) | |
584 | + mips.fast_countour_mip(tmp_array, border_size, 1, self.window_level, | |
585 | + self.window_level, 1, n_image) | |
586 | + elif self._type_projection == const.PROJECTION_CONTOUR_MIDA: | |
587 | + n_image = numpy.empty(shape=(tmp_array.shape[0], | |
588 | + tmp_array.shape[2]), | |
589 | + dtype=tmp_array.dtype) | |
590 | + mips.fast_countour_mip(tmp_array, border_size, 1, self.window_level, | |
591 | + self.window_level, 2, n_image) | |
592 | + else: | |
593 | + n_image = numpy.array(self.matrix[..., slice_number, ...]) | |
481 | 594 | elif orientation == 'SAGITAL': |
482 | - n_image = numpy.array(self.matrix[..., ..., slice_number]) | |
595 | + if self._type_projection == const.PROJECTION_NORMAL: | |
596 | + n_image = numpy.array(self.matrix[..., ..., slice_number]) | |
597 | + else: | |
598 | + tmp_array = numpy.array(self.matrix[..., ..., | |
599 | + slice_number: slice_number + number_slices]) | |
600 | + if inverted: | |
601 | + tmp_array = tmp_array[..., ..., ::-1] | |
602 | + if self._type_projection == const.PROJECTION_MaxIP: | |
603 | + n_image = numpy.array(tmp_array).max(2) | |
604 | + elif self._type_projection == const.PROJECTION_MinIP: | |
605 | + n_image = numpy.array(tmp_array).min(2) | |
606 | + elif self._type_projection == const.PROJECTION_MeanIP: | |
607 | + n_image = numpy.array(tmp_array).mean(2) | |
608 | + elif self._type_projection == const.PROJECTION_LMIP: | |
609 | + n_image = numpy.empty(shape=(tmp_array.shape[0], | |
610 | + tmp_array.shape[1]), | |
611 | + dtype=tmp_array.dtype) | |
612 | + mips.lmip(tmp_array, 2, self.window_level, self.window_level, n_image) | |
613 | + elif self._type_projection == const.PROJECTION_MIDA: | |
614 | + n_image = numpy.empty(shape=(tmp_array.shape[0], | |
615 | + tmp_array.shape[1]), | |
616 | + dtype=tmp_array.dtype) | |
617 | + mips.mida(tmp_array, 2, self.window_level, self.window_level, n_image) | |
618 | + | |
619 | + elif self._type_projection == const.PROJECTION_CONTOUR_MIP: | |
620 | + n_image = numpy.empty(shape=(tmp_array.shape[0], | |
621 | + tmp_array.shape[1]), | |
622 | + dtype=tmp_array.dtype) | |
623 | + mips.fast_countour_mip(tmp_array, border_size, 2, self.window_level, | |
624 | + self.window_level, 0, n_image) | |
625 | + elif self._type_projection == const.PROJECTION_CONTOUR_LMIP: | |
626 | + n_image = numpy.empty(shape=(tmp_array.shape[0], | |
627 | + tmp_array.shape[1]), | |
628 | + dtype=tmp_array.dtype) | |
629 | + mips.fast_countour_mip(tmp_array, border_size, 2, self.window_level, | |
630 | + self.window_level, 1, n_image) | |
631 | + elif self._type_projection == const.PROJECTION_CONTOUR_MIDA: | |
632 | + n_image = numpy.empty(shape=(tmp_array.shape[0], | |
633 | + tmp_array.shape[1]), | |
634 | + dtype=tmp_array.dtype) | |
635 | + mips.fast_countour_mip(tmp_array, border_size, 2, self.window_level, | |
636 | + self.window_level, 2, n_image) | |
637 | + else: | |
638 | + n_image = numpy.array(self.matrix[..., ..., slice_number]) | |
483 | 639 | return n_image |
484 | 640 | |
485 | 641 | def get_mask_slice(self, orientation, slice_number): |
... | ... | @@ -672,6 +828,26 @@ class Slice(object): |
672 | 828 | def GetOutput(self): |
673 | 829 | return self.blend_filter.GetOutput() |
674 | 830 | |
831 | + def _set_projection_type(self, pubsub_evt): | |
832 | + tprojection = pubsub_evt.data | |
833 | + self.SetTypeProjection(tprojection) | |
834 | + | |
835 | + def SetTypeProjection(self, tprojection): | |
836 | + if self._type_projection != tprojection: | |
837 | + if self._type_projection == const.PROJECTION_NORMAL: | |
838 | + Publisher.sendMessage('Hide current mask') | |
839 | + | |
840 | + if tprojection == const.PROJECTION_NORMAL: | |
841 | + Publisher.sendMessage('Show MIP interface', False) | |
842 | + else: | |
843 | + Publisher.sendMessage('Show MIP interface', True) | |
844 | + | |
845 | + self._type_projection = tprojection | |
846 | + for buffer_ in self.buffer_slices.values(): | |
847 | + buffer_.discard_buffer() | |
848 | + | |
849 | + Publisher.sendMessage('Check projection menu', tprojection) | |
850 | + | |
675 | 851 | def SetInput(self, imagedata, mask_dict): |
676 | 852 | print "SETINPUT!" |
677 | 853 | self.imagedata = imagedata |
... | ... | @@ -731,7 +907,14 @@ class Slice(object): |
731 | 907 | self.window_level = level |
732 | 908 | |
733 | 909 | for buffer_ in self.buffer_slices.values(): |
734 | - buffer_.discard_vtk_image() | |
910 | + if self._type_projection in (const.PROJECTION_NORMAL, | |
911 | + const.PROJECTION_MaxIP, | |
912 | + const.PROJECTION_MinIP, | |
913 | + const.PROJECTION_MeanIP, | |
914 | + const.PROJECTION_LMIP): | |
915 | + buffer_.discard_vtk_image() | |
916 | + else: | |
917 | + buffer_.discard_buffer() | |
735 | 918 | |
736 | 919 | Publisher.sendMessage('Reload actual slice') |
737 | 920 | |
... | ... | @@ -758,7 +941,14 @@ class Slice(object): |
758 | 941 | self.nodes = pubsub_evt.data |
759 | 942 | self.from_= WIDGET |
760 | 943 | for buffer_ in self.buffer_slices.values(): |
761 | - buffer_.discard_vtk_image() | |
944 | + if self._type_projection in (const.PROJECTION_NORMAL, | |
945 | + const.PROJECTION_MaxIP, | |
946 | + const.PROJECTION_MinIP, | |
947 | + const.PROJECTION_MeanIP, | |
948 | + const.PROJECTION_LMIP): | |
949 | + buffer_.discard_vtk_image() | |
950 | + else: | |
951 | + buffer_.discard_buffer() | |
762 | 952 | |
763 | 953 | knodes = sorted(self.nodes) |
764 | 954 | p0 = knodes[0].value | ... | ... |
invesalius/data/slice_data.py
... | ... | @@ -136,9 +136,13 @@ class SliceData(object): |
136 | 136 | self.overlay_renderer.AddActor(cursor.actor) |
137 | 137 | self.cursor = cursor |
138 | 138 | |
139 | - def SetNumber(self, number): | |
140 | - self.number = number | |
141 | - self.text.SetValue("%d" % self.number) | |
139 | + def SetNumber(self, init, end=None): | |
140 | + if end is None: | |
141 | + self.number = init | |
142 | + self.text.SetValue("%d" % self.number) | |
143 | + else: | |
144 | + self.number = init | |
145 | + self.text.SetValue("%d - %d" % (init, end)) | |
142 | 146 | self.text.SetPosition(const.TEXT_POS_LEFT_DOWN_ZERO) |
143 | 147 | |
144 | 148 | def SetOrientation(self, orientation): | ... | ... |
invesalius/data/viewer_slice.py
... | ... | @@ -32,6 +32,11 @@ import styles |
32 | 32 | import wx |
33 | 33 | from wx.lib.pubsub import pub as Publisher |
34 | 34 | |
35 | +try: | |
36 | + from agw import floatspin as FS | |
37 | +except ImportError: # if it's not there locally, try the wxPython lib. | |
38 | + import wx.lib.agw.floatspin as FS | |
39 | + | |
35 | 40 | import constants as const |
36 | 41 | import cursor_actors as ca |
37 | 42 | import data.slice_ as sl |
... | ... | @@ -51,6 +56,93 @@ ORIENTATIONS = { |
51 | 56 | "SAGITAL": const.SAGITAL, |
52 | 57 | } |
53 | 58 | |
59 | + | |
60 | +class ContourMIPConfig(wx.Panel): | |
61 | + def __init__(self, prnt, orientation): | |
62 | + wx.Panel.__init__(self, prnt) | |
63 | + self.mip_size_spin = wx.SpinCtrl(self, -1, min=1, max=240, | |
64 | + initial=const.PROJECTION_MIP_SIZE) | |
65 | + self.mip_size_spin.SetToolTip(wx.ToolTip(_("Number of slices used to compound the visualization"))) | |
66 | + w, h = self.mip_size_spin.GetTextExtent('M') | |
67 | + self.mip_size_spin.SetMinSize((5 * w + 10, -1)) | |
68 | + self.mip_size_spin.SetMaxSize((5 * w + 10, -1)) | |
69 | + | |
70 | + self.border_spin = FS.FloatSpin(self, -1, min_val=0, max_val=10, | |
71 | + increment=0.1, | |
72 | + value=const.PROJECTION_BORDER_SIZE, | |
73 | + digits=1, agwStyle=FS.FS_LEFT) | |
74 | + self.border_spin.SetToolTip(wx.ToolTip(_("Controls the sharpness of the" | |
75 | + " contour. The greater the" | |
76 | + " value, the sharper the" | |
77 | + " contour"))) | |
78 | + w, h = self.border_spin.GetTextExtent('M') | |
79 | + self.border_spin.SetMinSize((5 * w + 10, -1)) | |
80 | + self.border_spin.SetMaxSize((5 * w + 10, -1)) | |
81 | + | |
82 | + self.inverted = wx.CheckBox(self, -1, _("inverted")) | |
83 | + self.inverted.SetToolTip(wx.ToolTip(_("If checked, the slices are" | |
84 | + " traversed in descending" | |
85 | + " order to compound the" | |
86 | + " visualization instead of" | |
87 | + " ascending order"))) | |
88 | + | |
89 | + txt_mip_size = wx.StaticText(self, -1, _("Number of slices"), style=wx.ALIGN_CENTER_HORIZONTAL) | |
90 | + self.txt_mip_border = wx.StaticText(self, -1, _("Sharpness")) | |
91 | + | |
92 | + sizer = wx.BoxSizer(wx.HORIZONTAL) | |
93 | + sizer.Add(txt_mip_size, 0, wx.EXPAND | wx.ALL, 2) | |
94 | + sizer.Add(self.mip_size_spin, 0, wx.EXPAND) | |
95 | + sizer.AddSpacer((10, 0)) | |
96 | + sizer.Add(self.txt_mip_border, 0, wx.EXPAND | wx.ALL, 2) | |
97 | + sizer.Add(self.border_spin, 0, wx.EXPAND) | |
98 | + sizer.AddSpacer((10, 0)) | |
99 | + sizer.Add(self.inverted, 0, wx.EXPAND) | |
100 | + self.SetSizer(sizer) | |
101 | + sizer.Fit(self) | |
102 | + | |
103 | + self.Layout() | |
104 | + self.Update() | |
105 | + self.SetAutoLayout(1) | |
106 | + | |
107 | + self.orientation = orientation | |
108 | + | |
109 | + self.mip_size_spin.Bind(wx.EVT_SPINCTRL, self.OnSetMIPSize) | |
110 | + self.border_spin.Bind(wx.EVT_SPINCTRL, self.OnSetMIPBorder) | |
111 | + self.inverted.Bind(wx.EVT_CHECKBOX, self.OnCheckInverted) | |
112 | + | |
113 | + Publisher.subscribe(self._set_projection_type, 'Set projection type') | |
114 | + | |
115 | + def OnSetMIPSize(self, evt): | |
116 | + val = self.mip_size_spin.GetValue() | |
117 | + Publisher.sendMessage('Set MIP size %s' % self.orientation, val) | |
118 | + | |
119 | + def OnSetMIPBorder(self, evt): | |
120 | + val = self.border_spin.GetValue() | |
121 | + Publisher.sendMessage('Set MIP border %s' % self.orientation, val) | |
122 | + | |
123 | + def OnCheckInverted(self, evt): | |
124 | + val = self.inverted.GetValue() | |
125 | + Publisher.sendMessage('Set MIP Invert %s' % self.orientation, val) | |
126 | + | |
127 | + def _set_projection_type(self, pubsub_evt): | |
128 | + tprojection = pubsub_evt.data | |
129 | + | |
130 | + if tprojection in (const.PROJECTION_MIDA, | |
131 | + const.PROJECTION_CONTOUR_MIDA): | |
132 | + self.inverted.Enable() | |
133 | + else: | |
134 | + self.inverted.Disable() | |
135 | + | |
136 | + if tprojection in (const.PROJECTION_CONTOUR_MIP, | |
137 | + const.PROJECTION_CONTOUR_MIDA): | |
138 | + self.border_spin.Enable() | |
139 | + self.txt_mip_border.Enable() | |
140 | + else: | |
141 | + self.border_spin.Disable() | |
142 | + self.txt_mip_border.Disable() | |
143 | + | |
144 | + | |
145 | + | |
54 | 146 | class Viewer(wx.Panel): |
55 | 147 | |
56 | 148 | def __init__(self, prnt, orientation='AXIAL'): |
... | ... | @@ -63,6 +155,9 @@ class Viewer(wx.Panel): |
63 | 155 | #self.modes = []#['DEFAULT'] |
64 | 156 | self.left_pressed = 0 |
65 | 157 | self.right_pressed = 0 |
158 | + | |
159 | + self._number_slices = const.PROJECTION_MIP_SIZE | |
160 | + self._mip_inverted = False | |
66 | 161 | |
67 | 162 | self.spined_image = False #Use to control to spin |
68 | 163 | self.paned_image = False |
... | ... | @@ -83,11 +178,11 @@ class Viewer(wx.Panel): |
83 | 178 | self.actors_by_slice_number = {} |
84 | 179 | self.renderers_by_slice_number = {} |
85 | 180 | |
86 | - self.__init_gui() | |
87 | - | |
88 | 181 | self.orientation = orientation |
89 | 182 | self.slice_number = 0 |
90 | 183 | |
184 | + self.__init_gui() | |
185 | + | |
91 | 186 | self._brush_cursor_op = const.DEFAULT_BRUSH_OP |
92 | 187 | self._brush_cursor_size = const.BRUSH_SIZE |
93 | 188 | self._brush_cursor_colour = const.BRUSH_COLOUR |
... | ... | @@ -111,12 +206,17 @@ class Viewer(wx.Panel): |
111 | 206 | |
112 | 207 | scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) |
113 | 208 | self.scroll = scroll |
209 | + | |
210 | + self.mip_ctrls = ContourMIPConfig(self, self.orientation) | |
211 | + self.mip_ctrls.Hide() | |
212 | + | |
114 | 213 | sizer = wx.BoxSizer(wx.HORIZONTAL) |
115 | 214 | sizer.Add(self.interactor, 1, wx.EXPAND|wx.GROW) |
215 | + sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) | |
116 | 216 | |
117 | - background_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
217 | + background_sizer = wx.BoxSizer(wx.VERTICAL) | |
118 | 218 | background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) |
119 | - background_sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) | |
219 | + #background_sizer.Add(self.mip_ctrls, 0, wx.EXPAND|wx.GROW|wx.ALL, 2) | |
120 | 220 | self.SetSizer(background_sizer) |
121 | 221 | background_sizer.Fit(self) |
122 | 222 | |
... | ... | @@ -644,6 +744,13 @@ class Viewer(wx.Panel): |
644 | 744 | Publisher.subscribe(self.ReloadActualSlice, 'Reload actual slice') |
645 | 745 | Publisher.subscribe(self.OnUpdateScroll, 'Update scroll') |
646 | 746 | |
747 | + | |
748 | + # MIP | |
749 | + Publisher.subscribe(self.OnSetMIPSize, 'Set MIP size %s' % self.orientation) | |
750 | + Publisher.subscribe(self.OnSetMIPBorder, 'Set MIP border %s' % self.orientation) | |
751 | + Publisher.subscribe(self.OnSetMIPInvert, 'Set MIP Invert %s' % self.orientation) | |
752 | + Publisher.subscribe(self.OnShowMIPInterface, 'Show MIP interface') | |
753 | + | |
647 | 754 | def SetDefaultCursor(self, pusub_evt): |
648 | 755 | self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) |
649 | 756 | |
... | ... | @@ -982,6 +1089,17 @@ class Viewer(wx.Panel): |
982 | 1089 | max_slice_number) |
983 | 1090 | self.set_scroll_position(0) |
984 | 1091 | |
1092 | + @property | |
1093 | + def number_slices(self): | |
1094 | + return self._number_slices | |
1095 | + | |
1096 | + @number_slices.setter | |
1097 | + def number_slices(self, val): | |
1098 | + if val != self._number_slices: | |
1099 | + self._number_slices = val | |
1100 | + buffer_ = self.slice_.buffer_slices[self.orientation] | |
1101 | + buffer_.discard_buffer() | |
1102 | + | |
985 | 1103 | def set_scroll_position(self, position): |
986 | 1104 | self.scroll.SetThumbPosition(position) |
987 | 1105 | self.OnScrollBar() |
... | ... | @@ -1016,10 +1134,19 @@ class Viewer(wx.Panel): |
1016 | 1134 | |
1017 | 1135 | def OnKeyDown(self, evt=None, obj=None): |
1018 | 1136 | pos = self.scroll.GetThumbPosition() |
1137 | + skip = True | |
1019 | 1138 | |
1020 | 1139 | min = 0 |
1021 | 1140 | max = self.slice_.GetMaxSliceNumber(self.orientation) |
1022 | 1141 | |
1142 | + projections = {wx.WXK_NUMPAD0 : const.PROJECTION_NORMAL, | |
1143 | + wx.WXK_NUMPAD1 : const.PROJECTION_MaxIP, | |
1144 | + wx.WXK_NUMPAD2 : const.PROJECTION_MinIP, | |
1145 | + wx.WXK_NUMPAD3 : const.PROJECTION_MeanIP, | |
1146 | + wx.WXK_NUMPAD4 : const.PROJECTION_MIDA, | |
1147 | + wx.WXK_NUMPAD5 : const.PROJECTION_CONTOUR_MIP, | |
1148 | + wx.WXK_NUMPAD6 : const.PROJECTION_CONTOUR_MIDA,} | |
1149 | + | |
1023 | 1150 | if self._flush_buffer: |
1024 | 1151 | self.slice_.apply_slice_buffer_to_mask(self.orientation) |
1025 | 1152 | |
... | ... | @@ -1030,11 +1157,31 @@ class Viewer(wx.Panel): |
1030 | 1157 | elif (evt.GetKeyCode() == wx.WXK_DOWN and pos < max): |
1031 | 1158 | self.OnScrollBackward() |
1032 | 1159 | self.OnScrollBar() |
1160 | + | |
1161 | + elif (evt.GetKeyCode() == wx.WXK_NUMPAD_ADD): | |
1162 | + actual_value = self.mip_ctrls.mip_size_spin.GetValue() | |
1163 | + self.mip_ctrls.mip_size_spin.SetValue(actual_value + 1) | |
1164 | + if self.mip_ctrls.mip_size_spin.GetValue() != actual_value: | |
1165 | + self.number_slices = self.mip_ctrls.mip_size_spin.GetValue() | |
1166 | + self.ReloadActualSlice() | |
1167 | + | |
1168 | + elif (evt.GetKeyCode() == wx.WXK_NUMPAD_SUBTRACT): | |
1169 | + actual_value = self.mip_ctrls.mip_size_spin.GetValue() | |
1170 | + self.mip_ctrls.mip_size_spin.SetValue(actual_value - 1) | |
1171 | + if self.mip_ctrls.mip_size_spin.GetValue() != actual_value: | |
1172 | + self.number_slices = self.mip_ctrls.mip_size_spin.GetValue() | |
1173 | + self.ReloadActualSlice() | |
1174 | + | |
1175 | + elif evt.GetKeyCode() in projections: | |
1176 | + self.slice_.SetTypeProjection(projections[evt.GetKeyCode()]) | |
1177 | + Publisher.sendMessage('Set projection type', projections[evt.GetKeyCode()]) | |
1178 | + Publisher.sendMessage('Reload actual slice') | |
1179 | + skip = False | |
1033 | 1180 | |
1034 | 1181 | self.UpdateSlice3D(pos) |
1035 | 1182 | self.interactor.Render() |
1036 | 1183 | |
1037 | - if evt: | |
1184 | + if evt and skip: | |
1038 | 1185 | evt.Skip() |
1039 | 1186 | |
1040 | 1187 | def OnScrollForward(self, evt=None, obj=None): |
... | ... | @@ -1067,15 +1214,55 @@ class Viewer(wx.Panel): |
1067 | 1214 | self.slice_data.SetSize((w, h)) |
1068 | 1215 | evt.Skip() |
1069 | 1216 | |
1217 | + def OnSetMIPSize(self, pubsub_evt): | |
1218 | + val = pubsub_evt.data | |
1219 | + self.number_slices = val | |
1220 | + self.ReloadActualSlice() | |
1221 | + | |
1222 | + def OnSetMIPBorder(self, pubsub_evt): | |
1223 | + val = pubsub_evt.data | |
1224 | + self.slice_.n_border = val | |
1225 | + buffer_ = self.slice_.buffer_slices[self.orientation] | |
1226 | + buffer_.discard_buffer() | |
1227 | + self.ReloadActualSlice() | |
1228 | + | |
1229 | + def OnSetMIPInvert(self, pubsub_evt): | |
1230 | + val = pubsub_evt.data | |
1231 | + self._mip_inverted = val | |
1232 | + buffer_ = self.slice_.buffer_slices[self.orientation] | |
1233 | + buffer_.discard_buffer() | |
1234 | + self.ReloadActualSlice() | |
1235 | + | |
1236 | + def OnShowMIPInterface(self, pubsub_evt): | |
1237 | + value = pubsub_evt.data | |
1238 | + if value: | |
1239 | + if not self.mip_ctrls.Shown: | |
1240 | + self.mip_ctrls.Show() | |
1241 | + self.GetSizer().Add(self.mip_ctrls, 0, wx.EXPAND|wx.GROW|wx.ALL, 2) | |
1242 | + self.Layout() | |
1243 | + else: | |
1244 | + self.mip_ctrls.Hide() | |
1245 | + self.GetSizer().Remove(self.mip_ctrls) | |
1246 | + self.Layout() | |
1247 | + | |
1248 | + | |
1070 | 1249 | def set_slice_number(self, index): |
1071 | - image = self.slice_.GetSlices(self.orientation, index) | |
1250 | + inverted = self.mip_ctrls.inverted.GetValue() | |
1251 | + border_size = self.mip_ctrls.border_spin.GetValue() | |
1252 | + image = self.slice_.GetSlices(self.orientation, index, | |
1253 | + self.number_slices, inverted, border_size) | |
1072 | 1254 | self.slice_data.actor.SetInput(image) |
1073 | 1255 | for actor in self.actors_by_slice_number.get(self.slice_data.number, []): |
1074 | 1256 | self.slice_data.renderer.RemoveActor(actor) |
1075 | 1257 | for actor in self.actors_by_slice_number.get(index, []): |
1076 | 1258 | self.slice_data.renderer.AddActor(actor) |
1077 | 1259 | |
1078 | - self.slice_data.SetNumber(index) | |
1260 | + if self.slice_._type_projection == const.PROJECTION_NORMAL: | |
1261 | + self.slice_data.SetNumber(index) | |
1262 | + else: | |
1263 | + max_slices = self.slice_.GetMaxSliceNumber(self.orientation) | |
1264 | + end = min(max_slices, index + self.number_slices - 1) | |
1265 | + self.slice_data.SetNumber(index, end) | |
1079 | 1266 | self.__update_display_extent(image) |
1080 | 1267 | self.cross.SetModelBounds(self.slice_data.actor.GetBounds()) |
1081 | 1268 | |
... | ... | @@ -1111,7 +1298,7 @@ class Viewer(wx.Panel): |
1111 | 1298 | coord[index] = extent_min[index] |
1112 | 1299 | return coord |
1113 | 1300 | |
1114 | - def ReloadActualSlice(self, pubsub_evt): | |
1301 | + def ReloadActualSlice(self, pubsub_evt=None): | |
1115 | 1302 | pos = self.scroll.GetThumbPosition() |
1116 | 1303 | self.set_slice_number(pos) |
1117 | 1304 | self.interactor.Render() | ... | ... |
invesalius/gui/data_notebook.py
... | ... | @@ -365,6 +365,7 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
365 | 365 | 'Change mask colour in notebook') |
366 | 366 | |
367 | 367 | Publisher.subscribe(self.OnChangeCurrentMask, 'Change mask selected') |
368 | + Publisher.subscribe(self.__hide_current_mask, 'Hide current mask') | |
368 | 369 | Publisher.subscribe(self.OnCloseProject, 'Close project data') |
369 | 370 | |
370 | 371 | def OnKeyEvent(self, event): |
... | ... | @@ -432,6 +433,10 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
432 | 433 | if key != mask_index: |
433 | 434 | self.SetItemImage(key, 0) |
434 | 435 | |
436 | + def __hide_current_mask(self, pubsub_evt): | |
437 | + print self.mask_list_index.keys() | |
438 | + self.SetItemImage(self.current_index, 0) | |
439 | + | |
435 | 440 | def __init_columns(self): |
436 | 441 | |
437 | 442 | self.InsertColumn(0, "", wx.LIST_FORMAT_CENTER) | ... | ... |
invesalius/gui/widgets/slice_menu.py
... | ... | @@ -20,6 +20,7 @@ |
20 | 20 | # detalhes. |
21 | 21 | #-------------------------------------------------------------------------- |
22 | 22 | import sys |
23 | +from collections import OrderedDict | |
23 | 24 | |
24 | 25 | import wx |
25 | 26 | from wx.lib.pubsub import pub as Publisher |
... | ... | @@ -30,6 +31,15 @@ import presets |
30 | 31 | |
31 | 32 | from gui.dialogs import ClutImagedataDialog |
32 | 33 | |
34 | +PROJECTIONS_ID = OrderedDict(((_('Normal'), const.PROJECTION_NORMAL), | |
35 | + (_('MaxIP'), const.PROJECTION_MaxIP), | |
36 | + (_('MinIP'), const.PROJECTION_MinIP), | |
37 | + (_('MeanIP'), const.PROJECTION_MeanIP), | |
38 | + (_('MIDA'), const.PROJECTION_MIDA), | |
39 | + (_('Contour MaxIP'), const.PROJECTION_CONTOUR_MIP), | |
40 | + (_('Contour MIDA'), const.PROJECTION_CONTOUR_MIDA),) ) | |
41 | + | |
42 | + | |
33 | 43 | class SliceMenu(wx.Menu): |
34 | 44 | def __init__(self): |
35 | 45 | wx.Menu.__init__(self) |
... | ... | @@ -111,6 +121,17 @@ class SliceMenu(wx.Menu): |
111 | 121 | submenu_pseudo_colours.AppendItem(color_item) |
112 | 122 | self.ID_TO_TOOL_ITEM[new_id] = color_item |
113 | 123 | self.pseudo_color_items[new_id] = color_item |
124 | + | |
125 | + # --------------- Sub menu of the projection type --------------------- | |
126 | + self.projection_items = {} | |
127 | + submenu_projection = wx.Menu() | |
128 | + for name in PROJECTIONS_ID: | |
129 | + new_id = wx.NewId() | |
130 | + projection_item = wx.MenuItem(submenu_projection, new_id, name, | |
131 | + kind=wx.ITEM_RADIO) | |
132 | + submenu_projection.AppendItem(projection_item) | |
133 | + self.ID_TO_TOOL_ITEM[new_id] = projection_item | |
134 | + self.projection_items[PROJECTIONS_ID[name]] = projection_item | |
114 | 135 | |
115 | 136 | flag_tiling = False |
116 | 137 | #------------ Sub menu of the image tiling --------------- |
... | ... | @@ -130,6 +151,7 @@ class SliceMenu(wx.Menu): |
130 | 151 | # Add sub itens in the menu |
131 | 152 | self.AppendMenu(-1, _("Window width and level"), submenu_wl) |
132 | 153 | self.AppendMenu(-1, _("Pseudo color"), submenu_pseudo_colours) |
154 | + self.AppendMenu(-1, _("Projection type"), submenu_projection) | |
133 | 155 | ###self.AppendMenu(-1, _("Image Tiling"), submenu_image_tiling) |
134 | 156 | |
135 | 157 | # It doesn't work in Linux |
... | ... | @@ -139,6 +161,7 @@ class SliceMenu(wx.Menu): |
139 | 161 | submenu_wl.Bind(wx.EVT_MENU, self.OnPopup) |
140 | 162 | submenu_pseudo_colours.Bind(wx.EVT_MENU, self.OnPopup) |
141 | 163 | submenu_image_tiling.Bind(wx.EVT_MENU, self.OnPopup) |
164 | + submenu_projection.Bind(wx.EVT_MENU, self.OnPopup) | |
142 | 165 | |
143 | 166 | self.__bind_events() |
144 | 167 | |
... | ... | @@ -146,6 +169,8 @@ class SliceMenu(wx.Menu): |
146 | 169 | Publisher.subscribe(self.CheckWindowLevelOther, 'Check window and level other') |
147 | 170 | Publisher.subscribe(self.FirstItemSelect, 'Select first item from slice menu') |
148 | 171 | Publisher.subscribe(self._close, 'Close project data') |
172 | + | |
173 | + Publisher.subscribe(self._check_projection_menu, 'Check projection menu') | |
149 | 174 | |
150 | 175 | def FirstItemSelect(self, pusub_evt): |
151 | 176 | item = self.ID_TO_TOOL_ITEM[self.id_wl_first] |
... | ... | @@ -165,10 +190,16 @@ class SliceMenu(wx.Menu): |
165 | 190 | item = self.ID_TO_TOOL_ITEM[self.other_wl_id] |
166 | 191 | item.Check() |
167 | 192 | |
193 | + def _check_projection_menu(self, pubsub_evt): | |
194 | + p_id = pubsub_evt.data | |
195 | + item = self.projection_items[p_id] | |
196 | + item.Check() | |
197 | + | |
168 | 198 | def OnPopup(self, evt): |
169 | 199 | id = evt.GetId() |
170 | 200 | item = self.ID_TO_TOOL_ITEM[evt.GetId()] |
171 | 201 | key = item.GetLabel() |
202 | + print 'Key', key | |
172 | 203 | if(key in const.WINDOW_LEVEL.keys()): |
173 | 204 | window, level = const.WINDOW_LEVEL[key] |
174 | 205 | Publisher.sendMessage('Bright and contrast adjustment image', |
... | ... | @@ -216,6 +247,12 @@ class SliceMenu(wx.Menu): |
216 | 247 | Publisher.sendMessage('Set slice viewer layout', values) |
217 | 248 | Publisher.sendMessage('Update slice viewer') |
218 | 249 | |
250 | + elif key in PROJECTIONS_ID: | |
251 | + print 'Key', key | |
252 | + pid = PROJECTIONS_ID[key] | |
253 | + Publisher.sendMessage('Set projection type', pid) | |
254 | + Publisher.sendMessage('Reload actual slice') | |
255 | + | |
219 | 256 | elif key == _('Custom'): |
220 | 257 | if self.cdialog is None: |
221 | 258 | slc = sl.Slice() | ... | ... |
... | ... | @@ -0,0 +1,32 @@ |
1 | +from distutils.core import setup | |
2 | +from distutils.extension import Extension | |
3 | +from Cython.Distutils import build_ext | |
4 | + | |
5 | +import sys | |
6 | + | |
7 | +import numpy | |
8 | + | |
9 | +if sys.platform == 'linux2': | |
10 | + setup( | |
11 | + cmdclass = {'build_ext': build_ext}, | |
12 | + ext_modules = [ Extension("invesalius.data.mips", ["invesalius/data/mips.pyx"], | |
13 | + include_dirs = [numpy.get_include()], | |
14 | + extra_compile_args=['-fopenmp'], | |
15 | + extra_link_args=['-fopenmp'],)] | |
16 | + ) | |
17 | + | |
18 | +elif sys.platform == 'win32': | |
19 | + setup( | |
20 | + cmdclass = {'build_ext': build_ext}, | |
21 | + ext_modules = [ Extension("invesalius.data.mips", ["invesalius/data/mips.pyx"], | |
22 | + include_dirs = [numpy.get_include()], | |
23 | + extra_compile_args=['/openmp'], | |
24 | + )] | |
25 | + ) | |
26 | + | |
27 | +else: | |
28 | + setup( | |
29 | + cmdclass = {'build_ext': build_ext}, | |
30 | + ext_modules = [ Extension("invesalius.data.mips", ["invesalius/data/mips.pyx"], | |
31 | + include_dirs = [numpy.get_include()],)] | |
32 | + ) | ... | ... |