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 559 ID_CREATE_MASK = wx.NewId()
560 560  
561 561 ID_GOTO_SLICE = wx.NewId()
  562 +ID_GOTO_COORD = wx.NewId()
562 563  
563 564 #---------------------------------------------------------
564 565 STATE_DEFAULT = 1000
... ...
invesalius/control.py
... ... @@ -58,7 +58,8 @@ class Controller():
58 58 #None, others and opened Project = 0
59 59 #DICOM = 1
60 60 #TIFF uCT = 2
61   - self.img_type = 0
  61 + self.img_type = 0
  62 + self.affine = None
62 63  
63 64 #Init session
64 65 session = ses.Session()
... ... @@ -108,6 +109,8 @@ class Controller():
108 109  
109 110 Publisher.subscribe(self.OnSaveProject, 'Save project')
110 111  
  112 + Publisher.subscribe(self.Send_affine, 'Get affine matrix')
  113 +
111 114 def SetBitmapSpacing(self, spacing):
112 115 proj = prj.Project()
113 116 proj.spacing = spacing
... ... @@ -368,6 +371,7 @@ class Controller():
368 371 session = ses.Session()
369 372 session.CloseProject()
370 373  
  374 + Publisher.sendMessage('Update status text in GUI', label=_("Ready"))
371 375 ###########################
372 376  
373 377 def StartImportBitmapPanel(self, path):
... ... @@ -862,11 +866,7 @@ class Controller():
862 866  
863 867 hdr = group.header
864 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 870 Publisher.sendMessage('Update affine matrix',
871 871 affine=self.affine, status=True)
872 872 hdr.set_data_dtype('int16')
... ... @@ -889,6 +889,11 @@ class Controller():
889 889 threshold_range=scalar_range)
890 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 897 def LoadImagedataInfo(self):
893 898 proj = prj.Project()
894 899  
... ...
invesalius/data/surface.py
... ... @@ -337,7 +337,18 @@ class SurfaceManager():
337 337 self.CreateSurfaceFromPolydata(polydata, name=name, scalar=scalar)
338 338  
339 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 353 def UpdateconverttoInVflag(self, converttoInV):
343 354 self.converttoInV = converttoInV
... ...
invesalius/gui/dialogs.py
... ... @@ -3685,3 +3685,94 @@ class GoToDialog(wx.Dialog):
3685 3685 def Close(self):
3686 3686 wx.Dialog.Close(self)
3687 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 463 self.OnRedo()
464 464 elif id == const.ID_GOTO_SLICE:
465 465 self.OnGotoSlice()
  466 + elif id == const.ID_GOTO_COORD:
  467 + self.GoToDialogScannerCoord()
466 468  
467 469 elif id == const.ID_BOOLEAN_MASK:
468 470 self.OnMaskBoolean()
... ... @@ -700,6 +702,12 @@ class Frame(wx.Frame):
700 702 gt_dialog.ShowModal()
701 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 711 def OnMaskBoolean(self):
704 712 Publisher.sendMessage('Show boolean dialog')
705 713  
... ... @@ -792,6 +800,7 @@ class MenuBar(wx.MenuBar):
792 800 sub(self.OnEnableState, "Enable state project")
793 801 sub(self.OnEnableUndo, "Enable undo")
794 802 sub(self.OnEnableRedo, "Enable redo")
  803 + sub(self.OnEnableGotoCoord, "Update affine matrix")
795 804 sub(self.OnEnableNavigation, "Navigation status")
796 805  
797 806 sub(self.OnAddMask, "Add mask")
... ... @@ -859,6 +868,8 @@ class MenuBar(wx.MenuBar):
859 868 file_edit.Append(wx.ID_UNDO, _("Undo\tCtrl+Z")).Enable(False)
860 869 file_edit.Append(wx.ID_REDO, _("Redo\tCtrl+Y")).Enable(False)
861 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 873 #app(const.ID_EDIT_LIST, "Show Undo List...")
863 874 #################################################################
864 875  
... ... @@ -1077,6 +1088,16 @@ class MenuBar(wx.MenuBar):
1077 1088 else:
1078 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 1101 def OnEnableNavigation(self, status):
1081 1102 """
1082 1103 Disable mode menu when navigation is on.
... ...
invesalius/gui/task_navigator.py
... ... @@ -478,8 +478,6 @@ class NeuronavigationPanel(wx.Panel):
478 478 ctrl.SetSelection(0)
479 479 print("Tracker not connected!")
480 480 else:
481   - Publisher.sendMessage('Update status text in GUI',
482   - label=_("Ready"))
483 481 ctrl.SetSelection(self.tracker_id)
484 482 print("Tracker connected!")
485 483 elif choice == 6:
... ... @@ -513,10 +511,8 @@ class NeuronavigationPanel(wx.Panel):
513 511 dlg.NavigationTrackerWarning(self.tracker_id, self.trk_init[1])
514 512 self.tracker_id = 0
515 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 516 Publisher.sendMessage('Update tracker initializer',
521 517 nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id))
522 518  
... ...