Commit c24db7d3ff62e2dc843811a4239fc6cf05fc0bb9
Exists in
master
and in
3 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,7 +292,8 @@ WINDOW_LEVEL = {_("Abdomen"):(350,50), | ||
292 | _("Pelvis"): (450,50), | 292 | _("Pelvis"): (450,50), |
293 | _("Sinus"):(4000, 400), | 293 | _("Sinus"):(4000, 400), |
294 | _("Vasculature - Hard"):(240,80), | 294 | _("Vasculature - Hard"):(240,80), |
295 | - _("Vasculature - Soft"):(650,160)} | 295 | + _("Vasculature - Soft"):(650,160), |
296 | + _("Contour"): (255, 127)} | ||
296 | 297 | ||
297 | REDUCE_IMAGEDATA_QUALITY = 0 | 298 | REDUCE_IMAGEDATA_QUALITY = 0 |
298 | 299 | ||
@@ -542,3 +543,18 @@ DICOM_ENCODING_TO_PYTHON = { | @@ -542,3 +543,18 @@ DICOM_ENCODING_TO_PYTHON = { | ||
542 | 'ISO_IR 138': 'iso_ir_138', | 543 | 'ISO_IR 138': 'iso_ir_138', |
543 | 'ISO_IR 144': 'iso_ir_144', | 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 @@ | @@ -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,6 +33,7 @@ import utils | ||
33 | 33 | ||
34 | from mask import Mask | 34 | from mask import Mask |
35 | from project import Project | 35 | from project import Project |
36 | +from data import mips | ||
36 | 37 | ||
37 | OTHER=0 | 38 | OTHER=0 |
38 | PLIST=1 | 39 | PLIST=1 |
@@ -83,6 +84,10 @@ class Slice(object): | @@ -83,6 +84,10 @@ class Slice(object): | ||
83 | self.blend_filter = None | 84 | self.blend_filter = None |
84 | self.histogram = None | 85 | self.histogram = None |
85 | self._matrix = None | 86 | self._matrix = None |
87 | + | ||
88 | + self._type_projection = const.PROJECTION_NORMAL | ||
89 | + self.n_border = const.PROJECTION_BORDER_SIZE | ||
90 | + | ||
86 | self.spacing = (1.0, 1.0, 1.0) | 91 | self.spacing = (1.0, 1.0, 1.0) |
87 | 92 | ||
88 | self.number_of_colours = 256 | 93 | self.number_of_colours = 256 |
@@ -111,7 +116,7 @@ class Slice(object): | @@ -111,7 +116,7 @@ class Slice(object): | ||
111 | def matrix(self, value): | 116 | def matrix(self, value): |
112 | self._matrix = value | 117 | self._matrix = value |
113 | i, e = value.min(), value.max() | 118 | i, e = value.min(), value.max() |
114 | - r = e - i | 119 | + r = int(e) - int(i) |
115 | self.histogram = numpy.histogram(self._matrix, r, (i, e))[0] | 120 | self.histogram = numpy.histogram(self._matrix, r, (i, e))[0] |
116 | 121 | ||
117 | def __bind_events(self): | 122 | def __bind_events(self): |
@@ -133,6 +138,7 @@ class Slice(object): | @@ -133,6 +138,7 @@ class Slice(object): | ||
133 | 'Change mask colour') | 138 | 'Change mask colour') |
134 | Publisher.subscribe(self.__set_mask_name, 'Change mask name') | 139 | Publisher.subscribe(self.__set_mask_name, 'Change mask name') |
135 | Publisher.subscribe(self.__show_mask, 'Show mask') | 140 | Publisher.subscribe(self.__show_mask, 'Show mask') |
141 | + Publisher.subscribe(self.__hide_current_mask, 'Hide current mask') | ||
136 | 142 | ||
137 | Publisher.subscribe(self.__set_current_mask_threshold_limits, | 143 | Publisher.subscribe(self.__set_current_mask_threshold_limits, |
138 | 'Update threshold limits') | 144 | 'Update threshold limits') |
@@ -148,6 +154,7 @@ class Slice(object): | @@ -148,6 +154,7 @@ class Slice(object): | ||
148 | 154 | ||
149 | Publisher.subscribe(self.UpdateColourTableBackgroundWidget,\ | 155 | Publisher.subscribe(self.UpdateColourTableBackgroundWidget,\ |
150 | 'Change colour table from background image from widget') | 156 | 'Change colour table from background image from widget') |
157 | + Publisher.subscribe(self._set_projection_type, 'Set projection type') | ||
151 | 158 | ||
152 | Publisher.subscribe(self.InputImageWidget, 'Input Image in the widget') | 159 | Publisher.subscribe(self.InputImageWidget, 'Input Image in the widget') |
153 | 160 | ||
@@ -346,6 +353,16 @@ class Slice(object): | @@ -346,6 +353,16 @@ class Slice(object): | ||
346 | if not value: | 353 | if not value: |
347 | Publisher.sendMessage('Select mask name in combo', -1) | 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 | def edit_mask_pixel(self, operation, index, position, radius, orientation): | 366 | def edit_mask_pixel(self, operation, index, position, radius, orientation): |
350 | mask = self.buffer_slices[orientation].mask | 367 | mask = self.buffer_slices[orientation].mask |
351 | image = self.buffer_slices[orientation].image | 368 | image = self.buffer_slices[orientation].image |
@@ -421,12 +438,16 @@ class Slice(object): | @@ -421,12 +438,16 @@ class Slice(object): | ||
421 | self.buffer_slices[orientation].discard_vtk_mask() | 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 | if self.buffer_slices[orientation].vtk_image: | 445 | if self.buffer_slices[orientation].vtk_image: |
427 | image = self.buffer_slices[orientation].vtk_image | 446 | image = self.buffer_slices[orientation].vtk_image |
428 | else: | 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 | image = converters.to_vtk(n_image, self.spacing, slice_number, orientation) | 451 | image = converters.to_vtk(n_image, self.spacing, slice_number, orientation) |
431 | ww_wl_image = self.do_ww_wl(image) | 452 | ww_wl_image = self.do_ww_wl(image) |
432 | image = self.do_colour_image(ww_wl_image) | 453 | image = self.do_colour_image(ww_wl_image) |
@@ -446,7 +467,8 @@ class Slice(object): | @@ -446,7 +467,8 @@ class Slice(object): | ||
446 | final_image = image | 467 | final_image = image |
447 | self.buffer_slices[orientation].vtk_image = image | 468 | self.buffer_slices[orientation].vtk_image = image |
448 | else: | 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 | image = converters.to_vtk(n_image, self.spacing, slice_number, orientation) | 472 | image = converters.to_vtk(n_image, self.spacing, slice_number, orientation) |
451 | ww_wl_image = self.do_ww_wl(image) | 473 | ww_wl_image = self.do_ww_wl(image) |
452 | image = self.do_colour_image(ww_wl_image) | 474 | image = self.do_colour_image(ww_wl_image) |
@@ -469,17 +491,151 @@ class Slice(object): | @@ -469,17 +491,151 @@ class Slice(object): | ||
469 | 491 | ||
470 | return final_image | 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 | if self.buffer_slices[orientation].index == slice_number \ | 496 | if self.buffer_slices[orientation].index == slice_number \ |
474 | and self.buffer_slices[orientation].image is not None: | 497 | and self.buffer_slices[orientation].image is not None: |
475 | n_image = self.buffer_slices[orientation].image | 498 | n_image = self.buffer_slices[orientation].image |
476 | else: | 499 | else: |
500 | + | ||
477 | if orientation == 'AXIAL': | 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 | elif orientation == 'CORONAL': | 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 | elif orientation == 'SAGITAL': | 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 | return n_image | 639 | return n_image |
484 | 640 | ||
485 | def get_mask_slice(self, orientation, slice_number): | 641 | def get_mask_slice(self, orientation, slice_number): |
@@ -672,6 +828,26 @@ class Slice(object): | @@ -672,6 +828,26 @@ class Slice(object): | ||
672 | def GetOutput(self): | 828 | def GetOutput(self): |
673 | return self.blend_filter.GetOutput() | 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 | def SetInput(self, imagedata, mask_dict): | 851 | def SetInput(self, imagedata, mask_dict): |
676 | print "SETINPUT!" | 852 | print "SETINPUT!" |
677 | self.imagedata = imagedata | 853 | self.imagedata = imagedata |
@@ -731,7 +907,14 @@ class Slice(object): | @@ -731,7 +907,14 @@ class Slice(object): | ||
731 | self.window_level = level | 907 | self.window_level = level |
732 | 908 | ||
733 | for buffer_ in self.buffer_slices.values(): | 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 | Publisher.sendMessage('Reload actual slice') | 919 | Publisher.sendMessage('Reload actual slice') |
737 | 920 | ||
@@ -758,7 +941,14 @@ class Slice(object): | @@ -758,7 +941,14 @@ class Slice(object): | ||
758 | self.nodes = pubsub_evt.data | 941 | self.nodes = pubsub_evt.data |
759 | self.from_= WIDGET | 942 | self.from_= WIDGET |
760 | for buffer_ in self.buffer_slices.values(): | 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 | knodes = sorted(self.nodes) | 953 | knodes = sorted(self.nodes) |
764 | p0 = knodes[0].value | 954 | p0 = knodes[0].value |
invesalius/data/slice_data.py
@@ -136,9 +136,13 @@ class SliceData(object): | @@ -136,9 +136,13 @@ class SliceData(object): | ||
136 | self.overlay_renderer.AddActor(cursor.actor) | 136 | self.overlay_renderer.AddActor(cursor.actor) |
137 | self.cursor = cursor | 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 | self.text.SetPosition(const.TEXT_POS_LEFT_DOWN_ZERO) | 146 | self.text.SetPosition(const.TEXT_POS_LEFT_DOWN_ZERO) |
143 | 147 | ||
144 | def SetOrientation(self, orientation): | 148 | def SetOrientation(self, orientation): |
invesalius/data/viewer_slice.py
@@ -32,6 +32,11 @@ import styles | @@ -32,6 +32,11 @@ import styles | ||
32 | import wx | 32 | import wx |
33 | from wx.lib.pubsub import pub as Publisher | 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 | import constants as const | 40 | import constants as const |
36 | import cursor_actors as ca | 41 | import cursor_actors as ca |
37 | import data.slice_ as sl | 42 | import data.slice_ as sl |
@@ -51,6 +56,93 @@ ORIENTATIONS = { | @@ -51,6 +56,93 @@ ORIENTATIONS = { | ||
51 | "SAGITAL": const.SAGITAL, | 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 | class Viewer(wx.Panel): | 146 | class Viewer(wx.Panel): |
55 | 147 | ||
56 | def __init__(self, prnt, orientation='AXIAL'): | 148 | def __init__(self, prnt, orientation='AXIAL'): |
@@ -63,6 +155,9 @@ class Viewer(wx.Panel): | @@ -63,6 +155,9 @@ class Viewer(wx.Panel): | ||
63 | #self.modes = []#['DEFAULT'] | 155 | #self.modes = []#['DEFAULT'] |
64 | self.left_pressed = 0 | 156 | self.left_pressed = 0 |
65 | self.right_pressed = 0 | 157 | self.right_pressed = 0 |
158 | + | ||
159 | + self._number_slices = const.PROJECTION_MIP_SIZE | ||
160 | + self._mip_inverted = False | ||
66 | 161 | ||
67 | self.spined_image = False #Use to control to spin | 162 | self.spined_image = False #Use to control to spin |
68 | self.paned_image = False | 163 | self.paned_image = False |
@@ -83,11 +178,11 @@ class Viewer(wx.Panel): | @@ -83,11 +178,11 @@ class Viewer(wx.Panel): | ||
83 | self.actors_by_slice_number = {} | 178 | self.actors_by_slice_number = {} |
84 | self.renderers_by_slice_number = {} | 179 | self.renderers_by_slice_number = {} |
85 | 180 | ||
86 | - self.__init_gui() | ||
87 | - | ||
88 | self.orientation = orientation | 181 | self.orientation = orientation |
89 | self.slice_number = 0 | 182 | self.slice_number = 0 |
90 | 183 | ||
184 | + self.__init_gui() | ||
185 | + | ||
91 | self._brush_cursor_op = const.DEFAULT_BRUSH_OP | 186 | self._brush_cursor_op = const.DEFAULT_BRUSH_OP |
92 | self._brush_cursor_size = const.BRUSH_SIZE | 187 | self._brush_cursor_size = const.BRUSH_SIZE |
93 | self._brush_cursor_colour = const.BRUSH_COLOUR | 188 | self._brush_cursor_colour = const.BRUSH_COLOUR |
@@ -111,12 +206,17 @@ class Viewer(wx.Panel): | @@ -111,12 +206,17 @@ class Viewer(wx.Panel): | ||
111 | 206 | ||
112 | scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) | 207 | scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) |
113 | self.scroll = scroll | 208 | self.scroll = scroll |
209 | + | ||
210 | + self.mip_ctrls = ContourMIPConfig(self, self.orientation) | ||
211 | + self.mip_ctrls.Hide() | ||
212 | + | ||
114 | sizer = wx.BoxSizer(wx.HORIZONTAL) | 213 | sizer = wx.BoxSizer(wx.HORIZONTAL) |
115 | sizer.Add(self.interactor, 1, wx.EXPAND|wx.GROW) | 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 | background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) | 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 | self.SetSizer(background_sizer) | 220 | self.SetSizer(background_sizer) |
121 | background_sizer.Fit(self) | 221 | background_sizer.Fit(self) |
122 | 222 | ||
@@ -644,6 +744,13 @@ class Viewer(wx.Panel): | @@ -644,6 +744,13 @@ class Viewer(wx.Panel): | ||
644 | Publisher.subscribe(self.ReloadActualSlice, 'Reload actual slice') | 744 | Publisher.subscribe(self.ReloadActualSlice, 'Reload actual slice') |
645 | Publisher.subscribe(self.OnUpdateScroll, 'Update scroll') | 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 | def SetDefaultCursor(self, pusub_evt): | 754 | def SetDefaultCursor(self, pusub_evt): |
648 | self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) | 755 | self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) |
649 | 756 | ||
@@ -982,6 +1089,17 @@ class Viewer(wx.Panel): | @@ -982,6 +1089,17 @@ class Viewer(wx.Panel): | ||
982 | max_slice_number) | 1089 | max_slice_number) |
983 | self.set_scroll_position(0) | 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 | def set_scroll_position(self, position): | 1103 | def set_scroll_position(self, position): |
986 | self.scroll.SetThumbPosition(position) | 1104 | self.scroll.SetThumbPosition(position) |
987 | self.OnScrollBar() | 1105 | self.OnScrollBar() |
@@ -1016,10 +1134,19 @@ class Viewer(wx.Panel): | @@ -1016,10 +1134,19 @@ class Viewer(wx.Panel): | ||
1016 | 1134 | ||
1017 | def OnKeyDown(self, evt=None, obj=None): | 1135 | def OnKeyDown(self, evt=None, obj=None): |
1018 | pos = self.scroll.GetThumbPosition() | 1136 | pos = self.scroll.GetThumbPosition() |
1137 | + skip = True | ||
1019 | 1138 | ||
1020 | min = 0 | 1139 | min = 0 |
1021 | max = self.slice_.GetMaxSliceNumber(self.orientation) | 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 | if self._flush_buffer: | 1150 | if self._flush_buffer: |
1024 | self.slice_.apply_slice_buffer_to_mask(self.orientation) | 1151 | self.slice_.apply_slice_buffer_to_mask(self.orientation) |
1025 | 1152 | ||
@@ -1030,11 +1157,31 @@ class Viewer(wx.Panel): | @@ -1030,11 +1157,31 @@ class Viewer(wx.Panel): | ||
1030 | elif (evt.GetKeyCode() == wx.WXK_DOWN and pos < max): | 1157 | elif (evt.GetKeyCode() == wx.WXK_DOWN and pos < max): |
1031 | self.OnScrollBackward() | 1158 | self.OnScrollBackward() |
1032 | self.OnScrollBar() | 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 | self.UpdateSlice3D(pos) | 1181 | self.UpdateSlice3D(pos) |
1035 | self.interactor.Render() | 1182 | self.interactor.Render() |
1036 | 1183 | ||
1037 | - if evt: | 1184 | + if evt and skip: |
1038 | evt.Skip() | 1185 | evt.Skip() |
1039 | 1186 | ||
1040 | def OnScrollForward(self, evt=None, obj=None): | 1187 | def OnScrollForward(self, evt=None, obj=None): |
@@ -1067,15 +1214,55 @@ class Viewer(wx.Panel): | @@ -1067,15 +1214,55 @@ class Viewer(wx.Panel): | ||
1067 | self.slice_data.SetSize((w, h)) | 1214 | self.slice_data.SetSize((w, h)) |
1068 | evt.Skip() | 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 | def set_slice_number(self, index): | 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 | self.slice_data.actor.SetInput(image) | 1254 | self.slice_data.actor.SetInput(image) |
1073 | for actor in self.actors_by_slice_number.get(self.slice_data.number, []): | 1255 | for actor in self.actors_by_slice_number.get(self.slice_data.number, []): |
1074 | self.slice_data.renderer.RemoveActor(actor) | 1256 | self.slice_data.renderer.RemoveActor(actor) |
1075 | for actor in self.actors_by_slice_number.get(index, []): | 1257 | for actor in self.actors_by_slice_number.get(index, []): |
1076 | self.slice_data.renderer.AddActor(actor) | 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 | self.__update_display_extent(image) | 1266 | self.__update_display_extent(image) |
1080 | self.cross.SetModelBounds(self.slice_data.actor.GetBounds()) | 1267 | self.cross.SetModelBounds(self.slice_data.actor.GetBounds()) |
1081 | 1268 | ||
@@ -1111,7 +1298,7 @@ class Viewer(wx.Panel): | @@ -1111,7 +1298,7 @@ class Viewer(wx.Panel): | ||
1111 | coord[index] = extent_min[index] | 1298 | coord[index] = extent_min[index] |
1112 | return coord | 1299 | return coord |
1113 | 1300 | ||
1114 | - def ReloadActualSlice(self, pubsub_evt): | 1301 | + def ReloadActualSlice(self, pubsub_evt=None): |
1115 | pos = self.scroll.GetThumbPosition() | 1302 | pos = self.scroll.GetThumbPosition() |
1116 | self.set_slice_number(pos) | 1303 | self.set_slice_number(pos) |
1117 | self.interactor.Render() | 1304 | self.interactor.Render() |
invesalius/gui/data_notebook.py
@@ -365,6 +365,7 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | @@ -365,6 +365,7 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | ||
365 | 'Change mask colour in notebook') | 365 | 'Change mask colour in notebook') |
366 | 366 | ||
367 | Publisher.subscribe(self.OnChangeCurrentMask, 'Change mask selected') | 367 | Publisher.subscribe(self.OnChangeCurrentMask, 'Change mask selected') |
368 | + Publisher.subscribe(self.__hide_current_mask, 'Hide current mask') | ||
368 | Publisher.subscribe(self.OnCloseProject, 'Close project data') | 369 | Publisher.subscribe(self.OnCloseProject, 'Close project data') |
369 | 370 | ||
370 | def OnKeyEvent(self, event): | 371 | def OnKeyEvent(self, event): |
@@ -432,6 +433,10 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | @@ -432,6 +433,10 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | ||
432 | if key != mask_index: | 433 | if key != mask_index: |
433 | self.SetItemImage(key, 0) | 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 | def __init_columns(self): | 440 | def __init_columns(self): |
436 | 441 | ||
437 | self.InsertColumn(0, "", wx.LIST_FORMAT_CENTER) | 442 | self.InsertColumn(0, "", wx.LIST_FORMAT_CENTER) |
invesalius/gui/widgets/slice_menu.py
@@ -20,6 +20,7 @@ | @@ -20,6 +20,7 @@ | ||
20 | # detalhes. | 20 | # detalhes. |
21 | #-------------------------------------------------------------------------- | 21 | #-------------------------------------------------------------------------- |
22 | import sys | 22 | import sys |
23 | +from collections import OrderedDict | ||
23 | 24 | ||
24 | import wx | 25 | import wx |
25 | from wx.lib.pubsub import pub as Publisher | 26 | from wx.lib.pubsub import pub as Publisher |
@@ -30,6 +31,15 @@ import presets | @@ -30,6 +31,15 @@ import presets | ||
30 | 31 | ||
31 | from gui.dialogs import ClutImagedataDialog | 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 | class SliceMenu(wx.Menu): | 43 | class SliceMenu(wx.Menu): |
34 | def __init__(self): | 44 | def __init__(self): |
35 | wx.Menu.__init__(self) | 45 | wx.Menu.__init__(self) |
@@ -111,6 +121,17 @@ class SliceMenu(wx.Menu): | @@ -111,6 +121,17 @@ class SliceMenu(wx.Menu): | ||
111 | submenu_pseudo_colours.AppendItem(color_item) | 121 | submenu_pseudo_colours.AppendItem(color_item) |
112 | self.ID_TO_TOOL_ITEM[new_id] = color_item | 122 | self.ID_TO_TOOL_ITEM[new_id] = color_item |
113 | self.pseudo_color_items[new_id] = color_item | 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 | flag_tiling = False | 136 | flag_tiling = False |
116 | #------------ Sub menu of the image tiling --------------- | 137 | #------------ Sub menu of the image tiling --------------- |
@@ -130,6 +151,7 @@ class SliceMenu(wx.Menu): | @@ -130,6 +151,7 @@ class SliceMenu(wx.Menu): | ||
130 | # Add sub itens in the menu | 151 | # Add sub itens in the menu |
131 | self.AppendMenu(-1, _("Window width and level"), submenu_wl) | 152 | self.AppendMenu(-1, _("Window width and level"), submenu_wl) |
132 | self.AppendMenu(-1, _("Pseudo color"), submenu_pseudo_colours) | 153 | self.AppendMenu(-1, _("Pseudo color"), submenu_pseudo_colours) |
154 | + self.AppendMenu(-1, _("Projection type"), submenu_projection) | ||
133 | ###self.AppendMenu(-1, _("Image Tiling"), submenu_image_tiling) | 155 | ###self.AppendMenu(-1, _("Image Tiling"), submenu_image_tiling) |
134 | 156 | ||
135 | # It doesn't work in Linux | 157 | # It doesn't work in Linux |
@@ -139,6 +161,7 @@ class SliceMenu(wx.Menu): | @@ -139,6 +161,7 @@ class SliceMenu(wx.Menu): | ||
139 | submenu_wl.Bind(wx.EVT_MENU, self.OnPopup) | 161 | submenu_wl.Bind(wx.EVT_MENU, self.OnPopup) |
140 | submenu_pseudo_colours.Bind(wx.EVT_MENU, self.OnPopup) | 162 | submenu_pseudo_colours.Bind(wx.EVT_MENU, self.OnPopup) |
141 | submenu_image_tiling.Bind(wx.EVT_MENU, self.OnPopup) | 163 | submenu_image_tiling.Bind(wx.EVT_MENU, self.OnPopup) |
164 | + submenu_projection.Bind(wx.EVT_MENU, self.OnPopup) | ||
142 | 165 | ||
143 | self.__bind_events() | 166 | self.__bind_events() |
144 | 167 | ||
@@ -146,6 +169,8 @@ class SliceMenu(wx.Menu): | @@ -146,6 +169,8 @@ class SliceMenu(wx.Menu): | ||
146 | Publisher.subscribe(self.CheckWindowLevelOther, 'Check window and level other') | 169 | Publisher.subscribe(self.CheckWindowLevelOther, 'Check window and level other') |
147 | Publisher.subscribe(self.FirstItemSelect, 'Select first item from slice menu') | 170 | Publisher.subscribe(self.FirstItemSelect, 'Select first item from slice menu') |
148 | Publisher.subscribe(self._close, 'Close project data') | 171 | Publisher.subscribe(self._close, 'Close project data') |
172 | + | ||
173 | + Publisher.subscribe(self._check_projection_menu, 'Check projection menu') | ||
149 | 174 | ||
150 | def FirstItemSelect(self, pusub_evt): | 175 | def FirstItemSelect(self, pusub_evt): |
151 | item = self.ID_TO_TOOL_ITEM[self.id_wl_first] | 176 | item = self.ID_TO_TOOL_ITEM[self.id_wl_first] |
@@ -165,10 +190,16 @@ class SliceMenu(wx.Menu): | @@ -165,10 +190,16 @@ class SliceMenu(wx.Menu): | ||
165 | item = self.ID_TO_TOOL_ITEM[self.other_wl_id] | 190 | item = self.ID_TO_TOOL_ITEM[self.other_wl_id] |
166 | item.Check() | 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 | def OnPopup(self, evt): | 198 | def OnPopup(self, evt): |
169 | id = evt.GetId() | 199 | id = evt.GetId() |
170 | item = self.ID_TO_TOOL_ITEM[evt.GetId()] | 200 | item = self.ID_TO_TOOL_ITEM[evt.GetId()] |
171 | key = item.GetLabel() | 201 | key = item.GetLabel() |
202 | + print 'Key', key | ||
172 | if(key in const.WINDOW_LEVEL.keys()): | 203 | if(key in const.WINDOW_LEVEL.keys()): |
173 | window, level = const.WINDOW_LEVEL[key] | 204 | window, level = const.WINDOW_LEVEL[key] |
174 | Publisher.sendMessage('Bright and contrast adjustment image', | 205 | Publisher.sendMessage('Bright and contrast adjustment image', |
@@ -216,6 +247,12 @@ class SliceMenu(wx.Menu): | @@ -216,6 +247,12 @@ class SliceMenu(wx.Menu): | ||
216 | Publisher.sendMessage('Set slice viewer layout', values) | 247 | Publisher.sendMessage('Set slice viewer layout', values) |
217 | Publisher.sendMessage('Update slice viewer') | 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 | elif key == _('Custom'): | 256 | elif key == _('Custom'): |
220 | if self.cdialog is None: | 257 | if self.cdialog is None: |
221 | slc = sl.Slice() | 258 | slc = sl.Slice() |
@@ -0,0 +1,32 @@ | @@ -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 | + ) |