Commit 3fd085955c832600284d2bb86e296010fc777df2

Authored by okahilak
Committed by GitHub
1 parent f5086bb0
Exists in master

MOD: Decouple navigation starting/stopping from the UI (#290)

Essentially, start/stop navigation using "Start navigation"
and "Stop navigation" events.

Co-authored-by: Olli-Pekka Kahilakoski <olli-pekka.kahilakoski@aalto.fi>
Showing 1 changed file with 158 additions and 141 deletions   Show diff stats
invesalius/gui/task_navigator.py
@@ -368,6 +368,7 @@ class NeuronavigationPanel(wx.Panel): @@ -368,6 +368,7 @@ class NeuronavigationPanel(wx.Panel):
368 choice_ref.SetSelection(const.DEFAULT_REF_MODE) 368 choice_ref.SetSelection(const.DEFAULT_REF_MODE)
369 choice_ref.SetToolTip(tooltip) 369 choice_ref.SetToolTip(tooltip)
370 choice_ref.Bind(wx.EVT_COMBOBOX, partial(self.OnChoiceRefMode, ctrl=choice_trck)) 370 choice_ref.Bind(wx.EVT_COMBOBOX, partial(self.OnChoiceRefMode, ctrl=choice_trck))
  371 + self.choice_ref = choice_ref
371 372
372 # Toggle buttons for image fiducials 373 # Toggle buttons for image fiducials
373 for n, fiducial in enumerate(const.IMAGE_FIDUCIALS): 374 for n, fiducial in enumerate(const.IMAGE_FIDUCIALS):
@@ -406,7 +407,7 @@ class NeuronavigationPanel(wx.Panel): @@ -406,7 +407,7 @@ class NeuronavigationPanel(wx.Panel):
406 tooltip = wx.ToolTip(_("Start navigation")) 407 tooltip = wx.ToolTip(_("Start navigation"))
407 btn_nav = wx.ToggleButton(self, -1, _("Navigate"), size=wx.Size(80, -1)) 408 btn_nav = wx.ToggleButton(self, -1, _("Navigate"), size=wx.Size(80, -1))
408 btn_nav.SetToolTip(tooltip) 409 btn_nav.SetToolTip(tooltip)
409 - btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn=(btn_nav, choice_trck, choice_ref))) 410 + btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn_nav=btn_nav))
410 411
411 tooltip = wx.ToolTip(_(u"Refine the coregistration")) 412 tooltip = wx.ToolTip(_(u"Refine the coregistration"))
412 checkicp = wx.CheckBox(self, -1, _(' ')) 413 checkicp = wx.CheckBox(self, -1, _(' '))
@@ -480,6 +481,8 @@ class NeuronavigationPanel(wx.Panel): @@ -480,6 +481,8 @@ class NeuronavigationPanel(wx.Panel):
480 Publisher.subscribe(self.UpdateACTData, 'Update ACT data') 481 Publisher.subscribe(self.UpdateACTData, 'Update ACT data')
481 Publisher.subscribe(self.UpdateNavigationStatus, 'Navigation status') 482 Publisher.subscribe(self.UpdateNavigationStatus, 'Navigation status')
482 Publisher.subscribe(self.UpdateTarget, 'Update target') 483 Publisher.subscribe(self.UpdateTarget, 'Update target')
  484 + Publisher.subscribe(self.onStartNavigation, 'Start navigation')
  485 + Publisher.subscribe(self.onStopNavigation, 'Stop navigation')
483 486
484 def LoadImageFiducials(self, marker_id, coord): 487 def LoadImageFiducials(self, marker_id, coord):
485 fiducial = self.GetFiducialByAttribute(const.IMAGE_FIDUCIALS, 'label', marker_id) 488 fiducial = self.GetFiducialByAttribute(const.IMAGE_FIDUCIALS, 'label', marker_id)
@@ -815,10 +818,53 @@ class NeuronavigationPanel(wx.Panel): @@ -815,10 +818,53 @@ class NeuronavigationPanel(wx.Panel):
815 self.icp_queue.put_nowait([self.icp, self.m_icp]) 818 self.icp_queue.put_nowait([self.icp, self.m_icp])
816 #print(self.icp, self.m_icp) 819 #print(self.icp, self.m_icp)
817 820
818 - def OnNavigate(self, evt, btn):  
819 - btn_nav = btn[0]  
820 - choice_trck = btn[1]  
821 - choice_ref = btn[2] 821 + def onStopNavigation(self):
  822 + choice_trck = self.choice_trck
  823 + choice_ref = self.choice_ref
  824 +
  825 + self.event.set()
  826 +
  827 + # print("coord unfinished: {}, queue {}", self.coord_queue.unfinished_tasks, self.coord_queue.qsize())
  828 + # print("coord_tracts unfinished: {}, queue {}", self.coord_tracts_queue.unfinished_tasks, self.coord_tracts_queue.qsize())
  829 + # print("tracts unfinished: {}, queue {}", self.tracts_queue.unfinished_tasks, self.tracts_queue.qsize())
  830 + self.coord_queue.clear()
  831 + # self.visualization_queue.clear()
  832 + if self.trigger_state:
  833 + self.trigger_queue.clear()
  834 + if self.view_tracts:
  835 + self.coord_tracts_queue.clear()
  836 + self.tracts_queue.clear()
  837 +
  838 + # print("coord after unfinished: {}, queue {}", self.coord_queue.unfinished_tasks, self.coord_queue.qsize())
  839 + # print("coord_tracts after unfinished: {}, queue {}", self.coord_tracts_queue.unfinished_tasks, self.coord_tracts_queue.qsize())
  840 + # print("tracts after unfinished: {}, queue {}", self.tracts_queue.unfinished_tasks, self.tracts_queue.qsize())
  841 + self.coord_queue.join()
  842 + # self.visualization_queue.join()
  843 + if self.trigger_state:
  844 + self.trigger_queue.join()
  845 + if self.view_tracts:
  846 + self.coord_tracts_queue.join()
  847 + self.tracts_queue.join()
  848 +
  849 + # print("coord join unfinished: {}, queue {}", self.coord_queue.unfinished_tasks, self.coord_queue.qsize())
  850 + # print("vis join unfinished: {}, queue {}", self.visualization_queue.unfinished_tasks, self.visualization_queue.qsize())
  851 +
  852 + # Enable all navigation buttons
  853 + choice_ref.Enable(True)
  854 + choice_trck.Enable(True)
  855 + for btn_c in self.btns_coord:
  856 + btn_c.Enable(True)
  857 +
  858 + # if self.trigger_state:
  859 + # self.trigger.stop()
  860 +
  861 + vis_components = [self.trigger_state, self.view_tracts]
  862 + Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components)
  863 +
  864 + def onStartNavigation(self):
  865 + choice_trck = self.choice_trck
  866 + choice_ref = self.choice_ref
  867 +
822 errors = False 868 errors = False
823 869
824 # initialize jobs list 870 # initialize jobs list
@@ -826,158 +872,129 @@ class NeuronavigationPanel(wx.Panel): @@ -826,158 +872,129 @@ class NeuronavigationPanel(wx.Panel):
826 vis_components = [self.trigger_state, self.view_tracts] 872 vis_components = [self.trigger_state, self.view_tracts]
827 vis_queues = [self.coord_queue, self.trigger_queue, self.tracts_queue, self.icp_queue] 873 vis_queues = [self.coord_queue, self.trigger_queue, self.tracts_queue, self.icp_queue]
828 874
829 - nav_id = btn_nav.GetValue()  
830 - if not nav_id:  
831 - self.event.set()  
832 -  
833 - # print("coord unfinished: {}, queue {}", self.coord_queue.unfinished_tasks, self.coord_queue.qsize())  
834 - # print("coord_tracts unfinished: {}, queue {}", self.coord_tracts_queue.unfinished_tasks, self.coord_tracts_queue.qsize())  
835 - # print("tracts unfinished: {}, queue {}", self.tracts_queue.unfinished_tasks, self.tracts_queue.qsize())  
836 - self.coord_queue.clear()  
837 - # self.visualization_queue.clear()  
838 - if self.trigger_state:  
839 - self.trigger_queue.clear()  
840 - if self.view_tracts:  
841 - self.coord_tracts_queue.clear()  
842 - self.tracts_queue.clear()  
843 -  
844 - # print("coord after unfinished: {}, queue {}", self.coord_queue.unfinished_tasks, self.coord_queue.qsize())  
845 - # print("coord_tracts after unfinished: {}, queue {}", self.coord_tracts_queue.unfinished_tasks, self.coord_tracts_queue.qsize())  
846 - # print("tracts after unfinished: {}, queue {}", self.tracts_queue.unfinished_tasks, self.tracts_queue.qsize())  
847 - self.coord_queue.join()  
848 - # self.visualization_queue.join()  
849 - if self.trigger_state:  
850 - self.trigger_queue.join()  
851 - if self.view_tracts:  
852 - self.coord_tracts_queue.join()  
853 - self.tracts_queue.join()  
854 -  
855 - # print("coord join unfinished: {}, queue {}", self.coord_queue.unfinished_tasks, self.coord_queue.qsize())  
856 - # print("vis join unfinished: {}, queue {}", self.visualization_queue.unfinished_tasks, self.visualization_queue.qsize()) 875 + if np.isnan(self.fiducials).any():
  876 + wx.MessageBox(_("Invalid fiducials, select all coordinates."), _("InVesalius 3"))
857 877
858 - tooltip = wx.ToolTip(_("Start neuronavigation"))  
859 - btn_nav.SetToolTip(tooltip) 878 + elif not self.trk_init[0] or not self.tracker_id:
  879 + dlg.ShowNavigationTrackerWarning(0, 'choose')
  880 + errors = True
  881 +
  882 + else:
  883 + if self.event.is_set():
  884 + self.event.clear()
  885 +
  886 + # prepare GUI for navigation
  887 + Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components)
  888 + Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS)
  889 + Publisher.sendMessage("Hide current mask")
860 890
861 - # Enable all navigation buttons  
862 - choice_ref.Enable(True)  
863 - choice_trck.Enable(True) 891 + # disable all navigation buttons
  892 + choice_ref.Enable(False)
  893 + choice_trck.Enable(False)
864 for btn_c in self.btns_coord: 894 for btn_c in self.btns_coord:
865 - btn_c.Enable(True) 895 + btn_c.Enable(False)
866 896
867 - # if self.trigger_state:  
868 - # self.trigger.stop() 897 + # fiducials matrix
  898 + m_change = tr.affine_matrix_from_points(self.fiducials[3:, :].T, self.fiducials[:3, :].T,
  899 + shear=False, scale=False)
  900 + # initialize spatial tracker parameters
  901 + tracker_mode = self.trk_init, self.tracker_id, self.ref_mode_id
869 902
870 - Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components) 903 + # compute fiducial registration error (FRE)
  904 + if not self.icp_fre:
  905 + self.fre = db.calculate_fre(self.fiducials_raw, self.fiducials, self.ref_mode_id, m_change)
  906 + self.UpdateFRE(self.fre)
871 907
872 - else: 908 + if self.track_obj:
  909 + # if object tracking is selected
  910 + if not self.obj_reg_status:
  911 + # check if object registration was performed
  912 + wx.MessageBox(_("Perform coil registration before navigation."), _("InVesalius 3"))
  913 + errors = True
  914 + else:
  915 + # if object registration was correctly performed continue with navigation
  916 + # obj_reg[0] is object 3x3 fiducial matrix and obj_reg[1] is 3x3 orientation matrix
  917 + obj_fiducials, obj_orients, obj_ref_mode, obj_name = self.obj_reg
873 918
874 - if np.isnan(self.fiducials).any():  
875 - wx.MessageBox(_("Invalid fiducials, select all coordinates."), _("InVesalius 3"))  
876 - btn_nav.SetValue(False) 919 + coreg_data = [m_change, obj_ref_mode]
877 920
878 - elif not self.trk_init[0] or not self.tracker_id:  
879 - dlg.ShowNavigationTrackerWarning(0, 'choose')  
880 - errors = True 921 + if self.ref_mode_id:
  922 + coord_raw = dco.GetCoordinates(self.trk_init, self.tracker_id, self.ref_mode_id)
  923 + else:
  924 + coord_raw = np.array([None])
881 925
882 - else:  
883 - if self.event.is_set():  
884 - self.event.clear() 926 + obj_data = db.object_registration(obj_fiducials, obj_orients, coord_raw, m_change)
  927 + coreg_data.extend(obj_data)
885 928
886 - # prepare GUI for navigation  
887 - Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components)  
888 - Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS)  
889 - Publisher.sendMessage("Hide current mask")  
890 - tooltip = wx.ToolTip(_("Stop neuronavigation"))  
891 - btn_nav.SetToolTip(tooltip) 929 + queues = [self.coord_queue, self.coord_tracts_queue, self.icp_queue]
  930 + jobs_list.append(dcr.CoordinateCorregistrate(self.ref_mode_id, tracker_mode, coreg_data,
  931 + self.view_tracts, queues,
  932 + self.event, self.sleep_nav, self.tracker_id,
  933 + self.target))
  934 + else:
  935 + coreg_data = (m_change, 0)
  936 + queues = [self.coord_queue, self.coord_tracts_queue, self.icp_queue]
  937 + jobs_list.append(dcr.CoordinateCorregistrateNoObject(self.ref_mode_id, tracker_mode, coreg_data,
  938 + self.view_tracts, queues,
  939 + self.event, self.sleep_nav))
  940 +
  941 + if not errors:
  942 + #TODO: Test the trigger thread
  943 + if self.trigger_state:
  944 + # self.trigger = trig.Trigger(nav_id)
  945 + jobs_list.append(trig.TriggerNew(self.trigger_queue, self.event, self.sleep_nav))
892 946
893 - # disable all navigation buttons  
894 - choice_ref.Enable(False)  
895 - choice_trck.Enable(False)  
896 - for btn_c in self.btns_coord:  
897 - btn_c.Enable(False)  
898 -  
899 - # fiducials matrix  
900 - m_change = tr.affine_matrix_from_points(self.fiducials[3:, :].T, self.fiducials[:3, :].T,  
901 - shear=False, scale=False)  
902 - # initialize spatial tracker parameters  
903 - tracker_mode = self.trk_init, self.tracker_id, self.ref_mode_id  
904 -  
905 - # compute fiducial registration error (FRE)  
906 - if not self.icp_fre:  
907 - self.fre = db.calculate_fre(self.fiducials_raw, self.fiducials, self.ref_mode_id, m_change)  
908 - self.UpdateFRE(self.fre)  
909 -  
910 - if self.track_obj:  
911 - # if object tracking is selected  
912 - if not self.obj_reg_status:  
913 - # check if object registration was performed  
914 - wx.MessageBox(_("Perform coil registration before navigation."), _("InVesalius 3"))  
915 - errors = True 947 + if self.view_tracts:
  948 + # initialize Trekker parameters
  949 + slic = sl.Slice()
  950 + prj_data = prj.Project()
  951 + matrix_shape = tuple(prj_data.matrix_shape)
  952 + affine = slic.affine.copy()
  953 + affine[1, -1] -= matrix_shape[1]
  954 + affine_vtk = vtk_utils.numpy_to_vtkMatrix4x4(affine)
  955 + Publisher.sendMessage("Update marker offset state", create=True)
  956 + self.trk_inp = self.trekker, affine, self.seed_offset, self.n_tracts, self.seed_radius,\
  957 + self.n_threads, self.act_data, affine_vtk, matrix_shape[1]
  958 + # print("Appending the tract computation thread!")
  959 + queues = [self.coord_tracts_queue, self.tracts_queue]
  960 + if self.enable_act:
  961 + jobs_list.append(dti.ComputeTractsACTThread(self.trk_inp, queues, self.event, self.sleep_nav))
916 else: 962 else:
917 - # if object registration was correctly performed continue with navigation  
918 - # obj_reg[0] is object 3x3 fiducial matrix and obj_reg[1] is 3x3 orientation matrix  
919 - obj_fiducials, obj_orients, obj_ref_mode, obj_name = self.obj_reg 963 + jobs_list.append(dti.ComputeTractsThread(self.trk_inp, queues, self.event, self.sleep_nav))
920 964
921 - coreg_data = [m_change, obj_ref_mode] 965 + jobs_list.append(UpdateNavigationScene(vis_queues, vis_components,
  966 + self.event, self.sleep_nav))
922 967
923 - if self.ref_mode_id:  
924 - coord_raw = dco.GetCoordinates(self.trk_init, self.tracker_id, self.ref_mode_id)  
925 - else:  
926 - coord_raw = np.array([None]) 968 + for jobs in jobs_list:
  969 + # jobs.daemon = True
  970 + jobs.start()
  971 + # del jobs
927 972
928 - obj_data = db.object_registration(obj_fiducials, obj_orients, coord_raw, m_change)  
929 - coreg_data.extend(obj_data) 973 + if not self.checkicp.GetValue():
  974 + if dlg.ICPcorregistration(self.fre):
  975 + m_icp = self.OnICP()
  976 + self.icp_fre = db.calculate_fre(self.fiducials_raw, self.fiducials, self.ref_mode_id,
  977 + m_change, m_icp)
  978 + self.ctrl_icp()
930 979
931 - queues = [self.coord_queue, self.coord_tracts_queue, self.icp_queue]  
932 - jobs_list.append(dcr.CoordinateCorregistrate(self.ref_mode_id, tracker_mode, coreg_data,  
933 - self.view_tracts, queues,  
934 - self.event, self.sleep_nav, self.tracker_id,  
935 - self.target))  
936 - else:  
937 - coreg_data = (m_change, 0)  
938 - queues = [self.coord_queue, self.coord_tracts_queue, self.icp_queue]  
939 - jobs_list.append(dcr.CoordinateCorregistrateNoObject(self.ref_mode_id, tracker_mode, coreg_data,  
940 - self.view_tracts, queues,  
941 - self.event, self.sleep_nav))  
942 -  
943 - if not errors:  
944 - #TODO: Test the trigger thread  
945 - if self.trigger_state:  
946 - # self.trigger = trig.Trigger(nav_id)  
947 - jobs_list.append(trig.TriggerNew(self.trigger_queue, self.event, self.sleep_nav))  
948 -  
949 - if self.view_tracts:  
950 - # initialize Trekker parameters  
951 - slic = sl.Slice()  
952 - prj_data = prj.Project()  
953 - matrix_shape = tuple(prj_data.matrix_shape)  
954 - affine = slic.affine.copy()  
955 - affine[1, -1] -= matrix_shape[1]  
956 - affine_vtk = vtk_utils.numpy_to_vtkMatrix4x4(affine)  
957 - Publisher.sendMessage("Update marker offset state", create=True)  
958 - self.trk_inp = self.trekker, affine, self.seed_offset, self.n_tracts, self.seed_radius,\  
959 - self.n_threads, self.act_data, affine_vtk, matrix_shape[1]  
960 - # print("Appending the tract computation thread!")  
961 - queues = [self.coord_tracts_queue, self.tracts_queue]  
962 - if self.enable_act:  
963 - jobs_list.append(dti.ComputeTractsACTThread(self.trk_inp, queues, self.event, self.sleep_nav))  
964 - else:  
965 - jobs_list.append(dti.ComputeTractsThread(self.trk_inp, queues, self.event, self.sleep_nav))  
966 -  
967 - jobs_list.append(UpdateNavigationScene(vis_queues, vis_components,  
968 - self.event, self.sleep_nav))  
969 -  
970 - for jobs in jobs_list:  
971 - # jobs.daemon = True  
972 - jobs.start()  
973 - # del jobs  
974 -  
975 - if not self.checkicp.GetValue():  
976 - if dlg.ICPcorregistration(self.fre):  
977 - m_icp = self.OnICP()  
978 - self.icp_fre = db.calculate_fre(self.fiducials_raw, self.fiducials, self.ref_mode_id,  
979 - m_change, m_icp)  
980 - self.ctrl_icp() 980 + def OnNavigate(self, evt, btn_nav):
  981 + choice_trck = self.choice_trck
  982 + choice_ref = self.choice_ref
  983 +
  984 + nav_id = btn_nav.GetValue()
  985 + if not nav_id:
  986 + Publisher.sendMessage("Stop navigation")
  987 +
  988 + tooltip = wx.ToolTip(_("Start neuronavigation"))
  989 + btn_nav.SetToolTip(tooltip)
  990 + else:
  991 + Publisher.sendMessage("Start navigation")
  992 +
  993 + if self.nav_status:
  994 + tooltip = wx.ToolTip(_("Stop neuronavigation"))
  995 + btn_nav.SetToolTip(tooltip)
  996 + else:
  997 + btn_nav.SetValue(False)
981 998
982 def ResetImageFiducials(self): 999 def ResetImageFiducials(self):
983 for m in range(0, 3): 1000 for m in range(0, 3):