Commit a042b58e427ac48e1e6b4d1ec1bb5d9b3c71172a

Authored by Renan
Committed by Thiago Franco de Moraes
1 parent eac24f4f
Exists in master

Dialog "go to" scanner coordinate system (#180)

* Created dialog goto sacnner coord

* Get affine matrix from project or imported image

-deals with wrongly affine shape and old inv projects

* Update status GUI

* Remove redundant sendmessage and show cross

* cleaning...
invesalius/constants.py
@@ -559,6 +559,7 @@ ID_CREATE_SURFACE = wx.NewId() @@ -559,6 +559,7 @@ ID_CREATE_SURFACE = wx.NewId()
559 ID_CREATE_MASK = wx.NewId() 559 ID_CREATE_MASK = wx.NewId()
560 560
561 ID_GOTO_SLICE = wx.NewId() 561 ID_GOTO_SLICE = wx.NewId()
  562 +ID_GOTO_COORD = wx.NewId()
562 563
563 #--------------------------------------------------------- 564 #---------------------------------------------------------
564 STATE_DEFAULT = 1000 565 STATE_DEFAULT = 1000
invesalius/control.py
@@ -58,7 +58,8 @@ class Controller(): @@ -58,7 +58,8 @@ class Controller():
58 #None, others and opened Project = 0 58 #None, others and opened Project = 0
59 #DICOM = 1 59 #DICOM = 1
60 #TIFF uCT = 2 60 #TIFF uCT = 2
61 - self.img_type = 0 61 + self.img_type = 0
  62 + self.affine = None
62 63
63 #Init session 64 #Init session
64 session = ses.Session() 65 session = ses.Session()
@@ -108,6 +109,8 @@ class Controller(): @@ -108,6 +109,8 @@ class Controller():
108 109
109 Publisher.subscribe(self.OnSaveProject, 'Save project') 110 Publisher.subscribe(self.OnSaveProject, 'Save project')
110 111
  112 + Publisher.subscribe(self.Send_affine, 'Get affine matrix')
  113 +
111 def SetBitmapSpacing(self, spacing): 114 def SetBitmapSpacing(self, spacing):
112 proj = prj.Project() 115 proj = prj.Project()
113 proj.spacing = spacing 116 proj.spacing = spacing
@@ -368,6 +371,7 @@ class Controller(): @@ -368,6 +371,7 @@ class Controller():
368 session = ses.Session() 371 session = ses.Session()
369 session.CloseProject() 372 session.CloseProject()
370 373
  374 + Publisher.sendMessage('Update status text in GUI', label=_("Ready"))
371 ########################### 375 ###########################
372 376
373 def StartImportBitmapPanel(self, path): 377 def StartImportBitmapPanel(self, path):
@@ -862,11 +866,7 @@ class Controller(): @@ -862,11 +866,7 @@ class Controller():
862 866
863 hdr = group.header 867 hdr = group.header
864 if group.affine.any(): 868 if group.affine.any():
865 - from numpy import hstack  
866 - from numpy.linalg import inv  
867 - affine = inv(group.affine)  
868 - affine[1, 3] = -affine[1, 3]  
869 - self.affine = hstack(affine) 869 + self.affine = group.affine
870 Publisher.sendMessage('Update affine matrix', 870 Publisher.sendMessage('Update affine matrix',
871 affine=self.affine, status=True) 871 affine=self.affine, status=True)
872 hdr.set_data_dtype('int16') 872 hdr.set_data_dtype('int16')
@@ -889,6 +889,11 @@ class Controller(): @@ -889,6 +889,11 @@ class Controller():
889 threshold_range=scalar_range) 889 threshold_range=scalar_range)
890 return self.matrix, self.filename 890 return self.matrix, self.filename
891 891
  892 + def Send_affine(self):
  893 + if self.affine is not None:
  894 + Publisher.sendMessage('Update affine matrix',
  895 + affine=self.affine, status=True)
  896 +
892 def LoadImagedataInfo(self): 897 def LoadImagedataInfo(self):
893 proj = prj.Project() 898 proj = prj.Project()
894 899
invesalius/data/surface.py
@@ -337,7 +337,18 @@ class SurfaceManager(): @@ -337,7 +337,18 @@ class SurfaceManager():
337 self.CreateSurfaceFromPolydata(polydata, name=name, scalar=scalar) 337 self.CreateSurfaceFromPolydata(polydata, name=name, scalar=scalar)
338 338
339 def UpdateAffineMatrix(self, affine, status): 339 def UpdateAffineMatrix(self, affine, status):
340 - self.affine = affine 340 + try:
  341 + if status:
  342 + from numpy import hstack
  343 + from numpy.linalg import inv
  344 + affine = inv(affine)
  345 + affine[1, 3] = -affine[1, 3]
  346 + self.affine = hstack(affine)
  347 + else:
  348 + self.affine = None
  349 + except:
  350 + Publisher.sendMessage('Update affine matrix',
  351 + affine=None, status=False)
341 352
342 def UpdateconverttoInVflag(self, converttoInV): 353 def UpdateconverttoInVflag(self, converttoInV):
343 self.converttoInV = converttoInV 354 self.converttoInV = converttoInV
invesalius/gui/dialogs.py
@@ -3685,3 +3685,94 @@ class GoToDialog(wx.Dialog): @@ -3685,3 +3685,94 @@ class GoToDialog(wx.Dialog):
3685 def Close(self): 3685 def Close(self):
3686 wx.Dialog.Close(self) 3686 wx.Dialog.Close(self)
3687 self.Destroy() 3687 self.Destroy()
  3688 +
  3689 +
  3690 +class GoToDialogScannerCoord(wx.Dialog):
  3691 + def __init__(self, title=_("Go to scanner coord...")):
  3692 + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP)
  3693 + self._init_gui()
  3694 +
  3695 + def _init_gui(self):
  3696 + self.goto_sagital = wx.TextCtrl(self, size=(50,-1))
  3697 + self.goto_coronal = wx.TextCtrl(self, size=(50,-1))
  3698 + self.goto_axial = wx.TextCtrl(self, size=(50,-1))
  3699 +
  3700 + btn_ok = wx.Button(self, wx.ID_OK)
  3701 + btn_ok.SetHelpText("")
  3702 + btn_ok.SetDefault()
  3703 +
  3704 + btn_cancel = wx.Button(self, wx.ID_CANCEL)
  3705 + btn_cancel.SetHelpText("")
  3706 +
  3707 + btnsizer = wx.StdDialogButtonSizer()
  3708 + btnsizer.AddButton(btn_ok)
  3709 + btnsizer.AddButton(btn_cancel)
  3710 + btnsizer.Realize()
  3711 +
  3712 + sizer_create = wx.FlexGridSizer(3, 2, 10, 10)
  3713 + sizer_create.AddMany([(wx.StaticText(self, 1, _("Sagital coordinate:")), 1, wx.LEFT, 10), (self.goto_sagital, 1, wx.RIGHT, 10),
  3714 + (wx.StaticText(self, 1, _("Coronal coordinate:")), 1, wx.LEFT, 10), (self.goto_coronal, 1, wx.RIGHT, 10),
  3715 + (wx.StaticText(self, 1, _("Axial coordinate:")), 1, wx.LEFT, 10), (self.goto_axial, 1, wx.RIGHT, 10)])
  3716 +
  3717 + main_sizer = wx.BoxSizer(wx.VERTICAL)
  3718 +
  3719 + main_sizer.Add((5, 5))
  3720 + main_sizer.Add(sizer_create, proportion=3, flag=wx.CENTER, border=20)
  3721 + main_sizer.Add(btnsizer, proportion=1, flag=wx.CENTER|wx.TOP, border=5)
  3722 + main_sizer.Add((5, 5))
  3723 +
  3724 + self.SetSizer(main_sizer)
  3725 + main_sizer.Fit(self)
  3726 +
  3727 + self.orientation = None
  3728 + self.affine = None
  3729 +
  3730 + self.__bind_events()
  3731 +
  3732 + btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
  3733 + Publisher.sendMessage('Get affine matrix')
  3734 +
  3735 + def __bind_events(self):
  3736 + Publisher.subscribe(self.SetNewFocalPoint, 'Cross focal point')
  3737 + Publisher.subscribe(self.UpdateAffineMatrix, 'Update affine matrix')
  3738 +
  3739 + def UpdateAffineMatrix(self, affine, status):
  3740 + self.affine = affine
  3741 +
  3742 + def SetNewFocalPoint(self, coord, spacing):
  3743 + Publisher.sendMessage('Update cross pos', coord=self.result*spacing)
  3744 +
  3745 + def OnOk(self, evt):
  3746 + from numpy.linalg import inv
  3747 + import invesalius.data.slice_ as slc
  3748 + try:
  3749 + #get affine from image import
  3750 + if self.affine is not None:
  3751 + affine = self.affine
  3752 + #get affine from project
  3753 + else:
  3754 + from invesalius.project import Project
  3755 + affine = Project().affine
  3756 +
  3757 + point = [float(self.goto_sagital.GetValue()),
  3758 + float(self.goto_coronal.GetValue()),
  3759 + float(self.goto_axial.GetValue())]
  3760 +
  3761 + # transformation from scanner coordinates to inv coord system
  3762 + affine = inv(affine)
  3763 + self.result = np.dot(affine[:3, :3], np.transpose(point[0:3])) + affine[:3, 3]
  3764 + self.result[1] = slc.Slice().GetMaxSliceNumber(const.CORONAL_STR) - self.result[1]
  3765 +
  3766 + Publisher.sendMessage('Update status text in GUI', label=_("Calculating the transformation ..."))
  3767 +
  3768 + Publisher.sendMessage('Set Update cross pos')
  3769 + Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS)
  3770 +
  3771 + Publisher.sendMessage('Update status text in GUI', label=_("Ready"))
  3772 + except ValueError:
  3773 + pass
  3774 + self.Close()
  3775 +
  3776 + def Close(self):
  3777 + wx.Dialog.Close(self)
  3778 + self.Destroy()
invesalius/gui/frame.py
@@ -463,6 +463,8 @@ class Frame(wx.Frame): @@ -463,6 +463,8 @@ class Frame(wx.Frame):
463 self.OnRedo() 463 self.OnRedo()
464 elif id == const.ID_GOTO_SLICE: 464 elif id == const.ID_GOTO_SLICE:
465 self.OnGotoSlice() 465 self.OnGotoSlice()
  466 + elif id == const.ID_GOTO_COORD:
  467 + self.GoToDialogScannerCoord()
466 468
467 elif id == const.ID_BOOLEAN_MASK: 469 elif id == const.ID_BOOLEAN_MASK:
468 self.OnMaskBoolean() 470 self.OnMaskBoolean()
@@ -700,6 +702,12 @@ class Frame(wx.Frame): @@ -700,6 +702,12 @@ class Frame(wx.Frame):
700 gt_dialog.ShowModal() 702 gt_dialog.ShowModal()
701 self.Refresh() 703 self.Refresh()
702 704
  705 + def GoToDialogScannerCoord(self):
  706 + gts_dialog = dlg.GoToDialogScannerCoord()
  707 + gts_dialog.CenterOnParent()
  708 + gts_dialog.ShowModal()
  709 + self.Refresh()
  710 +
703 def OnMaskBoolean(self): 711 def OnMaskBoolean(self):
704 Publisher.sendMessage('Show boolean dialog') 712 Publisher.sendMessage('Show boolean dialog')
705 713
@@ -792,6 +800,7 @@ class MenuBar(wx.MenuBar): @@ -792,6 +800,7 @@ class MenuBar(wx.MenuBar):
792 sub(self.OnEnableState, "Enable state project") 800 sub(self.OnEnableState, "Enable state project")
793 sub(self.OnEnableUndo, "Enable undo") 801 sub(self.OnEnableUndo, "Enable undo")
794 sub(self.OnEnableRedo, "Enable redo") 802 sub(self.OnEnableRedo, "Enable redo")
  803 + sub(self.OnEnableGotoCoord, "Update affine matrix")
795 sub(self.OnEnableNavigation, "Navigation status") 804 sub(self.OnEnableNavigation, "Navigation status")
796 805
797 sub(self.OnAddMask, "Add mask") 806 sub(self.OnAddMask, "Add mask")
@@ -859,6 +868,8 @@ class MenuBar(wx.MenuBar): @@ -859,6 +868,8 @@ class MenuBar(wx.MenuBar):
859 file_edit.Append(wx.ID_UNDO, _("Undo\tCtrl+Z")).Enable(False) 868 file_edit.Append(wx.ID_UNDO, _("Undo\tCtrl+Z")).Enable(False)
860 file_edit.Append(wx.ID_REDO, _("Redo\tCtrl+Y")).Enable(False) 869 file_edit.Append(wx.ID_REDO, _("Redo\tCtrl+Y")).Enable(False)
861 file_edit.Append(const.ID_GOTO_SLICE, _("Go to slice ...\tCtrl+G")) 870 file_edit.Append(const.ID_GOTO_SLICE, _("Go to slice ...\tCtrl+G"))
  871 + file_edit.Append(const.ID_GOTO_COORD, _("Go to scanner coord ...\t")).Enable(False)
  872 +
862 #app(const.ID_EDIT_LIST, "Show Undo List...") 873 #app(const.ID_EDIT_LIST, "Show Undo List...")
863 ################################################################# 874 #################################################################
864 875
@@ -1077,6 +1088,16 @@ class MenuBar(wx.MenuBar): @@ -1077,6 +1088,16 @@ class MenuBar(wx.MenuBar):
1077 else: 1088 else:
1078 self.FindItemById(wx.ID_REDO).Enable(False) 1089 self.FindItemById(wx.ID_REDO).Enable(False)
1079 1090
  1091 + def OnEnableGotoCoord(self, affine, status):
  1092 + """
  1093 + Disable goto coord either if there is no affine matrix or affine is wrongly imported.
  1094 + :param status: Affine matrix status
  1095 + """
  1096 + if status:
  1097 + self.FindItemById(const.ID_GOTO_COORD).Enable(True)
  1098 + else:
  1099 + self.FindItemById(const.ID_GOTO_COORD).Enable(False)
  1100 +
1080 def OnEnableNavigation(self, status): 1101 def OnEnableNavigation(self, status):
1081 """ 1102 """
1082 Disable mode menu when navigation is on. 1103 Disable mode menu when navigation is on.
invesalius/gui/task_navigator.py
@@ -478,8 +478,6 @@ class NeuronavigationPanel(wx.Panel): @@ -478,8 +478,6 @@ class NeuronavigationPanel(wx.Panel):
478 ctrl.SetSelection(0) 478 ctrl.SetSelection(0)
479 print("Tracker not connected!") 479 print("Tracker not connected!")
480 else: 480 else:
481 - Publisher.sendMessage('Update status text in GUI',  
482 - label=_("Ready"))  
483 ctrl.SetSelection(self.tracker_id) 481 ctrl.SetSelection(self.tracker_id)
484 print("Tracker connected!") 482 print("Tracker connected!")
485 elif choice == 6: 483 elif choice == 6:
@@ -513,10 +511,8 @@ class NeuronavigationPanel(wx.Panel): @@ -513,10 +511,8 @@ class NeuronavigationPanel(wx.Panel):
513 dlg.NavigationTrackerWarning(self.tracker_id, self.trk_init[1]) 511 dlg.NavigationTrackerWarning(self.tracker_id, self.trk_init[1])
514 self.tracker_id = 0 512 self.tracker_id = 0
515 ctrl.SetSelection(self.tracker_id) 513 ctrl.SetSelection(self.tracker_id)
516 - else:  
517 - Publisher.sendMessage('Update status text in GUI',  
518 - label=_("Ready"))  
519 514
  515 + Publisher.sendMessage('Update status text in GUI', label=_("Ready"))
520 Publisher.sendMessage('Update tracker initializer', 516 Publisher.sendMessage('Update tracker initializer',
521 nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id)) 517 nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id))
522 518