Commit 3fd085955c832600284d2bb86e296010fc777df2
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): |