Commit 00952d8a152e7758dccbbc8d1cc2d5cde54fefeb

Authored by tfmoraes
1 parent 390a84c8

Generating surface again, from binary (if the mask was edited) or the original.

When the user tries to generates a surface from a edited mask, the user
is aske about the method to generate the surface: Binary, Context aware
smoothing or invesalius3.b2

Squashed commit of the following:

commit 480c88b680a9d79c977d3a8b81b9b25114dad43b
Author: Thiago Franco de Moraes <totonixsame@gmail.com>
Date:   Tue Mar 20 10:56:41 2012 -0300

    Not saving the project when a new case is opened

commit 1233e37982783412fd1a092820ddb55f60c7df79
Author: Thiago Franco de Moraes <totonixsame@gmail.com>
Date:   Tue Mar 20 10:51:04 2012 -0300

    Generating surface from edited masks, the user can select to use the context aware smoothing or be a binary

commit e722ea910db2931b866dfa647fcb5cc3a80d5675
Author: Thiago Franco de Moraes <totonixsame@gmail.com>
Date:   Tue Mar 20 10:47:10 2012 -0300

    Added a new dialog related to the generation surface whose mask was edited
invesalius/control.py
... ... @@ -464,7 +464,7 @@ class Controller():
464 464 filename = filename.replace("/", "") #Fix problem case other/Skull_DICOM
465 465  
466 466 dirpath = session.CreateProject(filename)
467   - proj.SavePlistProject(dirpath, filename)
  467 + #proj.SavePlistProject(dirpath, filename)
468 468  
469 469 def OnOpenDicomGroup(self, pubsub_evt):
470 470 group, interval, file_range = pubsub_evt.data
... ...
invesalius/data/mask.py
... ... @@ -42,6 +42,7 @@ class Mask():
42 42 self.edition_threshold_range = [const.THRESHOLD_OUTVALUE, const.THRESHOLD_INVALUE]
43 43 self.is_shown = 1
44 44 self.edited_points = {}
  45 + self.was_edited = False
45 46  
46 47 def SavePlist(self, filename):
47 48 mask = {}
... ...
invesalius/data/slice_.py
... ... @@ -285,6 +285,7 @@ class Slice(object):
285 285 mask = self.buffer_slices[orientation].mask
286 286 image = self.buffer_slices[orientation].image
287 287 thresh_min, thresh_max = self.current_mask.edition_threshold_range
  288 + self.current_mask.was_edited = True
288 289  
289 290 if hasattr(position, '__iter__'):
290 291 py, px = position
... ... @@ -515,7 +516,9 @@ class Slice(object):
515 516 If slice_number is None then all the threshold is calculated for all
516 517 slices, otherwise only to indicated slice.
517 518 """
  519 + self.current_mask.was_edited = False
518 520 thresh_min, thresh_max = threshold_range
  521 + print "Threshold"
519 522  
520 523 if self.current_mask.index == index:
521 524 # TODO: find out a better way to do threshold
... ... @@ -588,23 +591,26 @@ class Slice(object):
588 591 #---------------------------------------------------------------------------
589 592  
590 593 def CreateSurfaceFromIndex(self, pubsub_evt):
591   - mask_index, overwrite_surface = pubsub_evt.data
592   -
  594 + mask_index, overwrite_surface, algorithm, options = pubsub_evt.data
593 595  
594 596 proj = Project()
595 597 mask = proj.mask_dict[mask_index]
596 598  
597 599 # This is very important. Do not use masks' imagedata. It would mess up
598 600 # surface quality event when using contour
599   - colour = mask.colour
600   - threshold = mask.threshold_range
601   - edited_points = mask.edited_points
602   -
603   - self.SetMaskThreshold(mask.index, threshold)
  601 + #self.SetMaskThreshold(mask.index, threshold)
  602 + for n in xrange(1, mask.matrix.shape[0]):
  603 + if mask.matrix[n, 0, 0] == 0:
  604 + m = mask.matrix[n, 1:, 1:]
  605 + mask.matrix[n, 1:, 1:] = self.do_threshold_to_a_slice(self.matrix[n-1], m)
604 606  
605 607 mask.matrix.flush()
606 608  
607   - ps.Publisher().sendMessage('Create surface', (self.matrix, self.matrix_filename, mask, self.spacing))
  609 + ps.Publisher().sendMessage('Create surface', (algorithm, options,
  610 + self.matrix,
  611 + self.matrix_filename,
  612 + mask, self.spacing,
  613 + overwrite_surface))
608 614  
609 615 def GetOutput(self):
610 616 return self.blend_filter.GetOutput()
... ...
invesalius/data/surface.py
... ... @@ -21,8 +21,6 @@ import multiprocessing
21 21 import os
22 22 import plistlib
23 23 import random
24   -import sys
25   -import tempfile
26 24  
27 25 import vtk
28 26 import wx.lib.pubsub as ps
... ... @@ -35,6 +33,7 @@ import session as ses
35 33 import surface_process
36 34 import utils as utl
37 35 import vtk_utils as vu
  36 +import data.ca_smoothing as ca_smoothing
38 37  
39 38 class Surface():
40 39 """
... ... @@ -363,9 +362,11 @@ class SurfaceManager():
363 362 """
364 363 Create surface actor, save into project and send it to viewer.
365 364 """
366   - matrix, filename_img, mask, spacing = pubsub_evt.data
  365 + algorithm, options, matrix, filename_img, mask, spacing, overwrite = pubsub_evt.data
367 366 min_value, max_value = mask.threshold_range
368   - fill_holes = True
  367 + fill_holes = False
  368 +
  369 + mask.matrix.flush()
369 370  
370 371 #if len(surface_data) == 5:
371 372 #imagedata, colour, [min_value, max_value], \
... ... @@ -382,7 +383,7 @@ class SurfaceManager():
382 383  
383 384 mode = 'CONTOUR' # 'GRAYSCALE'
384 385 quality=_('Optimal *')
385   - keep_largest = True
  386 + keep_largest = False
386 387 surface_name = ""
387 388 colour = mask.colour
388 389  
... ... @@ -411,8 +412,6 @@ class SurfaceManager():
411 412  
412 413 language = ses.Session().language
413 414  
414   - overwrite = 0
415   -
416 415 if (prj.Project().original_orientation == const.CORONAL):
417 416 flip_image = False
418 417 else:
... ... @@ -422,7 +421,7 @@ class SurfaceManager():
422 421  
423 422 pipe_in, pipe_out = multiprocessing.Pipe()
424 423 o_piece = 1
425   - piece_size = 20
  424 + piece_size = 2000
426 425  
427 426 n_pieces = int(round(matrix.shape[0] / piece_size + 0.5, 0))
428 427  
... ... @@ -432,10 +431,18 @@ class SurfaceManager():
432 431 p = []
433 432 for i in xrange(n_processors):
434 433 sp = surface_process.SurfaceProcess(pipe_in, filename_img,
435   - matrix.shape, matrix.dtype, spacing,
436   - mode, min_value, max_value,
437   - decimate_reduction, smooth_relaxation_factor,
438   - smooth_iterations, language, flip_image, q_in, q_out)
  434 + matrix.shape, matrix.dtype,
  435 + mask.temp_file,
  436 + mask.matrix.shape,
  437 + mask.matrix.dtype,
  438 + spacing,
  439 + mode, min_value, max_value,
  440 + decimate_reduction,
  441 + smooth_relaxation_factor,
  442 + smooth_iterations, language,
  443 + flip_image, q_in, q_out,
  444 + mask.was_edited and algorithm != u'InVesalius 3.b2',
  445 + algorithm)
439 446 p.append(sp)
440 447 sp.start()
441 448  
... ... @@ -471,28 +478,65 @@ class SurfaceManager():
471 478 polydata_append.AddInput(reader.GetOutput())
472 479 t -= 1
473 480  
  481 + polydata_append.Update()
474 482 polydata = polydata_append.GetOutput()
475   - clean = vtk.vtkCleanPolyData()
476   - clean.AddObserver("ProgressEvent", lambda obj,evt:
477   - UpdateProgress(obj, _("Generating 3D surface...")))
478   - clean.SetInput(polydata)
479   - clean.PointMergingOn()
480   - clean.Update()
481   - polydata = clean.GetOutput()
482 483  
483   - smoother = vtk.vtkWindowedSincPolyDataFilter()
484   - smoother.AddObserver("ProgressEvent", lambda obj,evt:
485   - UpdateProgress(obj, _("Generating 3D surface...")))
486   - smoother.SetInput(polydata)
487   - smoother.SetNumberOfIterations(smooth_iterations)
488   - smoother.SetFeatureAngle(120)
489   - smoother.BoundarySmoothingOn()
490   - smoother.SetPassBand(0.1)
491   - #smoother.FeatureEdgeSmoothingOn()
492   - #smoother.NonManifoldSmoothingOn()
493   - #smoother.NormalizeCoordinatesOn()
494   - smoother.Update()
495   - polydata = smoother.GetOutput()
  484 + if algorithm == u'Context aware smoothing':
  485 + normals = vtk.vtkPolyDataNormals()
  486 + normals.AddObserver("ProgressEvent", lambda obj,evt:
  487 + UpdateProgress(obj, _("Generating 3D surface...")))
  488 + normals.SetInput(polydata)
  489 + #normals.SetFeatureAngle(80)
  490 + #normals.AutoOrientNormalsOn()
  491 + normals.ComputeCellNormalsOn()
  492 + normals.Update()
  493 + polydata = normals.GetOutput()
  494 +
  495 + clean = vtk.vtkCleanPolyData()
  496 + clean.AddObserver("ProgressEvent", lambda obj,evt:
  497 + UpdateProgress(obj, _("Generating 3D surface...")))
  498 + clean.SetInput(polydata)
  499 + clean.PointMergingOn()
  500 + clean.Update()
  501 + polydata = clean.GetOutput()
  502 +
  503 + polydata.BuildLinks()
  504 + polydata = ca_smoothing.ca_smoothing(polydata, options['angle'],
  505 + options['max distance'],
  506 + options['min weight'],
  507 + options['steps'])
  508 +
  509 + else:
  510 + smoother = vtk.vtkWindowedSincPolyDataFilter()
  511 + smoother.AddObserver("ProgressEvent", lambda obj,evt:
  512 + UpdateProgress(obj, _("Generating 3D surface...")))
  513 + smoother.SetInput(polydata)
  514 + smoother.SetNumberOfIterations(smooth_iterations)
  515 + smoother.SetFeatureAngle(120)
  516 + smoother.SetEdgeAngle(90.0)
  517 + smoother.BoundarySmoothingOn()
  518 + smoother.SetPassBand(0.1)
  519 + #smoother.FeatureEdgeSmoothingOn()
  520 + #smoother.NonManifoldSmoothingOn()
  521 + #smoother.NormalizeCoordinatesOn()
  522 + smoother.Update()
  523 + polydata = smoother.GetOutput()
  524 +
  525 +
  526 + if decimate_reduction:
  527 + print "Decimating", decimate_reduction
  528 + decimation = vtk.vtkQuadricDecimation()
  529 + decimation.SetInput(polydata)
  530 + decimation.SetTargetReduction(decimate_reduction)
  531 + decimation.AddObserver("ProgressEvent", lambda obj,evt:
  532 + UpdateProgress(obj, _("Generating 3D surface...")))
  533 + #decimation.PreserveTopologyOn()
  534 + #decimation.SplittingOff()
  535 + #decimation.BoundaryVertexDeletionOff()
  536 + decimation.Update()
  537 + polydata = decimation.GetOutput()
  538 +
  539 + to_measure = polydata
496 540  
497 541 if keep_largest:
498 542 conn = vtk.vtkPolyDataConnectivityFilter()
... ... @@ -500,17 +544,19 @@ class SurfaceManager():
500 544 conn.SetExtractionModeToLargestRegion()
501 545 conn.AddObserver("ProgressEvent", lambda obj,evt:
502 546 UpdateProgress(obj, _("Generating 3D surface...")))
  547 + conn.Update()
503 548 polydata = conn.GetOutput()
504 549  
505   - # Filter used to detect and fill holes. Only fill boundary edges holes.
  550 + #Filter used to detect and fill holes. Only fill boundary edges holes.
506 551 #TODO: Hey! This piece of code is the same from
507   - # polydata_utils.FillSurfaceHole, we need to review this.
  552 + #polydata_utils.FillSurfaceHole, we need to review this.
508 553 if fill_holes:
509 554 filled_polydata = vtk.vtkFillHolesFilter()
510 555 filled_polydata.SetInput(polydata)
511 556 filled_polydata.SetHoleSize(300)
512 557 filled_polydata.AddObserver("ProgressEvent", lambda obj,evt:
513 558 UpdateProgress(obj, _("Generating 3D surface...")))
  559 + filled_polydata.Update()
514 560 polydata = filled_polydata.GetOutput()
515 561  
516 562 normals = vtk.vtkPolyDataNormals()
... ... @@ -572,7 +618,7 @@ class SurfaceManager():
572 618  
573 619 # The following lines have to be here, otherwise all volumes disappear
574 620 measured_polydata = vtk.vtkMassProperties()
575   - measured_polydata.SetInput(smoother.GetOutput())
  621 + measured_polydata.SetInput(to_measure)
576 622 volume = measured_polydata.GetVolume()
577 623 surface.volume = volume
578 624 self.last_surface_index = surface.index
... ...
invesalius/data/surface_process.py
... ... @@ -11,9 +11,11 @@ from scipy import ndimage
11 11  
12 12 class SurfaceProcess(multiprocessing.Process):
13 13  
14   - def __init__(self, pipe, filename, shape, dtype, spacing, mode, min_value, max_value,
  14 + def __init__(self, pipe, filename, shape, dtype, mask_filename,
  15 + mask_shape, mask_dtype, spacing, mode, min_value, max_value,
15 16 decimate_reduction, smooth_relaxation_factor,
16   - smooth_iterations, language, flip_image, q_in, q_out):
  17 + smooth_iterations, language, flip_image, q_in, q_out,
  18 + from_binary, algorithm):
17 19  
18 20 multiprocessing.Process.__init__(self)
19 21 self.pipe = pipe
... ... @@ -31,10 +33,26 @@ class SurfaceProcess(multiprocessing.Process):
31 33 self.q_out = q_out
32 34 self.dtype = dtype
33 35 self.shape = shape
  36 + self.from_binary = from_binary
  37 + self.algorithm = algorithm
  38 +
  39 + self.mask_filename = mask_filename
  40 + self.mask_shape = mask_shape
  41 + self.mask_dtype = mask_dtype
34 42  
35 43 def run(self):
36   - self.mask = numpy.memmap(self.filename, mode='r', dtype=self.dtype,
37   - shape=self.shape)
  44 + if self.from_binary:
  45 + self.mask = numpy.memmap(self.mask_filename, mode='r',
  46 + dtype=self.mask_dtype,
  47 + shape=self.mask_shape)
  48 + else:
  49 + self.image = numpy.memmap(self.filename, mode='r', dtype=self.dtype,
  50 + shape=self.shape)
  51 + self.mask = numpy.memmap(self.mask_filename, mode='r',
  52 + dtype=self.mask_dtype,
  53 + shape=self.mask_shape)
  54 +
  55 +
38 56 while 1:
39 57 roi = self.q_in.get()
40 58 if roi is None:
... ... @@ -46,59 +64,95 @@ class SurfaceProcess(multiprocessing.Process):
46 64 self.pipe.send([prog, msg])
47 65  
48 66 def CreateSurface(self, roi):
49   - smoothed = numpy.array(self.mask[roi])
50   - image = converters.to_vtk(smoothed, self.spacing, roi.start,
  67 + if self.from_binary:
  68 + a_mask = numpy.array(self.mask[roi.start + 1: roi.stop + 1,
  69 + 1:, 1:])
  70 + image = converters.to_vtk(a_mask, self.spacing, roi.start,
51 71 "AXIAL")
  72 + else:
  73 + a_image = numpy.array(self.image[roi])
  74 +
  75 + if self.algorithm == u'InVesalius 3.b2':
  76 + a_mask = numpy.array(self.mask[roi.start + 1: roi.stop + 1,
  77 + 1:, 1:])
  78 + a_image[a_mask == 1] = a_image.min() - 1
  79 + a_image[a_mask == 254] = (self.min_value + self.max_value) / 2.0
  80 +
  81 + image = converters.to_vtk(a_image, self.spacing, roi.start,
  82 + "AXIAL")
  83 +
  84 + gauss = vtk.vtkImageGaussianSmooth()
  85 + gauss.SetInput(image)
  86 + gauss.SetRadiusFactor(0.3)
  87 + gauss.Update()
  88 +
  89 + image = gauss.GetOutput()
  90 + else:
  91 + image = converters.to_vtk(a_image, self.spacing, roi.start,
  92 + "AXIAL")
  93 +
52 94 flip = vtk.vtkImageFlip()
53 95 flip.SetInput(image)
54 96 flip.SetFilteredAxis(1)
55 97 flip.FlipAboutOriginOn()
56 98 flip.Update()
  99 +
  100 + #filename = tempfile.mktemp(suffix='_%s.vti' % (self.pid))
  101 + #writer = vtk.vtkXMLImageDataWriter()
  102 + #writer.SetInput(mask_vtk)
  103 + #writer.SetFileName(filename)
  104 + #writer.Write()
  105 +
  106 + #print "Writing piece", roi, "to", filename
  107 +
57 108 # Create vtkPolyData from vtkImageData
58 109 #print "Generating Polydata"
59   - if self.mode == "CONTOUR":
  110 + #if self.mode == "CONTOUR":
60 111 #print "Contour"
61   - contour = vtk.vtkContourFilter()
62   - contour.SetInput(flip.GetOutput())
  112 + contour = vtk.vtkContourFilter()
  113 + contour.SetInput(flip.GetOutput())
  114 + if self.from_binary:
  115 + contour.SetValue(0, 127) # initial threshold
  116 + else:
63 117 contour.SetValue(0, self.min_value) # initial threshold
64 118 contour.SetValue(1, self.max_value) # final threshold
65   - contour.ComputeScalarsOn()
66   - contour.ComputeGradientsOn()
67   - contour.ComputeNormalsOn()
68   - contour.AddObserver("ProgressEvent", lambda obj,evt:
69   - self.SendProgress(obj, _("Generating 3D surface...")))
70   - polydata = contour.GetOutput()
71   - else: #mode == "GRAYSCALE":
72   - mcubes = vtk.vtkMarchingCubes()
73   - mcubes.SetInput(flip.GetOutput())
74   - mcubes.SetValue(0, self.min_value)
75   - mcubes.SetValue(1, self.max_value)
76   - mcubes.ComputeScalarsOff()
77   - mcubes.ComputeGradientsOff()
78   - mcubes.ComputeNormalsOff()
79   - mcubes.AddObserver("ProgressEvent", lambda obj,evt:
80   - self.SendProgress(obj, _("Generating 3D surface...")))
81   - polydata = mcubes.GetOutput()
82   -
83   - triangle = vtk.vtkTriangleFilter()
84   - triangle.SetInput(polydata)
85   - triangle.AddObserver("ProgressEvent", lambda obj,evt:
86   - self.SendProgress(obj, _("Generating 3D surface...")))
87   - triangle.Update()
88   - polydata = triangle.GetOutput()
89   -
90   - if self.decimate_reduction:
91   -
92   - #print "Decimating"
93   - decimation = vtk.vtkDecimatePro()
94   - decimation.SetInput(polydata)
95   - decimation.SetTargetReduction(0.3)
96   - decimation.AddObserver("ProgressEvent", lambda obj,evt:
  119 + contour.ComputeScalarsOn()
  120 + contour.ComputeGradientsOn()
  121 + contour.ComputeNormalsOn()
  122 + contour.AddObserver("ProgressEvent", lambda obj,evt:
97 123 self.SendProgress(obj, _("Generating 3D surface...")))
98   - #decimation.PreserveTopologyOn()
99   - decimation.SplittingOff()
100   - decimation.BoundaryVertexDeletionOff()
101   - polydata = decimation.GetOutput()
  124 + polydata = contour.GetOutput()
  125 + #else: #mode == "GRAYSCALE":
  126 + #mcubes = vtk.vtkMarchingCubes()
  127 + #mcubes.SetInput(flip.GetOutput())
  128 + #mcubes.SetValue(0, self.min_value)
  129 + #mcubes.SetValue(1, self.max_value)
  130 + #mcubes.ComputeScalarsOff()
  131 + #mcubes.ComputeGradientsOff()
  132 + #mcubes.ComputeNormalsOff()
  133 + #mcubes.AddObserver("ProgressEvent", lambda obj,evt:
  134 + #self.SendProgress(obj, _("Generating 3D surface...")))
  135 + #polydata = mcubes.GetOutput()
  136 +
  137 + #triangle = vtk.vtkTriangleFilter()
  138 + #triangle.SetInput(polydata)
  139 + #triangle.AddObserver("ProgressEvent", lambda obj,evt:
  140 + #self.SendProgress(obj, _("Generating 3D surface...")))
  141 + #triangle.Update()
  142 + #polydata = triangle.GetOutput()
  143 +
  144 + #if self.decimate_reduction:
  145 +
  146 + ##print "Decimating"
  147 + #decimation = vtk.vtkDecimatePro()
  148 + #decimation.SetInput(polydata)
  149 + #decimation.SetTargetReduction(0.3)
  150 + #decimation.AddObserver("ProgressEvent", lambda obj,evt:
  151 + #self.SendProgress(obj, _("Generating 3D surface...")))
  152 + ##decimation.PreserveTopologyOn()
  153 + #decimation.SplittingOff()
  154 + #decimation.BoundaryVertexDeletionOff()
  155 + #polydata = decimation.GetOutput()
102 156  
103 157 self.pipe.send(None)
104 158  
... ... @@ -108,4 +162,6 @@ class SurfaceProcess(multiprocessing.Process):
108 162 writer.SetFileName(filename)
109 163 writer.Write()
110 164  
  165 + print "Writing piece", roi, "to", filename
  166 +
111 167 self.q_out.put(filename)
... ...
invesalius/gui/dialogs.py
... ... @@ -24,6 +24,7 @@ import sys
24 24  
25 25 import wx
26 26 from wx.lib import masked
  27 +from wx.lib.agw import floatspin
27 28 from wx.lib.wordwrap import wordwrap
28 29 import wx.lib.pubsub as ps
29 30  
... ... @@ -852,3 +853,114 @@ def ExportPicture(type_=&quot;&quot;):
852 853 return ()
853 854  
854 855  
  856 +class CAOptions(wx.Panel):
  857 + '''
  858 + Options related to Context aware algorithm:
  859 + Angle: The min angle to a vertex to be considered a staircase vertex;
  860 + Max distance: The max distance a normal vertex must be to calculate its
  861 + weighting;
  862 + Min Weighting: The min weight a vertex must have;
  863 + Steps: The number of iterations the smoothing algorithm have to do.
  864 + '''
  865 + def __init__(self, parent):
  866 + wx.Panel.__init__(self, parent, -1)
  867 + self._build_widgets()
  868 +
  869 + def _build_widgets(self):
  870 + self.angle = floatspin.FloatSpin(self, -1, value=0.7, min_val=0.0,
  871 + max_val=1.0, increment=0.1,
  872 + digits=1)
  873 +
  874 + self.max_distance = floatspin.FloatSpin(self, -1, value=3.0, min_val=0.0,
  875 + max_val=100.0, increment=0.1,
  876 + digits=2)
  877 +
  878 + self.min_weight = floatspin.FloatSpin(self, -1, value=0.2, min_val=0.0,
  879 + max_val=1.0, increment=0.1,
  880 + digits=1)
  881 +
  882 + self.steps = wx.SpinCtrl(self, -1, value='10', min=1, max=100)
  883 +
  884 + layout_sizer = wx.FlexGridSizer(rows=4, cols=2, hgap=5, vgap=5)
  885 + layout_sizer.Add(wx.StaticText(self, -1, u'Angle:'), 0, wx.EXPAND)
  886 + layout_sizer.Add(self.angle, 0, wx.EXPAND)
  887 + layout_sizer.Add(wx.StaticText(self, -1, u'Max. distance:'), 0, wx.EXPAND)
  888 + layout_sizer.Add(self.max_distance, 0, wx.EXPAND)
  889 + layout_sizer.Add(wx.StaticText(self, -1, u'Min. weight:'), 0, wx.EXPAND)
  890 + layout_sizer.Add(self.min_weight, 0, wx.EXPAND)
  891 + layout_sizer.Add(wx.StaticText(self, -1, u'N. steps:'), 0, wx.EXPAND)
  892 + layout_sizer.Add(self.steps, 0, wx.EXPAND)
  893 +
  894 + self.main_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Context aware options'), wx.VERTICAL)
  895 + self.main_sizer.Add(layout_sizer, 0, wx.EXPAND | wx.ALL, 5)
  896 + self.SetSizer(self.main_sizer)
  897 +
  898 +class SurfaceDialog(wx.Dialog):
  899 + '''
  900 + This dialog is only shown when the mask whose surface will be generate was
  901 + edited. So far, the only options available are the choice of method to
  902 + generate the surface, Binary or `Context aware smoothing', and options from
  903 + `Context aware smoothing'
  904 + '''
  905 + def __init__(self):
  906 + wx.Dialog.__init__(self, None, -1, u'Surface generation options')
  907 + self._build_widgets()
  908 + self._bind_wx()
  909 +
  910 + def _build_widgets(self):
  911 + btn_ok = wx.Button(self, wx.ID_OK)
  912 + btn_cancel = wx.Button(self, wx.ID_CANCEL)
  913 + btn_sizer = wx.StdDialogButtonSizer()
  914 + btn_sizer.AddButton(btn_ok)
  915 + btn_sizer.AddButton(btn_cancel)
  916 + btn_sizer.Realize()
  917 +
  918 + self.alg_types = {0: u'Context aware smoothing', 1: u'Binary',
  919 + 2: u'InVesalius 3.b2'}
  920 + self.cb_types = wx.ComboBox(self, -1, self.alg_types[0],
  921 + choices=[self.alg_types[i] for i in sorted(self.alg_types)],
  922 + style=wx.CB_READONLY)
  923 +
  924 + self.ca_options = CAOptions(self)
  925 +
  926 + method_sizer = wx.BoxSizer(wx.HORIZONTAL)
  927 + method_sizer.Add(wx.StaticText(self, -1, u'Method:'), 0,
  928 + wx.EXPAND | wx.ALL, 5)
  929 + method_sizer.Add(self.cb_types, 0, wx.EXPAND)
  930 +
  931 + self.main_sizer = wx.BoxSizer(wx.VERTICAL)
  932 + self.main_sizer.Add(method_sizer, 0, wx.EXPAND | wx.ALL, 5)
  933 + self.main_sizer.Add(self.ca_options, 0, wx.EXPAND | wx.ALL, 5)
  934 + self.main_sizer.Add(btn_sizer, 0, wx.EXPAND | wx.ALL, 5)
  935 +
  936 + self.SetSizer(self.main_sizer)
  937 + self.Fit()
  938 +
  939 + def _bind_wx(self):
  940 + self.cb_types.Bind(wx.EVT_COMBOBOX, self._set_cb_types)
  941 +
  942 + def _set_cb_types(self, evt):
  943 + print evt.GetString()
  944 + if self.alg_types[evt.GetSelection()] == self.alg_types[0]:
  945 + self.ca_options.Enable()
  946 + else:
  947 + self.ca_options.Disable()
  948 + evt.Skip()
  949 +
  950 + def GetAlgorithmSelected(self):
  951 + try:
  952 + return self.alg_types[self.cb_types.GetSelection()]
  953 + except KeyError:
  954 + return self.alg_types[0]
  955 +
  956 + def GetOptions(self):
  957 + if self.GetAlgorithmSelected() == self.alg_types[0]:
  958 + options = {'angle': self.ca_options.angle.GetValue(),
  959 + 'max distance': self.ca_options.max_distance.GetValue(),
  960 + 'min weight': self.ca_options.min_weight.GetValue(),
  961 + 'steps': self.ca_options.steps.GetValue()}
  962 + else:
  963 + options = {}
  964 + return options
  965 +
  966 +
... ...
invesalius/gui/task_slice.py
... ... @@ -25,6 +25,7 @@ import wx.lib.platebtn as pbtn
25 25 import wx.lib.pubsub as ps
26 26  
27 27 import data.mask as mask
  28 +import data.slice_ as slice_
28 29 import constants as const
29 30 import gui.dialogs as dlg
30 31 import gui.widgets.gradient as grad
... ... @@ -143,10 +144,20 @@ class InnerTaskPanel(wx.Panel):
143 144  
144 145 def OnButtonNextTask(self, evt):
145 146 overwrite = self.check_box.IsChecked()
  147 + algorithm = ''
  148 + options = {}
146 149 if self.GetMaskSelected() != -1:
  150 + sl = slice_.Slice()
  151 + if sl.current_mask.was_edited:
  152 + dlgs = dlg.SurfaceDialog()
  153 + if dlgs.ShowModal() == wx.ID_OK:
  154 + algorithm = dlgs.GetAlgorithmSelected()
  155 + options = dlgs.GetOptions()
  156 + else:
  157 + return
147 158 ps.Publisher().sendMessage('Create surface from index',
148 159 (self.GetMaskSelected(),
149   - overwrite))
  160 + overwrite, algorithm, options))
150 161 ps.Publisher().sendMessage('Fold surface task')
151 162 else:
152 163 dlg.InexistentMask()
... ...