Commit 3749eea2ab80ccbef0693c465311bd973c4551e6
1 parent
ad7294a7
Exists in
select_part
Starting to select part to new mask
Showing
5 changed files
with
149 additions
and
13 deletions
Show diff stats
invesalius/constants.py
... | ... | @@ -483,6 +483,7 @@ ID_CLEAN_MASK = wx.NewId() |
483 | 483 | ID_REORIENT_IMG = wx.NewId() |
484 | 484 | ID_FLOODFILL_MASK = wx.NewId() |
485 | 485 | ID_REMOVE_MASK_PART = wx.NewId() |
486 | +ID_SELECT_MASK_PART = wx.NewId() | |
486 | 487 | |
487 | 488 | #--------------------------------------------------------- |
488 | 489 | STATE_DEFAULT = 1000 |
... | ... | @@ -502,6 +503,7 @@ SLICE_STATE_WATERSHED = 3009 |
502 | 503 | SLICE_STATE_REORIENT = 3010 |
503 | 504 | SLICE_STATE_MASK_FFILL = 3011 |
504 | 505 | SLICE_STATE_REMOVE_MASK_PARTS = 3012 |
506 | +SLICE_STATE_SELECT_MASK_PARTS = 3013 | |
505 | 507 | |
506 | 508 | VOLUME_STATE_SEED = 2001 |
507 | 509 | # STATE_LINEAR_MEASURE = 3001 |
... | ... | @@ -521,6 +523,7 @@ SLICE_STYLES.append(SLICE_STATE_EDITOR) |
521 | 523 | SLICE_STYLES.append(SLICE_STATE_WATERSHED) |
522 | 524 | SLICE_STYLES.append(SLICE_STATE_MASK_FFILL) |
523 | 525 | SLICE_STYLES.append(SLICE_STATE_REMOVE_MASK_PARTS) |
526 | +SLICE_STYLES.append(SLICE_STATE_SELECT_MASK_PARTS) | |
524 | 527 | |
525 | 528 | VOLUME_STYLES = TOOL_STATES + [VOLUME_STATE_SEED, STATE_MEASURE_DISTANCE, |
526 | 529 | STATE_MEASURE_ANGLE] |
... | ... | @@ -531,6 +534,7 @@ STYLE_LEVEL = {SLICE_STATE_EDITOR: 1, |
531 | 534 | SLICE_STATE_WATERSHED: 1, |
532 | 535 | SLICE_STATE_MASK_FFILL: 2, |
533 | 536 | SLICE_STATE_REMOVE_MASK_PARTS: 2, |
537 | + SLICE_STATE_SELECT_MASK_PARTS: 2, | |
534 | 538 | SLICE_STATE_CROSS: 2, |
535 | 539 | SLICE_STATE_SCROLL: 2, |
536 | 540 | SLICE_STATE_REORIENT: 2, | ... | ... |
invesalius/data/slice_.py
... | ... | @@ -1282,6 +1282,22 @@ class Slice(object): |
1282 | 1282 | op, m1, m2 = pubsub_evt.data |
1283 | 1283 | self.do_boolean_op(op, m1, m2) |
1284 | 1284 | |
1285 | + def create_new_mask(self, show=True): | |
1286 | + last_name = self.current_mask.name | |
1287 | + | |
1288 | + proj = Project() | |
1289 | + mask_dict = proj.mask_dict | |
1290 | + names_list = [mask_dict[i].name for i in mask_dict.keys()] | |
1291 | + new_name = utils.next_copy_name(last_name, names_list) | |
1292 | + | |
1293 | + future_mask = Mask() | |
1294 | + future_mask.create_mask(self.matrix.shape) | |
1295 | + future_mask.name = new_name | |
1296 | + | |
1297 | + self._add_mask_into_proj(future_mask, show=show) | |
1298 | + | |
1299 | + return future_mask | |
1300 | + | |
1285 | 1301 | def do_boolean_op(self, op, m1, m2): |
1286 | 1302 | name_ops = {const.BOOLEAN_UNION: _(u"Union"), |
1287 | 1303 | const.BOOLEAN_DIFF: _(u"Diff"), | ... | ... |
invesalius/data/styles.py
... | ... | @@ -1928,6 +1928,115 @@ class RemoveMaskPartsInteractorStyle(FloodFillMaskInteractorStyle): |
1928 | 1928 | self._progr_msg = _(u"Removing part ...") |
1929 | 1929 | |
1930 | 1930 | |
1931 | +class SelectPartConfig(object): | |
1932 | + __metaclass__= utils.Singleton | |
1933 | + def __init__(self): | |
1934 | + self.matrix = None | |
1935 | + self.con_3d = 6 | |
1936 | + | |
1937 | + | |
1938 | +class SelectMaskPartsInteractorStyle(DefaultInteractorStyle): | |
1939 | + def __init__(self, viewer): | |
1940 | + DefaultInteractorStyle.__init__(self, viewer) | |
1941 | + | |
1942 | + self.viewer = viewer | |
1943 | + self.orientation = self.viewer.orientation | |
1944 | + | |
1945 | + self.picker = vtk.vtkWorldPointPicker() | |
1946 | + self.slice_actor = viewer.slice_data.actor | |
1947 | + self.slice_data = viewer.slice_data | |
1948 | + | |
1949 | + self.config = SelectPartConfig() | |
1950 | + self.dlg_ffill = None | |
1951 | + | |
1952 | + self.t0 = 254 | |
1953 | + self.t1 = 255 | |
1954 | + self.fill_value = 254 | |
1955 | + | |
1956 | + self.AddObserver("LeftButtonPressEvent", self.OnSelect) | |
1957 | + | |
1958 | + def CleanUp(self): | |
1959 | + self.config.matrix = None | |
1960 | + | |
1961 | + def OnSelect(self, obj, evt): | |
1962 | + if (self.viewer.slice_.buffer_slices[self.orientation].mask is None): | |
1963 | + return | |
1964 | + | |
1965 | + viewer = self.viewer | |
1966 | + iren = viewer.interactor | |
1967 | + | |
1968 | + mouse_x, mouse_y = iren.GetEventPosition() | |
1969 | + render = iren.FindPokedRenderer(mouse_x, mouse_y) | |
1970 | + slice_data = viewer.get_slice_data(render) | |
1971 | + | |
1972 | + self.picker.Pick(mouse_x, mouse_y, 0, render) | |
1973 | + | |
1974 | + coord = self.get_coordinate_cursor() | |
1975 | + position = slice_data.actor.GetInput().FindPoint(coord) | |
1976 | + | |
1977 | + if position != -1: | |
1978 | + coord = slice_data.actor.GetInput().GetPoint(position) | |
1979 | + | |
1980 | + if position < 0: | |
1981 | + position = viewer.calculate_matrix_position(coord) | |
1982 | + | |
1983 | + mask = self.viewer.slice_.current_mask.matrix[1:, 1:, 1:] | |
1984 | + x, y, z = self.calcultate_scroll_position(position) | |
1985 | + | |
1986 | + bstruct = np.array(generate_binary_structure(3, CON3D[self.config.con_3d]), dtype='uint8') | |
1987 | + self.viewer.slice_.do_threshold_to_all_slices() | |
1988 | + | |
1989 | + if self.config.matrix is None: | |
1990 | + self._create_new_mask() | |
1991 | + | |
1992 | + floodfill.floodfill_threshold(mask, [[x, y, z]], self.t0, self.t1, self.fill_value, bstruct, self.config.matrix[1:, 1:, 1:]) | |
1993 | + | |
1994 | + def _create_new_mask(self): | |
1995 | + mask = self.viewer.slice_.create_new_mask(show=False) | |
1996 | + mask.was_edited = True | |
1997 | + mask.matrix[0, :, :] = 1 | |
1998 | + mask.matrix[:, 0, :] = 1 | |
1999 | + mask.matrix[:, :, 0] = 1 | |
2000 | + | |
2001 | + self.config.matrix = mask.matrix | |
2002 | + | |
2003 | + def get_coordinate_cursor(self): | |
2004 | + # Find position | |
2005 | + x, y, z = self.picker.GetPickPosition() | |
2006 | + bounds = self.viewer.slice_data.actor.GetBounds() | |
2007 | + if bounds[0] == bounds[1]: | |
2008 | + x = bounds[0] | |
2009 | + elif bounds[2] == bounds[3]: | |
2010 | + y = bounds[2] | |
2011 | + elif bounds[4] == bounds[5]: | |
2012 | + z = bounds[4] | |
2013 | + return x, y, z | |
2014 | + | |
2015 | + def calcultate_scroll_position(self, position): | |
2016 | + # Based in the given coord (x, y, z), returns a list with the scroll positions for each | |
2017 | + # orientation, being the first position the sagital, second the coronal | |
2018 | + # and the last, axial. | |
2019 | + | |
2020 | + if self.orientation == 'AXIAL': | |
2021 | + image_width = self.slice_actor.GetInput().GetDimensions()[0] | |
2022 | + axial = self.slice_data.number | |
2023 | + coronal = position / image_width | |
2024 | + sagital = position % image_width | |
2025 | + | |
2026 | + elif self.orientation == 'CORONAL': | |
2027 | + image_width = self.slice_actor.GetInput().GetDimensions()[0] | |
2028 | + axial = position / image_width | |
2029 | + coronal = self.slice_data.number | |
2030 | + sagital = position % image_width | |
2031 | + | |
2032 | + elif self.orientation == 'SAGITAL': | |
2033 | + image_width = self.slice_actor.GetInput().GetDimensions()[1] | |
2034 | + axial = position / image_width | |
2035 | + coronal = position % image_width | |
2036 | + sagital = self.slice_data.number | |
2037 | + | |
2038 | + return sagital, coronal, axial | |
2039 | + | |
1931 | 2040 | def get_style(style): |
1932 | 2041 | STYLES = { |
1933 | 2042 | const.STATE_DEFAULT: DefaultInteractorStyle, |
... | ... | @@ -1945,6 +2054,7 @@ def get_style(style): |
1945 | 2054 | const.SLICE_STATE_REORIENT: ReorientImageInteractorStyle, |
1946 | 2055 | const.SLICE_STATE_MASK_FFILL: FloodFillMaskInteractorStyle, |
1947 | 2056 | const.SLICE_STATE_REMOVE_MASK_PARTS: RemoveMaskPartsInteractorStyle, |
2057 | + const.SLICE_STATE_SELECT_MASK_PARTS: SelectMaskPartsInteractorStyle, | |
1948 | 2058 | } |
1949 | 2059 | return STYLES[style] |
1950 | 2060 | ... | ... |
invesalius/gui/data_notebook.py
... | ... | @@ -512,11 +512,11 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
512 | 512 | self.SetStringItem(index, 1, label, |
513 | 513 | imageId=self.mask_list_index[index]) |
514 | 514 | self.SetStringItem(index, 2, threshold) |
515 | - self.SetItemImage(index, 1) | |
516 | - for key in self.mask_list_index.keys(): | |
517 | - if key != index: | |
518 | - self.SetItemImage(key, 0) | |
519 | - self.current_index = index | |
515 | + # self.SetItemImage(index, 1) | |
516 | + # for key in self.mask_list_index.keys(): | |
517 | + # if key != index: | |
518 | + # self.SetItemImage(key, 0) | |
519 | + # self.current_index = index | |
520 | 520 | |
521 | 521 | def AddMask(self, pubsub_evt): |
522 | 522 | index, mask_name, threshold_range, colour = pubsub_evt.data | ... | ... |
invesalius/gui/frame.py
... | ... | @@ -451,20 +451,16 @@ class Frame(wx.Frame): |
451 | 451 | elif id == const.ID_REMOVE_MASK_PART: |
452 | 452 | self.OnRemoveMaskParts() |
453 | 453 | |
454 | + elif id == const.ID_SELECT_MASK_PART: | |
455 | + self.OnSelectMaskParts() | |
456 | + | |
454 | 457 | elif id == const.ID_VIEW_INTERPOLATED: |
455 | - | |
456 | 458 | st = self.actived_interpolated_slices.IsChecked(const.ID_VIEW_INTERPOLATED) |
457 | - | |
458 | 459 | if st: |
459 | 460 | self.OnInterpolatedSlices(True) |
460 | 461 | else: |
461 | 462 | self.OnInterpolatedSlices(False) |
462 | 463 | |
463 | - def OnInterpolatedSlices(self, status): | |
464 | - | |
465 | - Publisher.sendMessage('Set interpolated slices', status) | |
466 | - | |
467 | - | |
468 | 464 | def OnSize(self, evt): |
469 | 465 | """ |
470 | 466 | Refresh GUI when frame is resized. |
... | ... | @@ -594,6 +590,12 @@ class Frame(wx.Frame): |
594 | 590 | def OnRemoveMaskParts(self): |
595 | 591 | Publisher.sendMessage('Enable style', const.SLICE_STATE_REMOVE_MASK_PARTS) |
596 | 592 | |
593 | + def OnSelectMaskParts(self): | |
594 | + Publisher.sendMessage('Enable style', const.SLICE_STATE_SELECT_MASK_PARTS) | |
595 | + | |
596 | + def OnInterpolatedSlices(self, status): | |
597 | + Publisher.sendMessage('Set interpolated slices', status) | |
598 | + | |
597 | 599 | # ------------------------------------------------------------------ |
598 | 600 | # ------------------------------------------------------------------ |
599 | 601 | # ------------------------------------------------------------------ |
... | ... | @@ -615,7 +617,8 @@ class MenuBar(wx.MenuBar): |
615 | 617 | const.ID_PROJECT_CLOSE, |
616 | 618 | const.ID_REORIENT_IMG, |
617 | 619 | const.ID_FLOODFILL_MASK, |
618 | - const.ID_REMOVE_MASK_PART,] | |
620 | + const.ID_REMOVE_MASK_PART, | |
621 | + const.ID_SELECT_MASK_PART,] | |
619 | 622 | self.__init_items() |
620 | 623 | self.__bind_events() |
621 | 624 | |
... | ... | @@ -733,6 +736,9 @@ class MenuBar(wx.MenuBar): |
733 | 736 | self.remove_mask_part_menu = mask_menu.Append(const.ID_REMOVE_MASK_PART, _(u"Remove parts")) |
734 | 737 | self.remove_mask_part_menu.Enable(False) |
735 | 738 | |
739 | + self.select_mask_part_menu = mask_menu.Append(const.ID_SELECT_MASK_PART, _(u"Select parts")) | |
740 | + self.select_mask_part_menu.Enable(False) | |
741 | + | |
736 | 742 | tools_menu.AppendMenu(-1, _(u"Mask"), mask_menu) |
737 | 743 | |
738 | 744 | # Image menu | ... | ... |