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 368 choice_ref.SetSelection(const.DEFAULT_REF_MODE)
369 369 choice_ref.SetToolTip(tooltip)
370 370 choice_ref.Bind(wx.EVT_COMBOBOX, partial(self.OnChoiceRefMode, ctrl=choice_trck))
  371 + self.choice_ref = choice_ref
371 372  
372 373 # Toggle buttons for image fiducials
373 374 for n, fiducial in enumerate(const.IMAGE_FIDUCIALS):
... ... @@ -406,7 +407,7 @@ class NeuronavigationPanel(wx.Panel):
406 407 tooltip = wx.ToolTip(_("Start navigation"))
407 408 btn_nav = wx.ToggleButton(self, -1, _("Navigate"), size=wx.Size(80, -1))
408 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 412 tooltip = wx.ToolTip(_(u"Refine the coregistration"))
412 413 checkicp = wx.CheckBox(self, -1, _(' '))
... ... @@ -480,6 +481,8 @@ class NeuronavigationPanel(wx.Panel):
480 481 Publisher.subscribe(self.UpdateACTData, 'Update ACT data')
481 482 Publisher.subscribe(self.UpdateNavigationStatus, 'Navigation status')
482 483 Publisher.subscribe(self.UpdateTarget, 'Update target')
  484 + Publisher.subscribe(self.onStartNavigation, 'Start navigation')
  485 + Publisher.subscribe(self.onStopNavigation, 'Stop navigation')
483 486  
484 487 def LoadImageFiducials(self, marker_id, coord):
485 488 fiducial = self.GetFiducialByAttribute(const.IMAGE_FIDUCIALS, 'label', marker_id)
... ... @@ -815,10 +818,53 @@ class NeuronavigationPanel(wx.Panel):
815 818 self.icp_queue.put_nowait([self.icp, self.m_icp])
816 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 868 errors = False
823 869  
824 870 # initialize jobs list
... ... @@ -826,158 +872,129 @@ class NeuronavigationPanel(wx.Panel):
826 872 vis_components = [self.trigger_state, self.view_tracts]
827 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 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 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 999 def ResetImageFiducials(self):
983 1000 for m in range(0, 3):
... ...