Commit c24db7d3ff62e2dc843811a4239fc6cf05fc0bb9

Authored by Thiago Franco de Moraes
2 parents d0d68211 fcc3fa2e

Merge pull request #19 from tfmoraes/MIPs

MIPs
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
... ...
invesalius/data/mips.pyx 0 → 100644
... ... @@ -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()
... ...
setup.py 0 → 100644
... ... @@ -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 + )
... ...