Commit 00952d8a152e7758dccbbc8d1cc2d5cde54fefeb
1 parent
390a84c8
Exists in
master
and in
67 other branches
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
Showing
7 changed files
with
322 additions
and
90 deletions
Show diff stats
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
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_=""): |
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() | ... | ... |